<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Namatuzio</title>
    <description>The latest articles on DEV Community by Namatuzio (@namatuzio).</description>
    <link>https://dev.to/namatuzio</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%2F1156695%2F73da8b0b-3124-4195-998e-c343c277a788.png</url>
      <title>DEV Community: Namatuzio</title>
      <link>https://dev.to/namatuzio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/namatuzio"/>
    <language>en</language>
    <item>
      <title>Custom Themes and Dark Mode Release</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Tue, 12 Dec 2023 02:54:07 +0000</pubDate>
      <link>https://dev.to/namatuzio/custom-themes-and-dark-mode-release-3h5l</link>
      <guid>https://dev.to/namatuzio/custom-themes-and-dark-mode-release-3h5l</guid>
      <description>&lt;p&gt;Well, this is it! Exciting stuff I know. Both issues on the integration of &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/12"&gt;dark mode&lt;/a&gt; and &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/22"&gt;custom themes&lt;/a&gt; have been completed! This marks the end of the 3 post series outlining my experience implementing these 2 functionalities. &lt;/p&gt;

&lt;p&gt;However, it doesn't feel right to not add a little more information on my experience with the custom theme functionality, so here are some notes I wrote down while reimplementing it which didn't make it into the previous post:&lt;/p&gt;

&lt;h4&gt;
  
  
  Unreleased Notes
&lt;/h4&gt;

&lt;p&gt;My progress on the custom themes has been going great! Right now I'm planning on adding 2 new themes, a Gameboy theme, that will work off the limited palette of the original Gameboy and a Pokemon Home theme, based on the colours used in the &lt;a href="https://www.nintendo.com/us/store/products/pokemon-home-switch/"&gt;Pokemon Home&lt;/a&gt; app. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZYZug_dn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htw6q90pfs17ihp5nvom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZYZug_dn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htw6q90pfs17ihp5nvom.png" alt="Gameboy Colours" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gameboy palette example&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--odikM-6e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ux1kqf8rjl6xdxhp8dw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--odikM-6e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ux1kqf8rjl6xdxhp8dw.png" alt="Home Ref 1" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9ls_Fubz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4x30uqkq1bpo039t4cs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9ls_Fubz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4x30uqkq1bpo039t4cs.png" alt="Home Ref 2" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pokemon Home reference images&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My prediction was correct, using the scraped data would've been a lot for the web app, even though I spent quite some time gathering all the portraits for the Pokemon, I deleted the data and found an alternate way to handle everything: states and ID tracking. &lt;/p&gt;

&lt;p&gt;See the entirety of the styling right now is based on a dropdown, so essentially, when a style is changed:&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;select&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"themeSel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt; &lt;span class="na"&gt;selected=&lt;/span&gt;&lt;span class="s"&gt;{theme&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="err"&gt;"}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Dark&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"gameboy"&lt;/span&gt; &lt;span class="na"&gt;selected=&lt;/span&gt;&lt;span class="s"&gt;{theme&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;gameboy&lt;/span&gt;&lt;span class="err"&gt;"}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Gameboy&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"home"&lt;/span&gt; &lt;span class="na"&gt;selected=&lt;/span&gt;&lt;span class="s"&gt;{theme&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="err"&gt;"}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Pokemon Home&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Tracking the value of the select component &lt;code&gt;"themeSel"&lt;/code&gt; along with a state variable &lt;code&gt;"theme"&lt;/code&gt; enabled me to track the selected theme directory-wide by simply referencing the ID of the dropdown. This lets me do fun things like dynamically change the styling of the background and other areas like the cards easily when using a custom theme. This is a pretty big change over the simple toggle I had previously implemented for dark mode.&lt;/p&gt;

&lt;p&gt;Using a reference to the dropdown also allows me to dynamically make API calls, as opposed to storing all that data locally. If the Gameboy theme is selected, I want to be able to change the official art portraits to the pixel-based sprites seen in most of the games. Of course, if the home theme is selected, I want to use the home model to show off all the Pokemon. I already have an idea as to what needs to be done for the home theme because of some reference pictures taken from the Nintendo store's site. That's all for the update!&lt;/p&gt;
&lt;h4&gt;
  
  
  Anyways...
&lt;/h4&gt;

&lt;p&gt;So yes, both functionalities are implemented and fully functional. It was super fun implementing the new themes as it almost required me to create a skeleton theme manager that can be used to implement any number of new themes! I ran into a few problems, mainly with how I handled storing all the themes, which was promptly fixed up by reducing the scale of what I was doing. For example...&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleDarkMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isDarkMode&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;newTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isDarkMode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;setIsDarkMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setIsHomeTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setIsGameboyTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isDarkMode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isHomeMode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isGBMode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;this chunk of code handled everything JUST for toggling dark mode... yeah it's ugly, and it made me realize almost immediately after creating this abomination that I should scale back on how I handle it. Now it's handled in a much neater fashion;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newTheme&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;these 2 do it all! the useEffect is mainly just for extracting the theme when loading the site so that it remembers what theme you had stored in a previous session, whereas the handleThemeChange, works its magic when you change the theme while using the site. There's a lot more to it, but the bread and butter is right here, and it was a ton of fun to figure out and implement!&lt;/p&gt;
&lt;h2&gt;
  
  
  Completing my goals
&lt;/h2&gt;

&lt;p&gt;It took a lot of time (mainly because of my overcomplication with the implementation) but it was a fantastic experience to work on these features. Dark Mode is interesting because working on it made me realize that it can be as easy or as hard as you want it to be to implement. It's one of these features where there are almost a million ways to implement it but luckily I realized a bit after overdeveloping it that there was a much better and neater solution. Custom Themes are almost the same, I spent a good couple of days crafting a comprehensive list using a mixture of web scraping and manual formatting only for me to realize how benign an implementation like this is for such a feature. Of course, it would've worked, but at what cost? Working on the dark mode feature gave me a good idea as to how I would need to handle the custom themes, and working on custom themes gave me an excellent idea as to how custom theme frameworks are built. Tailwind CSS made all of this a breeze by allowing me to create custom colours that could be easily referenced.&lt;/p&gt;

&lt;p&gt;The PRs and issues can be found below, as well as pictures of the final look for all the themes!&lt;/p&gt;
&lt;h3&gt;
  
  
  Issues
&lt;/h3&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/12"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        add dark mode (my eyes hurt)
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#12&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/lstuma"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--SINWs5WD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars.githubusercontent.com/u/52998857%3Fv%3D4" alt="lstuma avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/lstuma"&gt;lstuma&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/12"&gt;&lt;time&gt;Oct 16, 2023&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      
    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TheShiveshNetwork/Pokedex/issues/12"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/22"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Custom Themes
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#22&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/Namatuzio"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--DPk0Wqmx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars.githubusercontent.com/u/55001591%3Fv%3D4" alt="Namatuzio avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/Namatuzio"&gt;Namatuzio&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/22"&gt;&lt;time&gt;Dec 04, 2023&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Hey again @TheShiveshNetwork, I would like to add custom themes to the site, if you want I could also package the dark mode stuff into there!&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TheShiveshNetwork/Pokedex/issues/22"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  PRs
&lt;/h3&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/23"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Feature: Dark Mode Toggle and Styling
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#23&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/Namatuzio"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--DPk0Wqmx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars.githubusercontent.com/u/55001591%3Fv%3D4" alt="Namatuzio avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/Namatuzio"&gt;Namatuzio&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/23"&gt;&lt;time&gt;Dec 08, 2023&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;@TheShiveshNetwork&lt;/p&gt;
&lt;p&gt;Fixes #12&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Added a dark mode toggle&lt;/li&gt;
&lt;li&gt;Adjusted theme for when dark mode is enabled&lt;/li&gt;
&lt;li&gt;Followed guidelines from the official &lt;a href="https://tailwindcss.com/docs/dark-mode" rel="nofollow"&gt;Tailwind CSS Dark Mode guide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'll post some screenshots of the UI updates but I want to know if you would like anything adjusted in terms of colours, placement, etc, for the implementation before any merge.&lt;/p&gt;
&lt;p&gt;Current toggle placement:
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/dc78c783-0ec4-4e3c-9068-3191c85777a9"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I-mcyN3n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/dc78c783-0ec4-4e3c-9068-3191c85777a9" alt="darkmode toggle button"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Homepage colours:
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/645e7a79-c5ed-4a98-8caa-643084a40c81"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qufPt3l2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/645e7a79-c5ed-4a98-8caa-643084a40c81" alt="Dark mode colours"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pokemon info styling:
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/af48682a-3ffe-4dfa-85a8-9b0d5516e156"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EPO_2ORX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/af48682a-3ffe-4dfa-85a8-9b0d5516e156" alt="info styling"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I'll mark this as a draft for now and update it until it meets your standards!&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TheShiveshNetwork/Pokedex/pull/23"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/26"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Feature: Custom themes
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#26&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/Namatuzio"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--DPk0Wqmx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars.githubusercontent.com/u/55001591%3Fv%3D4" alt="Namatuzio avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/Namatuzio"&gt;Namatuzio&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/26"&gt;&lt;time&gt;Dec 11, 2023&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;@TheShiveshNetwork&lt;/p&gt;
&lt;p&gt;Fixes #22&lt;/p&gt;
&lt;p&gt;Updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Added new custom themes: Gameboy &amp;amp; Pokemon Home&lt;/li&gt;
&lt;li&gt;Gameboy utilizes the limited colour scheme of the original Gameboy and utilizes sprites instead of official art for all 1017 Pokemon&lt;/li&gt;
&lt;li&gt;Pokemon Home is based on the colours used in the app reference pictures can be found below&lt;/li&gt;
&lt;li&gt;Made both themes storable&lt;/li&gt;
&lt;li&gt;Updated the way tracking themes is handled to support any number of additional themes&lt;/li&gt;
&lt;li&gt;Added a drop-down for the themes&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;SelectComponents.jsx&lt;/code&gt; to support ids for tracking the theme changes (default is &lt;code&gt;null&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had a lot of fun with this, I hope all the changes are up to spec! Here are some pictures of the themes in action:&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Gameboy:&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/94a403cf-a663-42a6-afd4-e66d53f723b5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JjMjXeJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/94a403cf-a663-42a6-afd4-e66d53f723b5" alt="gameboy home"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/d8196bfd-d813-4c6e-a782-0594ffd6fe0d"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UEd629oq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/d8196bfd-d813-4c6e-a782-0594ffd6fe0d" alt="gameboy info"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Pokemon Home:&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/35078e50-ad11-4a3a-9fe6-03cd840fdac8"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HchiuPqH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/35078e50-ad11-4a3a-9fe6-03cd840fdac8" alt="home home"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/ce72eff9-545d-4154-8c28-36ba39e85808"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T3kP0Rrl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/ce72eff9-545d-4154-8c28-36ba39e85808" alt="home info"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;References for the home theme:&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/63c87e9b-b19c-4a90-8f27-ea7918ce503b"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VdixyAGU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/63c87e9b-b19c-4a90-8f27-ea7918ce503b" alt="home ref"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/aa9f038d-2cfc-4ddf-acc3-e38da8bd1cd1"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iqg8qwWv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/aa9f038d-2cfc-4ddf-acc3-e38da8bd1cd1" alt="home ref 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;taken from the &lt;a href="https://www.nintendo.com/us/store/products/pokemon-home-switch/" rel="nofollow"&gt;official Nintendo store page&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course, feel free to request changes, I will add them ASAP!&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TheShiveshNetwork/Pokedex/pull/26"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;em&gt;EDIT: 12-12-2023&lt;/em&gt;&lt;/strong&gt; Something seemed to have gone wrong with the merging of the previous PR according to the repo owner, so I made another identical one found here: &lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/28"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Feature: ReAdded Custom themes
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#28&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/Namatuzio"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--DPk0Wqmx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://avatars.githubusercontent.com/u/55001591%3Fv%3D4" alt="Namatuzio avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/Namatuzio"&gt;Namatuzio&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/28"&gt;&lt;time&gt;Dec 12, 2023&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;@TheShiveshNetwork&lt;/p&gt;
&lt;p&gt;Fixes #22&lt;/p&gt;
&lt;p&gt;Created a new PR as requested in #26&lt;/p&gt;
&lt;p&gt;Updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Added new custom themes: Gameboy &amp;amp; Pokemon Home&lt;/li&gt;
&lt;li&gt;Gameboy utilizes the limited colour scheme of the original Gameboy and utilizes sprites instead of official art for all 1017 Pokemon&lt;/li&gt;
&lt;li&gt;Pokemon Home is based on the colours used in the app reference pictures can be found below&lt;/li&gt;
&lt;li&gt;Made both themes storable&lt;/li&gt;
&lt;li&gt;Updated the way tracking themes is handled to support any number of additional themes&lt;/li&gt;
&lt;li&gt;Added a drop-down for the themes&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;SelectComponents.jsx&lt;/code&gt; to support ids for tracking the theme changes (default is &lt;code&gt;null&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had a lot of fun with this, I hope all the changes are up to spec! Here are some pictures of the themes in action:&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Gameboy:&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/94a403cf-a663-42a6-afd4-e66d53f723b5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JjMjXeJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/94a403cf-a663-42a6-afd4-e66d53f723b5" alt="gameboy home"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/d8196bfd-d813-4c6e-a782-0594ffd6fe0d"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UEd629oq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/d8196bfd-d813-4c6e-a782-0594ffd6fe0d" alt="gameboy info"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Pokemon Home:&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/35078e50-ad11-4a3a-9fe6-03cd840fdac8"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HchiuPqH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/35078e50-ad11-4a3a-9fe6-03cd840fdac8" alt="home home"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/ce72eff9-545d-4154-8c28-36ba39e85808"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T3kP0Rrl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/ce72eff9-545d-4154-8c28-36ba39e85808" alt="home info"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;References for the home theme:&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/63c87e9b-b19c-4a90-8f27-ea7918ce503b"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VdixyAGU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/63c87e9b-b19c-4a90-8f27-ea7918ce503b" alt="home ref"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/aa9f038d-2cfc-4ddf-acc3-e38da8bd1cd1"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iqg8qwWv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/TheShiveshNetwork/Pokedex/assets/55001591/aa9f038d-2cfc-4ddf-acc3-e38da8bd1cd1" alt="home ref 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;taken from the &lt;a href="https://www.nintendo.com/us/store/products/pokemon-home-switch/" rel="nofollow"&gt;official Nintendo store page&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course, feel free to request changes, I will add them ASAP!&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TheShiveshNetwork/Pokedex/pull/28"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The final not final TIL
&lt;/h2&gt;

&lt;p&gt;I learned a good amount from the implementation of these features; how to create and style a dark mode (in more ways than one) using both stylesheets and CSS frameworks, creating custom themes using these frameworks, saving styling themes to a local cache and loading it, and lastly, dynamic API calling through referencing IDs. I felt like I poured my heart into these final features, which was reflected in the code review conducted by the repo owner. It was truly a wonderful experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;It's been an incredible 4 months since my open-source journey began and this is not the end of it. I want to deeply thank you all for reading my posts and keeping up! It was a great semester and I'm so happy I got to learn and experiment so much with open-source projects, something that means the absolute most to me. I hope you all enjoy your holiday season and once more, thank you for following the start of my open source journey!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>design</category>
    </item>
    <item>
      <title>Custom Themes &amp; Dark Mode Progress</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Mon, 11 Dec 2023 04:59:39 +0000</pubDate>
      <link>https://dev.to/namatuzio/custom-themes-dark-mode-progress-34jm</link>
      <guid>https://dev.to/namatuzio/custom-themes-dark-mode-progress-34jm</guid>
      <description>&lt;p&gt;Hey everyone, as outlined in my &lt;a href="https://dev.to/namatuzio/custom-themes-and-dark-mode-3n47"&gt;previous post&lt;/a&gt;, I've spent the last couple of weeks working out dark mode and some custom themes for an open source pokedex app. This is the update on the progress that has been made so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/12"&gt;issue&lt;/a&gt; dealing with dark mode has been &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/23"&gt;completed&lt;/a&gt;! It was super fun learning how to weave this into an app and I went through a few different iterations before I realized how much more complicated I was making it. &lt;/p&gt;

&lt;p&gt;My initial idea for dark mode was to create multiple style sheets that would be updated based on a toggle. For example, take this stylesheet;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* index.css */&lt;/span&gt;
&lt;span class="nd"&gt;::-webkit-scrollbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::-webkit-scrollbar-track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f1f1f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e0e0e0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::-webkit-scrollbar-thumb:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#a0a0a0&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;These colours change the styling of the scrollbar and are predominately the standard colours for the most part. The track is white, the roller is grey, and hovering makes it more grey. &lt;/p&gt;

&lt;p&gt;My initial implementation had multiple CSS files, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* index_dark_mode.css */&lt;/span&gt;
&lt;span class="nd"&gt;::-webkit-scrollbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::-webkit-scrollbar-track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2f2f2f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Darker color for the track */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::-webkit-scrollbar-thumb&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4f4f4f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Darker color for the thumb */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::-webkit-scrollbar-thumb:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6f6f6f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Slightly lighter color for the thumb hover */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would be great for a standard static app, but I realized this might add extra fluff and cache-related storage when loading the site, especially because I was planning on adding more themes, so I scrapped the implementation and went back to the drawing board. &lt;/p&gt;

&lt;p&gt;As I stated before I looked at some libraries for adding dark mode, which is when it hit me... The project already uses Tailwind CSS, surely there's something in there that will help right? So I did some &lt;a href="https://tailwindcss.com/"&gt;digging&lt;/a&gt; and landed upon just what I needed, a &lt;a href="https://tailwindcss.com/docs/dark-mode"&gt;dark mode&lt;/a&gt; page! Tailwind made adding dark mode a ton of fun! I handled everything through a toggle and one variable. To enable the use of dark mode, all I had to do was add the following line to the &lt;code&gt;tailwind.config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tailwind.config.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then from there, I could prefix some styling components with &lt;code&gt;dark:&lt;/code&gt; which would utilize them when dark mode is enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
    &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;
        &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="na"&gt;bg-slate-200&lt;/span&gt; &lt;span class="na"&gt;dark:bg-slate-900&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The div is normally white (&lt;code&gt;bg-slate-200&lt;/code&gt;) but if dark mode is enabled, it will be closer to a navy blue or black (&lt;code&gt;dark:bg-slate-900&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Also before I forget, Here are some screenshots of the updated theme!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tf7CKVQm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ceqeyg1ipr5ny7z34tci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tf7CKVQm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ceqeyg1ipr5ny7z34tci.png" alt="home" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m2z0rOQo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fp53ehprikg234hh3yfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m2z0rOQo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fp53ehprikg234hh3yfa.png" alt="home dark" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ujsHd_rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhwhh60cvk11jwijoj3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ujsHd_rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhwhh60cvk11jwijoj3f.png" alt="info" width="800" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pHSltmXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ye9zncgbcfz7fjq2iyjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pHSltmXs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ye9zncgbcfz7fjq2iyjl.png" alt="info dark" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I love the sleekness of it all, tailwind does have some nice colours.&lt;/p&gt;

&lt;p&gt;Either way this works great, and gave me some ideas for the implementation of my &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/22"&gt;second issue: custom themes&lt;/a&gt;. I did start on that and have some json data from scraping the pokeapi that contains portraits, but after the realization I had with the CSS files, I may very well try to find an alternative for this. &lt;/p&gt;

&lt;h2&gt;
  
  
  Until Next Time
&lt;/h2&gt;

&lt;p&gt;So that's pretty much all for this post, I figured out dark mode and got some brilliant ideas for fulfilling my final issue, custom themes. Thank you for reading and see ya next time!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>Custom Themes and Dark Mode</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Thu, 07 Dec 2023 04:13:23 +0000</pubDate>
      <link>https://dev.to/namatuzio/custom-themes-and-dark-mode-3n47</link>
      <guid>https://dev.to/namatuzio/custom-themes-and-dark-mode-3n47</guid>
      <description>&lt;p&gt;In the next couple of weeks, I'll be tackling a functionality that I've never really messed with before but always wanted to do, that being &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/22"&gt;Custom Themes&lt;/a&gt; and &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/12"&gt;Dark Mode&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Schematic
&lt;/h2&gt;

&lt;p&gt;Considering I've never worked with these functionalities before, I'll have to do some research. For dark mode, I believe I'll use a library as it will be relatively easy to incorporate into the code. So far, I've looked at &lt;a href="https://www.npmjs.com/package/darkmode-js"&gt;darkmode-js&lt;/a&gt; which seems like a pretty nice package for me to use. As for the custom themes, I believe I may have to crack out a Python web scraper mainly because I have some big plans for that, of course, I have a backup in case I'm heavily time-constrained or problems arise. &lt;/p&gt;

&lt;h2&gt;
  
  
  Updates
&lt;/h2&gt;

&lt;p&gt;I'll be posting a couple of updates throughout the development process, I hope you can stick around and watch my progress through these brand-new implementations! Until next time, see ya.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>design</category>
      <category>python</category>
    </item>
    <item>
      <title>Digging Deeper with TypeScript: ESlint</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Tue, 28 Nov 2023 00:23:02 +0000</pubDate>
      <link>https://dev.to/namatuzio/digging-deeper-with-typescript-eslint-31dn</link>
      <guid>https://dev.to/namatuzio/digging-deeper-with-typescript-eslint-31dn</guid>
      <description>&lt;p&gt;Last week I undertook another TypeScript-related &lt;a href="https://github.com/MattIPv4/workers-discord/issues/3" rel="noopener noreferrer"&gt;issue&lt;/a&gt; which dealt with adding ESlint to the codebase with some guidelines given by the owner of the repo. I figured the issue would be easy enough, I've used ESlint before - how much different could it be? Well, it turns out it was a lot different than what I'm used to. &lt;/p&gt;

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

&lt;p&gt;To begin, I installed ESlint by following their official &lt;a href="https://eslint.org/docs/latest/use/getting-started" rel="noopener noreferrer"&gt;quickstart guide&lt;/a&gt;. I was pretty surprised by how simple it was to set up for a TS project, but it ran me through some questions and in no time, ESlint was working!&lt;/p&gt;

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

&lt;p&gt;I added the 2 rules that related with the repo owner's lint request which both related to importing files and types.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rules&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;@typescript-eslint/consistent-type-imports&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-restricted-imports&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&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;patterns&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;*.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, after adding the rules, I went through the standard procedure and linted everything by using &lt;code&gt;npx eslint .&lt;/code&gt; with only 11 errors showing up... which was great, it should be an open-and-shut issue. Pretty much every linting error had to do with a type of &lt;code&gt;any&lt;/code&gt; being used. I decided to look up the error and found out that typescript has support for a type called &lt;code&gt;unknown&lt;/code&gt;. The &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; even states "&lt;code&gt;unknown&lt;/code&gt; is the type-safe counterpart of any." which is exactly what I needed!&lt;/p&gt;

&lt;p&gt;So I got around the errors by changing all the &lt;code&gt;any&lt;/code&gt; types to &lt;code&gt;unknown&lt;/code&gt;. It was then that I realized this was going to be a whole lot harder than I initially thought. A lot of the object property calls were throwing errors because technically, it would be unknown if anything would exist on there. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;typeof&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="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.name&lt;/code&gt; would throw an error because, to the compiler, it doesn't exist on value.&lt;/p&gt;

&lt;p&gt;Unfortunately for me, I didn't think like that when I first encountered the error, though it did give me some practice with casting and assertions in TS / JS so there's always a bright side. &lt;/p&gt;

&lt;p&gt;I read in the documentation and in a lot of discussions on stack overflow that &lt;code&gt;unknown&lt;/code&gt; types must first be funnelled into a more concrete typing before use. In order to fix the error, I decided to cast value almost every single time it was called to the expected object type. While not necessarily wrong, it was being done in functions that would check the type of an abstract object. If two objects had different structures, the function would still return false if it didn't match but it's overall just poor code conduct. &lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback
&lt;/h2&gt;

&lt;p&gt;After submitting the initial &lt;a href="https://github.com/MattIPv4/workers-discord/pull/8" rel="noopener noreferrer"&gt;PR&lt;/a&gt;, the repo owner pointed this out to me. &lt;/p&gt;

&lt;p&gt;I ended up overcomplicating a relatively easy fix both computationally and semantically. Thankfully, the repo owner was nice about it and gave some feedback for the requested changes. I applied those changes, opting for null checks which was in line with standard ESlint rules. I then had to change a function heading to take a generic type which after doing so, everything worked perfectly fine. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/MattIPv4/workers-discord/pull/8" rel="noopener noreferrer"&gt;PR&lt;/a&gt; still has yet to be merged and I'll definitely update the article once it has but for the time being, everything is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progression since Hacktoberfest
&lt;/h2&gt;

&lt;p&gt;This PR was the first time I've used ESlint with TypeScript and also the first time I've ever used the &lt;code&gt;unknown&lt;/code&gt; type. It was a painful but necessary experience and it felt good to finally understand the ins and outs of this foreign type. It really goes to show that issues themselves might sound easy, but you'll almost never know what you'll run into until you take the first step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;These PRs were a fantastic learning experience and I'm glad to have chosen them as the ones I would tackle this month. Moving forward there's still so much I want to learn and contribute to. For now, I'd love to take a look at adding custom themes or dark modes to websites, as I've never really added such a functionality before. I'm more excited to try custom themes as I would assume they aren't as solved as dark mode considering how many npm packages must exist that do just that. Thank you all for reading and until next time, see ya!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>discord</category>
      <category>github</category>
    </item>
    <item>
      <title>Type-strict coding with TypeScripts "strict" mode</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Mon, 27 Nov 2023 22:25:14 +0000</pubDate>
      <link>https://dev.to/namatuzio/type-strict-coding-with-typescripts-strict-mode-5h41</link>
      <guid>https://dev.to/namatuzio/type-strict-coding-with-typescripts-strict-mode-5h41</guid>
      <description>&lt;p&gt;Around 2 weeks ago, I decided to work on an &lt;a href="https://github.com/google/blockly-samples/issues/2031"&gt;issue&lt;/a&gt; that was unrelated to a lot of what I've done previously. For some context, I've worked extensively in C and C++, which are generally type-strict languages. Though, when thinking about the copious amounts of time I've spent developing sites and such, I've never really utilized TypeScript nor its &lt;a href="https://www.typescriptlang.org/tsconfig#strict"&gt;strict mode&lt;/a&gt;. I became so used to the untyped freedom and tedium that came with JavaScript that I decided to dive into the complete opposite end of the spectrum. It was a fun experience working in TypeScript and strict mode reminded me a lot of working in VS with C/++. Anyhow, here's the process I went through while working on this issue.&lt;/p&gt;

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

&lt;p&gt;Thankfully, the issue itself gave me a direct link to the documentation for strict mode. I took a look and sifted through some of the checks and requirements for enabling and maintaining a strict-mode codebase, and then shortly got to work. &lt;/p&gt;

&lt;p&gt;I browsed the folder that contained the blockly plugin and went straight into the &lt;code&gt;tsconfig.json&lt;/code&gt; for it to enable strict mode, simple and easy. After doing so, I ran &lt;code&gt;npm run build&lt;/code&gt; which showed many of the errors I had to fix because of strict mode type checks.&lt;/p&gt;

&lt;p&gt;Many of these errors were simple enough, &lt;code&gt;TS2531: Object is possibly 'null'.&lt;/code&gt; simply refers to a check for null that must be done to ensure a null value isn't utilized when it's not expected. Other errors like &lt;code&gt;TS2564: Property 'minimapWrapper' has no initializer and is not definitely assigned in the constructor.&lt;/code&gt; simply mean something is declared in a class but is not nullable, therefore they must be assigned in the constructor. It's funny seeing a lot of exceptions and errors that I would run into in C++ appear in code that is almost JavaScript, almost uncanny to me, but this also meant I had a decent idea as to how to fix them.&lt;/p&gt;

&lt;p&gt;For example, a lot of the cases were solved with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// old&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;minimapWrapper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// new &lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;minimapWrapper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple change means that the variable can now expect to be null by default, meaning there's no need to assign anything in the constructor. However, this creates its own slew of additional issues. Now that we know we can expect &lt;code&gt;minimapWrapper&lt;/code&gt; to be null, what happens when we are actually expecting a value? Well, that's where &lt;code&gt;null&lt;/code&gt; checks come into play. Luckily there are many different ways to handle this. Whenever the code requires minimapWrapper to have a value, strict mode will yell at us UNLESS we include this simple check:&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;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;minimapWrapper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Do something...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was essentially the majority of the changes that were needed for everything to work, though there was a strange issue that occurred in the testing files. &lt;/p&gt;

&lt;p&gt;For some odd reason, a reference to the DOM was not being linked, meaning that tests on the specific functionality of the plugin could not be done due to needing a DOM reference to grab the positions of objects. After doing some digging, I found that adding&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsdom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsdom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JSDOM&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsdom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JSDOM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to the top of the file fixed everything, most likely because we're forcing the reference to the DOM to be made by assigning it through an empty HTML doc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changes Requested, Changes Made
&lt;/h2&gt;

&lt;p&gt;After submitting my initial &lt;a href="https://github.com/google/blockly-samples/pull/2078"&gt;PR&lt;/a&gt;, explaining what was changed, and some info on the &lt;code&gt;npm run test&lt;/code&gt; issues I was facing, various changes were requested. A lot of them were very simple changes, whether it be to make something not nullable and instead assign it in a constructor or fixing up the formatting of a certain function. The PR still hasn't been merged, but it's most likely due to the issue with running the tests, the reviewer said they would extensively look into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progression since Hacktoberfest
&lt;/h2&gt;

&lt;p&gt;This was the first time I've made pretty big changes to an existing, and complex codebase. It came with a lot of surprises and I was pleasantly surprised at how easy it was to understand everything right from the get-go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;It was actually pretty fun working with TypeScript and I did enjoy making all the comparisons between it and C++. Seeing certain errors that I never would've expected to see in JS was pretty awesome and it really felt like I was programming in a very familiar yet different language. I look forward to trying my hand at some more TS projects. Thanks for reading and until next time, see ya!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>github</category>
    </item>
    <item>
      <title>Building and Releasing a Python CLI</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Sat, 25 Nov 2023 04:30:17 +0000</pubDate>
      <link>https://dev.to/namatuzio/building-and-releasing-a-python-cli-4728</link>
      <guid>https://dev.to/namatuzio/building-and-releasing-a-python-cli-4728</guid>
      <description>&lt;p&gt;So this week I packaged and released the final version of my CLI, &lt;a href="https://github.com/Namatuzio/tiller"&gt;Tiller&lt;/a&gt;. It is now officially downloadable through &lt;a href="https://test.pypi.org/project/namatuzio-tiller/"&gt;PyPI&lt;/a&gt; as well!&lt;/p&gt;

&lt;p&gt;This was something totally new to me so there was lots to be learned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Package and Release method
&lt;/h2&gt;

&lt;p&gt;One of the first things to consider when releasing any kind of project is &lt;em&gt;where&lt;/em&gt; users will be able to find it. Considering I used Python, I decided to go with a popular package hosting site called &lt;a href="https://test.pypi.org/"&gt;PyPi&lt;/a&gt;. The best part is that there's a very useful &lt;a href="https://packaging.python.org/en/latest/tutorials/packaging-projects/"&gt;guide&lt;/a&gt; on how to do just what I wanted to do!&lt;/p&gt;

&lt;p&gt;This guide helped tremendously with getting everything uploaded and working, but don't worry if you decide to go with other package distribution sites or platforms, there are bound to be many tutorials on how to use it!&lt;/p&gt;

&lt;p&gt;So to get started, I refactored my file structure within my repo to separate test and functional code. I also added an empty &lt;code&gt;__init__.py&lt;/code&gt; file, which I was unsure of its use until today. &lt;code&gt;__init__.py&lt;/code&gt; is a handy file that tells Python what to do when a package is first installed. I also learned that without an init file, Python packages are treated as namespace packages, which for 99% of use cases, is completely unnecessary, especially for my CLI which should be run in a self-contained package. &lt;/p&gt;

&lt;p&gt;Another concept I learned was about &lt;a href="https://packaging.python.org/en/latest/glossary/#term-Build-Backend"&gt;build backends&lt;/a&gt;, an import step which is used to initialize and install any dependencies of the app you're packaging. Since the tutorial went with using &lt;a href="https://hatch.pypa.io/latest/"&gt;Hatch&lt;/a&gt; that is also what I went with, though it didn't provide a lot of useful details especially because it didn't show how to add any dependencies, so I took a look at the docs which were very nice and simple to follow. &lt;/p&gt;

&lt;p&gt;Then I set all the metadata required for my app such as version numbers, names, authors, etc. and moved on to building my application. This was done using &lt;code&gt;py -m build&lt;/code&gt; which generated 2 types of files; a tar.gz which is a &lt;a href="https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist"&gt;source distribution file&lt;/a&gt; and a .whl which is a &lt;a href="https://packaging.python.org/en/latest/glossary/#term-Built-Distribution"&gt;built distribution file&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;A source distribution file is a file that contains raw information about a package and contains only the essential files required for installing from tools like pip or being turned into a built distribution.&lt;/p&gt;

&lt;p&gt;A built distribution file is a pre-built package file that simply needs to find the right home for the package to run as expected. Both were concepts that were somewhat new to me. I've seen built distribution files previously but had no idea what the file extension really meant. &lt;/p&gt;

&lt;p&gt;Anyways, I then installed &lt;a href="https://twine.readthedocs.io/en/latest/"&gt;twine&lt;/a&gt; which would allow me to finally upload my CLI to PyPi. Running &lt;code&gt;py -m twine upload --repository testpypi dist/*&lt;/code&gt; took everything from the previously made &lt;code&gt;dist&lt;/code&gt; file and uploaded it onto PyPi to be openly downloaded. After some bug fixing and a few patch release later, everything was running smoothly, and &lt;a href="https://github.com/Namatuzio/tiller"&gt;tiller&lt;/a&gt; was finally ready to be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems
&lt;/h2&gt;

&lt;p&gt;During the packaging process, I ran into a few problems, it's the reason why my &lt;a href="https://github.com/Namatuzio/tiller/tags"&gt;tags&lt;/a&gt; are all over the place in terms of versioning. Some of the changes were so minuscule I overlooked adding a new patch version.&lt;/p&gt;

&lt;p&gt;A lot of my problems came from my &lt;code&gt;pyproject.toml&lt;/code&gt; file which after uploading everything to PyPi, would not properly load everything which made me lost on how to run my app. It was at this point that I actually decided to take a deeper look at the hatchling docs to find out I was missing the dependencies part. I also decided to add an entry point into my app, allowing users to simply the call to running tiller by adding the following to my &lt;code&gt;pyproject.toml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[project.scripts]&lt;/span&gt;
&lt;span class="py"&gt;tiller&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src.namatuzio_tiller_package.main:app"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets users call all my functionality by calling &lt;code&gt;tiller&lt;/code&gt; as opposed to &lt;code&gt;./main.py&lt;/code&gt; when the app is installed.&lt;/p&gt;

&lt;p&gt;Another issue was forgetting to update certain aspects of my code. For example, when I tested my -v functionality, I had the check set to a default string, when I could've simply imported my &lt;code&gt;__version__&lt;/code&gt; variable from the main file to have it be the same in multiple locations. &lt;/p&gt;

&lt;h2&gt;
  
  
  Testing other CLIs
&lt;/h2&gt;

&lt;p&gt;The CLI I decided to test was &lt;a href="https://github.com/Amir-Helali/Txt2StaticHtml"&gt;Txt2StaticHTML&lt;/a&gt; a package of similar functionality, though done in C#. It was interesting testing it mainly because of how different NuGet packages are from Python packages. I had to open up VS2022 and install the NuGet package. Everything was inside of the namespace the repo owner had developed using the namespace allowed all of the functionality to become available. Calling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Converter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DIR&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ran the default functionality, and even more could be called using a string array. I didn't have much feedback to give especially since the README drew everything out for me. The functionality was all there and it was pretty simple to use and a great refresher on C#. &lt;/p&gt;

&lt;h2&gt;
  
  
  Having my CLI tested
&lt;/h2&gt;

&lt;p&gt;The owner of Txt2StaticHtml also tested my package out. He installed everything from the PyPi page using pip and followed the rest of my README to test the functionality. There wasn't a lot of feedback from the tester because I tried to make my README as descriptive as possible. &lt;/p&gt;

&lt;h2&gt;
  
  
  Try it for yourself!
&lt;/h2&gt;

&lt;p&gt;Did you want to try out Tiller? Well if that's a yes then worry not! It's extremely simple to download and use. &lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Please note that Tiller requires Python 3.11 or higher to run due to the use of TOMLib.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -i https://test.pypi.org/simple/ namatuzio-tiller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Package Usage:
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tiller [OPTIONS] DIR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Options:
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--version, -v  Print the current version of Tiller.
--help, -h     Print the help message.
--config, -c   Specify the path to a TOML config file to be used.
--output, -o   Change the output directory of the .html files.
--lang, -l    Specify the language of the generated HTML file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Display the current version of Tiller:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.\main.py --version (or -v)

Tiller Version: 1.1.0 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Transform a file through a relative path:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tiller .\example.txt
Converted example.txt to example.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.\example.txt

Hello

World\\

How are you?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;.\til\example.html
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;World&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;How are you?&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Generate HTML file with a different language:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tiller --lang fr .\example.txt

Converted example.txt to example.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;.\example.txt

Hello

World

How are you?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;.\til\example.html
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"fr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;116&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;145&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;xx-large&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;example&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;World&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;How are you?&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Transform file(s) using a TOML config file
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tiller --config (or -c) .\examples\TOMLExample.toml .\examples

Converted example.txt to example.html
Converted example2.txt to example2.html
Converted example3.md to example3.html
example4.html is not a .txt file or a .md file. Skipping...

TOMLExample.toml is not a .txt file or a .md file. Skipping...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>opensource</category>
      <category>cli</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CI/CD Part 2: GitHub Actions</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Sat, 18 Nov 2023 04:33:51 +0000</pubDate>
      <link>https://dev.to/namatuzio/cicd-part-2-github-actions-2a6n</link>
      <guid>https://dev.to/namatuzio/cicd-part-2-github-actions-2a6n</guid>
      <description>&lt;p&gt;Previous Post:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/namatuzio" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X9LsHRkq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/practicaldev/image/fetch/s--nRym0FlO--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/1156695/73da8b0b-3124-4195-998e-c343c277a788.png" alt="namatuzio"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/namatuzio/cicd-part-1-unitintegration-testing-3639" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;CI/CD Part 1: Unit/Integration Testing&lt;/h2&gt;
      &lt;h3&gt;Namatuzio ・ Nov 10&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Hey everyone, in a continuation post from my original CI/CD guide/exploration/TIL blog, here's part 2, and as described last time, today is all about &lt;a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions"&gt;GitHub Actions&lt;/a&gt;. As always, &lt;a href="https://github.com/Namatuzio/tiller/pull/23"&gt;here&lt;/a&gt; is the PR for this week. &lt;/p&gt;

&lt;h2&gt;
  
  
  What are GitHub actions?
&lt;/h2&gt;

&lt;p&gt;GitHub actions are simply put, a CI/CD pipeline, with some extra spice added in the mix. For today, I'm going to focus specifically on pipeline creation, which is thankfully, incredibly simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  The process
&lt;/h2&gt;

&lt;p&gt;So, to start off, I browsed through the &lt;code&gt;Build and Test&lt;/code&gt; section of the Actions wiki page to get to the &lt;a href="https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python"&gt;Python GitHub actions setup&lt;/a&gt;. This page was extremely useful and provided everything I needed to get a basic CI setup. &lt;/p&gt;

&lt;p&gt;Everything that is required for setup can be done through the action tab, by creating a &lt;code&gt;new workflow&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9px-0lHr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hqjvlq73w37zopt1bnyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9px-0lHr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hqjvlq73w37zopt1bnyr.png" alt="Actions" width="395" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon clicking &lt;code&gt;New Workflow&lt;/code&gt;, a ton of options are displayed on the screen. 99% of the time, your focus will lie predominately in the &lt;code&gt;Suggested for this repository&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uEkvwWIz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u29vqgqi0xfgjy5x254m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uEkvwWIz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u29vqgqi0xfgjy5x254m.png" alt="New Workflow" width="800" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For my app, I went for the &lt;code&gt;Python Application&lt;/code&gt; configuration which opened up a yaml file which can be configured however you see fit for automatically testing and linting your commits and PRs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F0AqHxPI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szf1isgqm26m3hisa13h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F0AqHxPI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szf1isgqm26m3hisa13h.png" alt="YAML Workflow Template" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This file can of course be updated whenever you want, which is crazy useful in the event that the scale of your app increases significantly. In a previous project, I had a CI/CD pipeline yaml file that would automatically deploy my app to a docker daemon while linting and testing all the functionality, which really drives home the concept I learned in my previous post of integrating CI/CD as early as possible to reduce headaches later on. &lt;/p&gt;

&lt;p&gt;Anyway, because I did use some different libraries I had to change my file up a bit. Firstly, I'm using Python &lt;code&gt;3.12.0&lt;/code&gt; instead of &lt;code&gt;3.10.0&lt;/code&gt;. Secondly, I'm using Ruff as my linter of choice as opposed to flake8 and thankfully GitHub has that covered in their documentation. I simply replaced &lt;code&gt;lint with flake8&lt;/code&gt; with the following code and everything worked perfectly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lint&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Ruff&lt;/span&gt;
  &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;ruff&lt;/span&gt;
    &lt;span class="n"&gt;ruff&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My finalized workflow file looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;

&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;

&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;

    &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;checkout&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;v3&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="mf"&gt;3.12&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="n"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;v3&lt;/span&gt;
      &lt;span class="k"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"3.12.0"&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Install&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;
      &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;upgrade&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;fi&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Lint&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Ruff&lt;/span&gt;
      &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;ruff&lt;/span&gt;
        &lt;span class="n"&gt;ruff&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Test&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
      &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;on:&lt;/code&gt; flag at the top of the file controls when this workflow fires. So pretty much, on a commit to my main branch, or a PR to my main branch, the workflow will trigger, lint my code and ensure there are no errors, and then it will test all the functionality. &lt;/p&gt;

&lt;p&gt;So like every great programmer, I had to break my code to ensure this worked. In a series of commits to my repo, I decided to break some linting rules within my test file to see if it would trigger. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---iMV3axV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ndyt6ncjlrki0i020xxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---iMV3axV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ndyt6ncjlrki0i020xxq.png" alt="Breaking the workflow" width="414" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown by the red X next to some of the commits, it worked! Clicking on the red X lets us investigate the reason for the failure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sVX6UlKw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wrauiuq9e8la4hyu15ih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sVX6UlKw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wrauiuq9e8la4hyu15ih.png" alt="Viewing Errors" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AHA! So the piece of code that breaks the rule is in my test file on line 158, and look at that, it even explains the rule that the line broke. Browsing into the PR gives us an even more useful view of the error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cPYeBP3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hv7tu71r79rd4oisu7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cPYeBP3G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hv7tu71r79rd4oisu7u.png" alt="Viewing code errors" width="800" height="859"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It even outlines each line that features an error too! &lt;/p&gt;

&lt;p&gt;So after getting everything all fixed up, I merged my changes and finished adding a basic CI/CD pipeline to my app!&lt;/p&gt;

&lt;p&gt;That isn't all for this week though, I decided to take a look at someone else's app and add some more test cases to their workflow. I posted an &lt;a href="https://github.com/Amir-Helali/Txt2StaticHtml/issues/10"&gt;issue&lt;/a&gt; to the repo and added a super simple test case for when a file didn't exist. This repo is made entirely in C# which makes it intuitive to work with and easy to develop for. The final test for that repo looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Test&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;FinalizeOutput_NonExistentFile_ThrowsException&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;nonExistentFile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestDirectory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"nonexistent.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throws&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FileNotFoundException&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Helper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FinalizeOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonExistentFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OutputDirectory&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 the same person who owned this repo also added a super simple case to my repo that I had completely overlooked which was pretty much the same case I did for their app. Their issue can be found &lt;a href="https://github.com/Namatuzio/tiller/issues/22"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So that about wraps up this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  TIL
&lt;/h2&gt;

&lt;p&gt;Today I relearned how GitHub actions work and how to easily develop them for any kind of project. I really did forget just how easy it was to integrate CI/CD pipelines which is why this was a great refresher and an awesome learning experience. I loved how easy some of the Python libraries I used made it for me to test, and I adore how intuitive GitHub Actions was to use. &lt;/p&gt;

&lt;p&gt;I hope my posts helped out other newcomers or those just simply looking for a refresher on how to create these powerful pipelines! That's about it for now, thank you to everyone who read the first part and to those who read this part as well, see ya later.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>opensource</category>
      <category>beginners</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>CI/CD Part 1: Unit/Integration Testing</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Fri, 10 Nov 2023 23:48:06 +0000</pubDate>
      <link>https://dev.to/namatuzio/cicd-part-1-unitintegration-testing-3639</link>
      <guid>https://dev.to/namatuzio/cicd-part-1-unitintegration-testing-3639</guid>
      <description>&lt;p&gt;This week, in a setup for a CI/CD pipeline, I added unit and integration testing using &lt;a href="https://docs.pytest.org/en/7.4.x/"&gt;Pytest&lt;/a&gt; to my Python CLI and utilized &lt;a href="https://pypi.org/project/pytest-cov/"&gt;pytest-cov&lt;/a&gt; for generating a coverage report. As always, the merged commit for changes to the repo can be found &lt;a href="https://github.com/Namatuzio/tiller/commit/60f421fad456cbd29dbe9824ef4d4db788a4d4eb"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CI/CD?
&lt;/h2&gt;

&lt;p&gt;CI stands for continuous integration and CD stands for continuous delivery. CI focuses on the foundations of creating new features and testing them rigorously, whereas CD focuses on what to do after code is added and tested. If you've ever worked in a DevOps environment, chances are CI/CD pipelines were something you worked closely with.&lt;/p&gt;

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

&lt;p&gt;So, I knew CI/CD was going to be an inevitable beast to be tamed. Having done it previously, Unit/Integration testing is one of, if not the most important steps to a solid foundation for continuous deployment. Having never done it in Python this was a new experience, though because of the scale of my app, I figured it wouldn't be as bad as the first time I worked with it. &lt;/p&gt;

&lt;p&gt;After looking into various libraries for testing, I decided to use Pytest because it seemed very easy to use and was somewhat similar to other testing libraries I've used previously. &lt;/p&gt;

&lt;p&gt;I feel like I lucked out in choosing Typer to create my app because it has an entire testing library within it which came in great use with Pytest, which I only found out about after I attempted writing my first test. &lt;/p&gt;

&lt;p&gt;It turns out that the way I was handling my Typer app made it difficult to actually call any CLI arguments programmatically. It was funny because it was a single line of code that prevented me from progressing, more specifically this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Typer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add_completion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can't remember why I even added &lt;code&gt;add_completion=False&lt;/code&gt; but it was breaking my tests, so I removed it and got to work. &lt;/p&gt;

&lt;p&gt;The main idea behind unit and integration testing is adding as many small tests as possible to ensure every corner of your app is tested and 100% works as intended. So for my first test, I tested the &lt;code&gt;-v&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CliRunner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# test_main.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_main_version_arg&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"-v"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="s"&gt;"Tiller Version: 0.1.0"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown in the first test, I wanted to have clear function names for tests (which will come in handy later). This will let me easily organize tests for different functionalities of the app. The &lt;code&gt;runner&lt;/code&gt; variable is a &lt;a href="https://typer.tiangolo.com/tutorial/testing/"&gt;CliRunner&lt;/a&gt; which calls the CLI programmatically, which is extremely useful for what I need. Calling &lt;code&gt;runner.invoke()&lt;/code&gt; will call the app (my main function) and will pass the &lt;code&gt;-v&lt;/code&gt; flag to invoke the version flag. The 2nd line of the function uses the bread and butter of unit testing in Python, the &lt;code&gt;assert&lt;/code&gt; keyword. &lt;code&gt;assert&lt;/code&gt; essentially acts as an if statement, if the condition after it is true, the function worked as intended, but if it's false, it will raise an &lt;code&gt;AssertionError&lt;/code&gt; and a custom exit message could be set. This is essentially how all the tests will be developed. &lt;/p&gt;

&lt;p&gt;The next problem arose when I tried to create a test for my output flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Test Output Flag
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_main_output_arg&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isolated_filesystem&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;examples_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;realpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s"&gt;"examples"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copytree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;examples_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"examples"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"-o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"examples"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code is the final version of the test, but I ran into a couple of issues when trying to create it. First off, when using the &lt;code&gt;runner.isolated_filesystem()&lt;/code&gt; function I had issues asserting parts of the output, specifically if the files were actually being copied over. After trying to use &lt;code&gt;shutil.copytree()&lt;/code&gt; I realized it was just throwing over a blank examples folder, so using good ol' os I was able to string together the absolute path that contains the script with the examples folder to create a way to copy the exact contents of the examples folder to out virtual isolated environment. &lt;code&gt;isolated_filesystem()&lt;/code&gt; was another life saver for testing as it let me create a temporary place to run my tests which wouldn't reflect in the actual folder of my app and is actually a part of the backend that fuels Typer, &lt;a href="https://click.palletsprojects.com/en/8.1.x/testing/"&gt;Click&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Pytest was great to work with and extremely simple to use, all I had to do was create a file that had a &lt;code&gt;test_&lt;/code&gt; prefix and then call &lt;code&gt;pytest&lt;/code&gt; in my CMD which ran every test and threw errors whenever one of them broke.&lt;/p&gt;

&lt;p&gt;I wrote around 18 tests, made sure they all passed as expected, then installed &lt;code&gt;pytest-cov&lt;/code&gt; and was happy to see that I had 90% coverage, only missing out on a few lines that weren't functional code. After that, I updated &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; to give users info on how to test and generate coverage reports, added both pytest and pytest-cov to my requirements.txt folder, and merged my branches.&lt;/p&gt;

&lt;h2&gt;
  
  
  A learning experience
&lt;/h2&gt;

&lt;p&gt;In all honesty, I'm blown away by how fluently everything worked together. I've used Jest before for testing a microservice js app, and writing those tests was a pain. Sometimes it would just break for no reason, or some small config line was wrong, but with Pytest setting up CI/CD was amazing. I loved how simple it was to setup and I loved how the library I was using had its own library for helpful testing functions. &lt;/p&gt;

&lt;p&gt;However, I guess the complexity that came with using Jest was mainly because of how late into development I ended up adding it. The app was already a beast, and in comparison, my CLI is still simple, which made me realize an important lesson. The earlier you add a continuous deployment pipeline, the better. In part 2, I'll be looking towards setting up this testing framework with GitHub Actions. Thanks for reading, and see ya next time.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>python</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Code Standards and Consistency with Ruff Linter and Formatter</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Fri, 03 Nov 2023 09:47:48 +0000</pubDate>
      <link>https://dev.to/namatuzio/code-standards-and-consistency-with-ruff-linter-and-formatter-333m</link>
      <guid>https://dev.to/namatuzio/code-standards-and-consistency-with-ruff-linter-and-formatter-333m</guid>
      <description>&lt;p&gt;This week, I decided to add a Linter and Formatter to my codebase. I've used them previously with a project called fragments, which was handy for keeping JavaScript and HTML code standards in check. However, using it for Python is completely foreign to me, especially considering how much easier it is to write stronger code using Python. &lt;/p&gt;

&lt;h2&gt;
  
  
  Adding CONTRIBUTING.md
&lt;/h2&gt;

&lt;p&gt;Outside of adding a linter and formatter, I finally added a CONTRIBUTING.md file to my project, in hopes that anyone who wishes to make it better than it is, can do so easily. I learned of a very awesome &lt;a href="https://generator.contributing.md/"&gt;generator for these md files&lt;/a&gt; and decided to use it for mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Ruff
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.astral.sh/ruff/"&gt;Ruff&lt;/a&gt; is a dual linter and code formatter made in Rust for Python. Very helpful for what I needed and a huge change from previously using eslint and prettier for everything. Also unlike eslint and prettier, ruff comes pre-packaged to work right off the installation. Another interesting aspect of ruff's formatter is that it's actually compatible with &lt;a href="https://pypi.org/project/black/"&gt;Black&lt;/a&gt; another code formatter used in Python. For ease of use and limiting the amount required to install, I just decided to use Ruff. &lt;/p&gt;

&lt;p&gt;The formatter is extremely easy to use - simply running &lt;code&gt;ruff format .&lt;/code&gt; will fix formatting for a file - and highly customizable and can easily be adjusted by creating a &lt;code&gt;pyproject.toml&lt;/code&gt; file, though, the only customization I needed was to exclude certain files and folders from being formatted done as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.ruff.format]&lt;/span&gt;
&lt;span class="py"&gt;exclude&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"examples/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".venv/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".ruff_cache/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"LICENSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".gitignore"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The linter was also just as easy to use - simply running &lt;code&gt;ruff check . --fix&lt;/code&gt; linted a directory and instantly fixed standard errors - and came with a slew of options for customization and configuration. For the ruff-lint configuration, I used one of the templated setups found in the &lt;a href="https://docs.astral.sh/ruff/configuration/#using-pyprojecttoml"&gt;official configuring ruff page&lt;/a&gt; to handle all the linting needs. &lt;/p&gt;

&lt;p&gt;Another benefit to using ruff is that I simply added it to my requirements.txt file allowing anyone who may want to run the app to install it and be able to utilize it. VSCode has an official plugin for it as well which allowed me to turn on automatic linting:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5x5STFXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61jwbm1o6a9re53qiy8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5x5STFXF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61jwbm1o6a9re53qiy8h.png" alt="RuffAutoLint" width="598" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ruff features a neat feature where it will check for specific comments to skip over re-formatting a specific chunk of code. Adding &lt;code&gt;# fmt: off&lt;/code&gt; and &lt;code&gt;# fmt: on&lt;/code&gt; around a code block would prevent it from getting formatted.&lt;/p&gt;

&lt;h2&gt;
  
  
  How effective were they?
&lt;/h2&gt;

&lt;p&gt;Well to be quite honest, linting my file didn't do too much, mostly because I tried to keep my implementation as bare-bones as possible but there was one error, which was an unused import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main.py:14:31: F401 [*] `typing_extensions.Annotated` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When formatting my file, it changed a good deal, mainly in the way it handled my main function header and the formatting for it. Other than that, it seemed to have changed mostly small indentation errors here and there.&lt;/p&gt;

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

&lt;p&gt;I want to try and implement a pre-commit hook onto my code, so that it can auto-lint and format everything before each commit. I wasn't able to get it this time around due to time constraints but it's definitely something I'd love to look into since it's very foreign to me. &lt;/p&gt;

&lt;h2&gt;
  
  
  TIL
&lt;/h2&gt;

&lt;p&gt;Today I learned about Ruff, a very awesome library that contains not only a source code formatter, but a linter as well! I learned the basics of utilizing the formatter and the linter as well as basic setup for both. It's awesome stuff and really goes to show just how amazing Python and the community surrounding it is. &lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Post-Hacktoberfest Recap</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Tue, 31 Oct 2023 20:47:08 +0000</pubDate>
      <link>https://dev.to/namatuzio/post-hacktoberfest-recap-3dfn</link>
      <guid>https://dev.to/namatuzio/post-hacktoberfest-recap-3dfn</guid>
      <description>&lt;p&gt;Welp, that's it, Hacktoberfest is coming to a close, all 4 of my PRs have been completed, and most importantly, it's Halloween!&lt;/p&gt;

&lt;h2&gt;
  
  
  Early Contributions
&lt;/h2&gt;

&lt;p&gt;The most important but scary thing about Hacktoberfest for me was finding issues that are not only good for my growth but can also be great contributions to a community of people. It was worrying having to scramble around looking for such issues, but luckily GitHub has you covered with their comprehensive search functionality. I found the issues I wanted to work on by scrolling through the &lt;code&gt;Hacktoberfest&lt;/code&gt; label on GitHub. During the earlier days of October, it is completely swamped with issues both big and small, complex and simple. Making it super easy to find the perfect one for any skill level. Furthermore, for my very first issue, I added the &lt;code&gt;good first issue&lt;/code&gt; tag which was groups of issues that are relatively low in scope and easy to understand. The great thing about GitHub searches is that you can also further filter these issues by looking for specific languages. If you feel more comfortable with Python, simply filter all your results to display repos with their main language being Python. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t_sMh3RX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7ofw3wog1o2yz86gh1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t_sMh3RX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n7ofw3wog1o2yz86gh1f.png" alt="GithubFilter" width="293" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PRs
&lt;/h2&gt;

&lt;p&gt;I had a fantastic time working on all of my PRs and truly loved how kind all the repo owners were when giving feedback. If Hacktoberfest taught me anything, it's that the open-source community is absolutely great! I loved the idea of contributing to someone's project and getting real experience working with others to deliver a solid product. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/bhargavnova/python-helper-modules/pull/22"&gt;PR1&lt;/a&gt; Had me create a Python timer class to get the runtime of functions and entire scripts. I shared my thoughts and process &lt;a href="https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-1-2f96"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/KriteshTimsina/Wassupbroo-Chat-App/pull/3"&gt;PR2&lt;/a&gt;&lt;br&gt;
Had me implement an emoji keyboard into a web app which was done with a library and a whole lot of blood, sweat, and CSS. I shared the process of developing and finding a proper library &lt;a href="https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-2-48j6"&gt;here&lt;/a&gt; along with my battle with CSS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/13"&gt;PR3&lt;/a&gt; Had me update an existing functionality to accommodate for a larger amount of data, and weave in loading techniques to not break the app. I shared my process and hardships &lt;a href="https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-3-5a61"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/17"&gt;PR4&lt;/a&gt; Had me eager to return to the previous functionality and add a system of advanced filtration to the data, allowing users to easily sift through data they may deem important. I shared my thoughts and the overcomplex process &lt;a href="https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-4-450k"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TIL
&lt;/h2&gt;

&lt;p&gt;I learned a whole lot from Hacktoberfest, honestly, probably more in a single month than I have in the last 3. I learned a lot of new concepts and implementation techniques, and a lot more about myself and the mistakes I make during the implementation of a functionality. I feel like I've made a lot of progress in both knowledge and hands-on skills with a lot of concepts from the 1st PR to the last and it really is a great feeling. Most importantly I learned how great Hacktoberfest is, with this being my first one, I will definitely try to not make it my last. The open-source community around Hacktoberfest is truly something awesome. Thank you for reading, and I hope your Halloweens are fantastic.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>hacktoberfest</category>
      <category>webdev</category>
      <category>python</category>
    </item>
    <item>
      <title>Contributing to the Community: Hacktoberfest PR #4</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Tue, 31 Oct 2023 20:20:30 +0000</pubDate>
      <link>https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-4-450k</link>
      <guid>https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-4-450k</guid>
      <description>&lt;p&gt;This is it, the final &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/15"&gt;issue&lt;/a&gt; of Hacktoberfest 2023 and it's one that I made! So, since the last PR, I had a couple of ideas for the &lt;a href="https://github.com/TheShiveshNetwork/Pokedex"&gt;repo&lt;/a&gt; that I was working on. Considering the last issue was all about the search functionality, I decided I wanted to create a more advanced search function. As noted in the issue I filed, I decided to add a generational filter for all the Pokemon, meaning Pokemon that appear in Generation 1 will only be visible, same with filtering by Generation 2, 3, etc... Which I figured would bring a lot more challenges this time around than the previous PR. So I got to work.&lt;/p&gt;

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

&lt;p&gt;At first, I created a checkbox, but quickly realized checkboxes would not be nice if more filters were to ever be added. So I created a simple dropdown menu in &lt;code&gt;Home.jsx&lt;/code&gt; and loaded it with the different Generations, 1-9. So now, I had to figure out a way of grabbing the Pokemon from all the different generations. I figured I would most likely have to use a req URL, so I began my implementation. While developing this feature I found out PokeAPI has a generations endpoint - &lt;code&gt;GET https://pokeapi.co/api/v2/generation/{id or name}/&lt;/code&gt; - which displays the following data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UjDF9MC5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0c8pgvg7988hhj1doysi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UjDF9MC5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0c8pgvg7988hhj1doysi.png" alt="GenEndpoint" width="459" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"This is great!" I thought, "This is just what I need to implement this!". As hindsight is 20/20 I know this is absolutely not the case for what I was going for. After countless nights of broken filters and multiple API calls, I was stuck. I decided to dial back my implementation, clearly what I was going for was too complex for the app to work flawlessly. So, I decided to create values for every dropdown item and make them the number of Pokemon added in that generation and the number of Pokemon that were previously available. After some simple filtering, it worked perfectly. A common problem of mine is trying to overcomplicate functional design which is why I was happy that the solution was so simple. &lt;/p&gt;

&lt;h2&gt;
  
  
  Wandering Mind
&lt;/h2&gt;

&lt;p&gt;Then, my brain decided to wander off... "What if I added a filter that could sort Pokemon by each game and it would show the version exclusives present in each, the Pokemon added in that game including alternate forms, and the domestic Pokedex".&lt;/p&gt;

&lt;p&gt;Well, I thought it would be really cool so I got to work. I once again added another dropdown this time filled with almost every mainline Pokemon game from red and blue to scarlet and violet. I also increased the API call from 1017 to 1292, which encompasses every single Pokemon that the PokeAPI has numbered which will come into play later. &lt;/p&gt;

&lt;h2&gt;
  
  
  Process Part 2
&lt;/h2&gt;

&lt;p&gt;So after contemplating how something like this would be possible, I went to the documentation page for PokeAPI and went back to the generation endpoint. I noticed the version-group JSON object and figured this might be just what I needed to grab all the information about the Pokemon from each version. I realized at this point that I would most likely need to utilize Python in order to scrape the API for every single Pokedex, this is when I dug deeper and came across the region endpoint: &lt;br&gt;
&lt;code&gt;GET https://pokeapi.co/api/v2/region/{id or name}/&lt;/code&gt;&lt;br&gt;
which would produce &lt;code&gt;version-groups&lt;/code&gt; of each game that features this pokedex entirely. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cAmi8jNA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n2h4bi9ckenve0bwoc4t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cAmi8jNA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n2h4bi9ckenve0bwoc4t.png" alt="RegionEndpoint" width="403" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was huge because it meant making an API scraper in Python would be so much easier, I would have to iterate a for loop 9 times grab the &lt;code&gt;version-group&lt;/code&gt; data from the JSON response and add it to a JSON file. This also gave me an opportunity to use code from my 1st PR to test the runtime of my scraper! So after all was done, I had a &lt;code&gt;generations&lt;/code&gt; folder that looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W4t7usQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4steiqkjmez2qmy9a7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W4t7usQr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s4steiqkjmez2qmy9a7w.png" alt="GenFolder" width="188" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P2hMlJdH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmmuoh1ewnmioh78i7iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P2hMlJdH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmmuoh1ewnmioh78i7iw.png" alt="GenJson" width="559" height="904"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the code used to gather the information was also rather simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I would take the &lt;code&gt;data&lt;/code&gt; and simply filter it out for the &lt;code&gt;version-endpoint&lt;/code&gt; JSON object.&lt;/p&gt;

&lt;p&gt;So with all that done, it was time to iterate through the generations. &lt;/p&gt;

&lt;p&gt;The version-groups endpoint luckily gave me references to Pokedex, though they're not as game-specific as they could be, but that's fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MpfrNkqD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkkko60jqnsx2w4tgdl6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MpfrNkqD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkkko60jqnsx2w4tgdl6.png" alt="PokeAPIEndpoint" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, I ran through each game's Pokedex, grabbing all the Pokemon under &lt;code&gt;pokemon_species&lt;/code&gt;. The data for that looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# black-2-white-2.json
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"entry_number"&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="s"&gt;"pokemon_species"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"victini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://pokeapi.co/api/v2/pokemon-species/494/"&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="s"&gt;"entry_number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"pokemon_species"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"snivy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://pokeapi.co/api/v2/pokemon-species/495/"&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;And then further filtered everything to only grab the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;url&lt;/code&gt; split to only use it's dex number from &lt;code&gt;pokemon_species&lt;/code&gt; until I had something a little more useable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# unova.json
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"black-2-white-2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"Pokemon"&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="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"victini"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"494"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"snivy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"495"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then separated each version into its own JSON object, meaning &lt;code&gt;black-2&lt;/code&gt; and &lt;code&gt;white-2&lt;/code&gt; were separated. Next is the fun part, the version exclusives. For the entire span of the mainline games, Pokemon has always released 2 versions together and sometimes a 3rd as a step up from the previous versions. the 2 versions often featured slightly different Pokemon to get people to trade with one another, or outright buy both versions, and the 3rd version would usually add a Pokemon unavailable in the previous versions but also have some Pokemon left out to once again, allow players to trade with others.&lt;/p&gt;

&lt;p&gt;My best friend for this section was a website I've used extensively over the years, &lt;a href="https://www.serebii.net/"&gt;seribii.net&lt;/a&gt; not only does it feature most of what you would need for the games, but it also gives you a nice list of version exclusive pokemon for each and every pokemon game. &lt;/p&gt;

&lt;p&gt;So I created a file called &lt;code&gt;exclu.py&lt;/code&gt; and added each generational exclusive to an array and iterated the data in the regional files extracting certain Pokemon from the opposite versions&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h8qXIsBN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xq7w9tfksls6kt986x07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h8qXIsBN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xq7w9tfksls6kt986x07.png" alt="KantoExclusives" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;which I ended up doing 20 or so times, adding some tweaks here and there for the additional Pokemon forms and regional variants that have been added over the years. Which finally gave me 8 files with every domestic pokedex from every single mainline Pokemon game, excluding BDSP, and their version-exclusive Pokemon including version-exclusive forms such as Mega-Mewtwo X and Y. I combined the files into one mega file and used that as a filter for the main web app, which ended up working flawlessly and was done with absolutely no additional API calls allowing for both filters to work at once. Here's an example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WydkMcY_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3n9t4h563is6oicsvr40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WydkMcY_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3n9t4h563is6oicsvr40.png" alt="AdvancedFilters" width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The live site can be found &lt;a href="https://sprightly-marshmallow-70a015.netlify.app/"&gt;here&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;The final PR can be found &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/17"&gt;here&lt;/a&gt;, and with that, my first Hacktoberfest comes to a close!&lt;/p&gt;

&lt;h2&gt;
  
  
  What would I do differently?
&lt;/h2&gt;

&lt;p&gt;As I said earlier, I have a really bad habit of overcomplicating functionalities. It would be best if in the future I take a step back and find out the fastest way to implement something and break down the functionality to its most basic roots. Thank you for reading and I hope you have a fantastic Halloween!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>hacktoberfest</category>
      <category>python</category>
    </item>
    <item>
      <title>Contributing to the Community: Hacktoberfest PR #3</title>
      <dc:creator>Namatuzio</dc:creator>
      <pubDate>Tue, 31 Oct 2023 19:05:04 +0000</pubDate>
      <link>https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-3-5a61</link>
      <guid>https://dev.to/namatuzio/contributing-to-the-community-hacktoberfest-pr-3-5a61</guid>
      <description>&lt;p&gt;My &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/issues/9"&gt;3rd issue&lt;/a&gt; this Hacktober was in a fun web app that was looking to recreate the Pokedex. This particular issue was about improving the search functionality, more specifically by increasing the number of Pokemon from 20 to 1017 and the many variants that come with that number.&lt;/p&gt;

&lt;p&gt;As someone who loves Pokemon and grew up with the series, I figured I'd give it a shot since it would be something fun. &lt;/p&gt;

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

&lt;p&gt;Based on the requirements realistically all I would need to do is change the request URL for the PokeAPI call. So, when I first forked the app, I explored the many files found within trying to find where my addition would go. Luckily everything was nicely organized within folders and I ended up finding it in a file called &lt;code&gt;FetchApi.js&lt;/code&gt;. Simply changing the number from 20 to 1017 would have technically met the requirements for the overall functionality. Though the way things were being handled when a user loaded the site, 1017 requests were simultaneously made to grab the image files of every Pokemon, which of course is terrible practice and ended up breaking the app.&lt;/p&gt;

&lt;p&gt;The requirements also noted to reduce the number of API calls being made in order to speed the app up, so clearly the way this information was being handled wasn't appropriate for 1017 Pokemon. &lt;/p&gt;

&lt;p&gt;To fix this, I decided to implement 'Lazy Loading' a loading technique used on webpages with a lot of dynamic data that allows users to batch call data when it's needed (i.e. when it's on their screen). After getting it implemented in the &lt;code&gt;PokemonCard.jsx&lt;/code&gt; file, everything worked flawlessly! There was one small problem though, some data in the PokeAPI wasn't entirely up to date, most likely because some Pokemon were too new. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QaWhiGYQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqft0i0tslih09u7jc3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QaWhiGYQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqft0i0tslih09u7jc3w.png" alt="PokedexErrImg" width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though Sinistcha has some art floating around, none of it is really official and it's mainly just in-game models that are used. I consulted the owner of the repo and asked how I should handle it. I proposed either leaving it or adding a default image and the owner said to add a default image for Pokemon with no image references. I added the default image and it definitely looked a lot better:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P4WDxG1X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f2qr3say789a8cltbbpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4WDxG1X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f2qr3say789a8cltbbpg.png" alt="PokedexImgFix" width="800" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and with that, my job was done... or so I thought. Turns out, I completely forgot to fetch and pull from the main repo, and to make things worse, there were a ton of changes to it. The biggest change was a version of literal Infinite Scrolling which seemed to have completely broken the app. The way this was being handled was by constantly calling the API as you scroll down. The worst part about it was that it would constantly fling you back to the top of the page. Here's an example using my favourite Pokemon as to how it broke the search functionality:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iNZnE5tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3zoy3e9ltyt6xveg9o9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iNZnE5tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3zoy3e9ltyt6xveg9o9.png" alt="BrokenPokedexSearch" width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The owner ended up asking me to remove the infinite scrolling stuff, and after getting that resolved, everything was done. The final PR can be found &lt;a href="https://github.com/TheShiveshNetwork/Pokedex/pull/13"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What would I do differently?
&lt;/h2&gt;

&lt;p&gt;Well, this was definitely a big step up from the previous PR but I definitely made mistakes that I have to learn from. Not keeping my fork updated, made my job way harder than it needed to be. It's definitely something that I have to keep in mind when developing an active repo and a lesson I won't soon forget. &lt;/p&gt;

</description>
      <category>opensource</category>
      <category>typescript</category>
      <category>hacktoberfest23</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
