<?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: Wispy </title>
    <description>The latest articles on DEV Community by Wispy  (@wispyco).</description>
    <link>https://dev.to/wispyco</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%2F291227%2F20016c0d-9fbe-45c8-91db-288f8bf7b57a.png</url>
      <title>DEV Community: Wispy </title>
      <link>https://dev.to/wispyco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wispyco"/>
    <language>en</language>
    <item>
      <title>Yo, I built a simple domain name generator, but it's kinda different...</title>
      <dc:creator>Wispy </dc:creator>
      <pubDate>Mon, 25 Dec 2023 18:36:10 +0000</pubDate>
      <link>https://dev.to/wispyco/yo-i-built-a-simple-domain-name-generator-but-its-kinda-different-cp4</link>
      <guid>https://dev.to/wispyco/yo-i-built-a-simple-domain-name-generator-but-its-kinda-different-cp4</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nv9wQcGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6v42rz50ufvsigjnrxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nv9wQcGu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6v42rz50ufvsigjnrxm.png" alt="At my desk thinking up domain names" width="798" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Often I start a new project and I want to buy a domain for it. this leaves me at odds sometimes for I know a lot of the projects will not make it, or maybe I will come back to them one day. I have let a lot of domains expire though.&lt;/p&gt;

&lt;p&gt;However the process of coming up with domains I absolutely love, and there are already some good existing tools out there I don't want to name a bunch but &lt;a href="https://namelix.com/"&gt;https://namelix.com/&lt;/a&gt; is fun.&lt;/p&gt;

&lt;p&gt;One thing I noticed is that there are a ton of TLD's out there now and a lot of them with specific meaning. You can see the full list here &lt;a href="https://data.iana.org/TLD/tlds-alpha-by-domain.txt"&gt;https://data.iana.org/TLD/tlds-alpha-by-domain.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had a an idea 💡&lt;/p&gt;

&lt;p&gt;Something was missing from most generators maybe it is out there but I couldn't find it, so I started to build a tool just for me, and kept it on my laptop for months using it each time I needed to come up with a domain.&lt;/p&gt;

&lt;p&gt;What does it do?&lt;/p&gt;

&lt;p&gt;It's simple really, if you want a unique TLD with a short word you can find the TLD use a thesaurus to find associative words and then go do a domain registrar and see if it is avaialbe like &lt;a href="https://stripe.sale"&gt;https://stripe.sale&lt;/a&gt; or &lt;a href="https://vehical.car"&gt;https://vehical.car&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can do this endlessly and make a short list of your favourite but it's time consuming.&lt;/p&gt;

&lt;p&gt;That's why I built &lt;a href="https://yostatus.com"&gt;https://yostatus.com&lt;/a&gt; it's super clunky right now and it's not designed yet, but it does the trick for now.&lt;/p&gt;

&lt;p&gt;I used Domainr API to check availability, and Domainr API to reroute users to buy links routed through DNSimple. I want have a buy option on the site but for now it's just a fun tool.&lt;/p&gt;

&lt;p&gt;The funny thing is I originally used OpenAI to find associative words to the TlD, but it was slow and expensive, and turns out a free thesaurus dictionary API was faster and worked better.   &lt;/p&gt;

&lt;p&gt;Anyways if your looking for a new domain name maybe this could be another tool in your belt.&lt;/p&gt;

&lt;p&gt;I hope I can keep it functional and that it helps just one person that would be great.&lt;/p&gt;

&lt;p&gt;Kindly,&lt;/p&gt;

&lt;p&gt;Anders from &lt;a href="https://wispy.co"&gt;https://wispy.co&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to build a Stripe App OAuth with Next.js: A Beginner's Guide</title>
      <dc:creator>Wispy </dc:creator>
      <pubDate>Mon, 25 Dec 2023 18:02:53 +0000</pubDate>
      <link>https://dev.to/wispyco/how-to-build-a-stripe-app-oauth-with-nextjs-a-beginners-guide-358e</link>
      <guid>https://dev.to/wispyco/how-to-build-a-stripe-app-oauth-with-nextjs-a-beginners-guide-358e</guid>
      <description>&lt;p&gt;OAuth has become the cornerstone of modern web authentication, allowing users to grant websites and applications access to their information on other websites but without giving them the passwords. This tutorial will guide you through the process of implementing OAuth in a Next.js application, specifically focusing on a common use case: integrating with Stripe App Marketplace.&lt;/p&gt;

&lt;p&gt;If you want to skip the hand build process checkout &lt;a href="https://stripeapp.build"&gt;https://stripeapp.build&lt;/a&gt; for boilerplate starter point.&lt;/p&gt;

&lt;p&gt;Prerequisites &lt;br&gt;
Create a stripe app, following this getting started guide &lt;a href="https://stripe.com/docs/stripe-apps/create-app"&gt;https://stripe.com/docs/stripe-apps/create-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow steps 1,2,3 and three here &lt;a href="https://stripe.com/docs/stripe-apps/api-authentication/oauth"&gt;https://stripe.com/docs/stripe-apps/api-authentication/oauth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have a install link for your app, were ready to implement oAuth in Next JS&lt;/p&gt;

&lt;p&gt;What is OAuth?&lt;br&gt;
Before diving into the code, let's understand what OAuth is. It's an open standard for access delegation. In simpler terms, it's a way of letting your users log in to your web application using their information from another website (like Google, Facebook, or Stripe in this case but we are actually allowing our Stripe app to connect to our backend instead of a login process).&lt;/p&gt;

&lt;p&gt;Setting Up Your Next.js Environment&lt;br&gt;
For this tutorial, we'll be working with Next.js, a popular React framework. Ensure you have Node.js installed on your computer before starting. Create a new Next.js project by running &lt;code&gt;npx create-next-app my-oauth-app&lt;/code&gt; in your terminal, and navigate into your project directory.&lt;/p&gt;

&lt;p&gt;Integrating Stripe OAuth&lt;br&gt;
This example demonstrates how to integrate Stripe's OAuth process into a Next.js API route.&lt;/p&gt;

&lt;p&gt;Step 1: Setting Up Environment Variables&lt;br&gt;
Create a .env.local file in the root of your project and add your Stripe API keys:&lt;/p&gt;

&lt;p&gt;STRIPE_TEST=your_test_api_key&lt;br&gt;
STRIPE_PRODUCTION=your_production_api_key&lt;br&gt;
Replace &lt;code&gt;your_test_api_key&lt;/code&gt; and &lt;code&gt;your_production_api_key&lt;/code&gt; with your actual Stripe API keys.&lt;/p&gt;

&lt;p&gt;Step 2: Writing the OAuth Endpoint&lt;br&gt;
Create a new file under &lt;code&gt;pages/api&lt;/code&gt;, say &lt;code&gt;stripe-oauth.js&lt;/code&gt;. Here's how you can adapt the provided code snippet for OAuth with Stripe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NextApiRequest, NextApiResponse } from "next";
import btoa from 'btoa'; // You might need to install the 'btoa' package
// Use environment variables for the API key
const STRIPE_API_KEY = process.env.NODE_ENV === 'production' ? process.env.STRIPE_PRODUCTION : process.env.STRIPE_TEST;
const encodedKey = btoa(STRIPE_API_KEY + ":");

// Utility function for setting CORS headers
const setCORSHeaders = () =&amp;gt; ({
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers":
    "Content-Type, stripe-signature, Authorization",
});

// Your API endpoint
export async function POST(req: NextApiRequest, res: NextApiResponse) {
  try {
    const body = await req.json();
    const stripeOAuth = await fetch("https://api.stripe.com/v1/oauth/token", {
      method: "POST",
      headers: {
        Authorization: `Basic ${encodedKey}`,
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        code: body.code.value,
        grant_type: "authorization_code",
      }),
    })
      .then((response) =&amp;gt; response.json())
      .then((data) =&amp;gt; {
        // console.log("stripe-oauth", data);
        return data;
      })
      .catch((error) =&amp;gt; {
        console.error("Error:", error);
      });

    const stripeOAuthRefresh = await fetch(
      "https://api.stripe.com/v1/oauth/token",
      {
        method: "POST",
        headers: {
          Authorization: `Basic ${encodedKey}`,
          "Content-Type": "application/x-www-form-urlencoded",
        },
        body: new URLSearchParams({
          refresh_token: stripeOAuth.refresh_token,
          grant_type: "refresh_token",
        }),
      },
    )
      .then((response) =&amp;gt; response.json())
      .then(async (data) =&amp;gt; {
        return data;
      })
      .catch((error) =&amp;gt; {
        console.error("Error:", error);
      });

    return new Response(
      JSON.stringify({
        stripeOAuth: stripeOAuth,
        stripeOAuthRefresh: stripeOAuthRefresh,
      }),
      {
        status: 200,
        headers: {
          ...setCORSHeaders(),
          "Content-Type": "application/json",
        },
      },
    );
  } catch (error) {
    // Check if error is an instance of Error
    if (error instanceof Error) {
      console.error("Error in POST request:", error.message);
      return new Response(error.message, {
        status: 500, // Internal Server Error
        headers: setCORSHeaders(),
      });
    } else {
      // Handle the case where the error is not an instance of Error
      console.error("An unknown error occurred:", error);
      return new Response("An unknown error occurred", {
        status: 500,
        headers: setCORSHeaders(),
      });
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Implementing the Frontend&lt;br&gt;
In your front-end code, create a simple fetch request, that grabs the stripe code from the URL params&lt;/p&gt;

&lt;p&gt;Step 4: Testing&lt;br&gt;
Run your application npm run dev and test the OAuth flow. Ensure you handle any errors and the user experience is smooth.&lt;/p&gt;

&lt;p&gt;Best Practices and Tips&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; Always keep your API keys secure. Never expose them in your client-side code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Environment Variables:&lt;/strong&gt; Use different keys for development and production to avoid mixing up data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Handling:&lt;/strong&gt; Implement robust error handling to improve the user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing:&lt;/strong&gt; Regularly test the OAuth flow, especially after updating dependencies or making significant changes to your code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conclusion&lt;br&gt;
Integrating OAuth into your Next.js application might seem daunting initially, but it's a powerful tool to enhance user experience and security. By following the steps outlined in this tutorial, you should have a basic but functional OAuth implementation with Stripe in your Next.js application.&lt;/p&gt;

&lt;p&gt;Remember, OAuth is a broad topic, and Stripe is just one of the many services that use it. The principles you learned here can be applied to other services as well. Keep exploring and happy coding!&lt;/p&gt;

&lt;p&gt;If you are looking to speed up your Stripe App Development checkout &lt;a href="https://stripeapp.build"&gt;https://stripeapp.build&lt;/a&gt; for boilerplate code to get you going.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Emojigraph: Conventional Commits Npm package in React with Chart.js and simple-git</title>
      <dc:creator>Wispy </dc:creator>
      <pubDate>Wed, 13 Dec 2023 15:47:09 +0000</pubDate>
      <link>https://dev.to/wispyco/emojigraph-conventional-commits-npm-package-in-react-with-chartjs-and-simple-git-4den</link>
      <guid>https://dev.to/wispyco/emojigraph-conventional-commits-npm-package-in-react-with-chartjs-and-simple-git-4den</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5jkGjSze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wz79wmpo40e93w2728th.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5jkGjSze--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wz79wmpo40e93w2728th.png" alt="React Conventional Commits Graph" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What am I looking at?
&lt;/h3&gt;

&lt;p&gt;It's a package that graphs your conventional commits into a Emoji Chart based on the number of types of commits. I built this package while working on a side project to see where I was spending my time. What's get measured get's managed don't you know. You can install the package here &lt;a href="https://www.npmjs.com/package/react-conventional-commits-graph"&gt;https://www.npmjs.com/package/react-conventional-commits-graph&lt;/a&gt; however it may have bugs so please open an issue since it's my first package. Now let's understand some prerequisites...&lt;/p&gt;

&lt;p&gt;Oh and if you want to see the graph live on a page here it is&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.stripeappkit.com/api/commits"&gt;https://app.stripeappkit.com/api/commits&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Conventional Commits
&lt;/h3&gt;

&lt;p&gt;Conventional commits are a specification for adding human and machine-readable meaning to commit messages. They follow a structured format to make the history of a repository more accessible and easier to navigate. Here's a brief overview of their key elements and importance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structured Format&lt;/strong&gt;: Conventional commits require a specific format for commit messages, typically starting with a type (e.g., &lt;code&gt;feat&lt;/code&gt;, &lt;code&gt;fix&lt;/code&gt;), optionally followed by a scope, and a description. For example: &lt;code&gt;feat(database): add new indexing feature&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Types of Commits&lt;/strong&gt;: Common types include &lt;code&gt;feat&lt;/code&gt; (new feature), &lt;code&gt;fix&lt;/code&gt; (bug fix), &lt;code&gt;docs&lt;/code&gt; (documentation changes), &lt;code&gt;style&lt;/code&gt; (style changes that do not affect meaning), &lt;code&gt;refactor&lt;/code&gt; (code changes that neither fix a bug nor add a feature), &lt;code&gt;test&lt;/code&gt; (adding missing tests), and &lt;code&gt;chore&lt;/code&gt; (maintenance tasks).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;: This is an optional part that describes the part of the codebase affected by the changes (e.g., &lt;code&gt;feat(auth)&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;: A concise description of the changes made.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Importance&lt;/strong&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Readability&lt;/strong&gt;: Makes it easier to understand the purpose of each commit at a glance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Tooling&lt;/strong&gt;: Enables automated tools to process commit messages and generate changelogs, version bumps, and other outputs based on the type of commits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Collaboration&lt;/strong&gt;: Helps team members quickly understand the nature of changes, improving collaboration and review processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Management&lt;/strong&gt;: Assists in tracking the progress of features, fixes, and other types of changes in a project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, conventional commits provide a standardized way to write commit messages, enhancing readability, supporting tool automation, and improving collaborative development workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the NPM Package
&lt;/h3&gt;

&lt;p&gt;We are going to be using mircobundle to package up the npm package for distribution as a react component.&lt;/p&gt;

&lt;h4&gt;
  
  
  make sure to globally install &lt;code&gt;microbundle&lt;/code&gt; &lt;code&gt;npm i -g microbundle&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Steps&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a folder with project name mine was called commit-graph&lt;/li&gt;
&lt;li&gt;Copy this into a new package.json file in the folder
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"package-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"package description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.2.8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.esm.cjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"microbundle --jsx 'React.createElement' --jsxImportSource react --globals react/jsx-runtime=jsx --format es,cjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"microbundle watch --jsxFragment React.Fragment"&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"chart.js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.4.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"react-chartjs-2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"simple-git"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.21.0"&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"peerDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^17.0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^17.0.2"&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"@babel/cli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.23.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"@babel/core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.23.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"@babel/preset-env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.23.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"@babel/preset-react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.23.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"babel-loader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^9.1.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"microbundle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.15.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"webpack-bundle-analyzer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.10.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"webpack-cli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.1.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"webpack-dev-server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.15.1"&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"extract-commits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"server/processData.js"&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add a server folder at the root and a file called &lt;code&gt;processData.js&lt;/code&gt; with the following code
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   #!/usr/bin/env node
   import simpleGit from 'simple-git';
   import fs from "fs"

   const localPath = "./";

   // Check if the local repository exists
   if (fs.existsSync(localPath)) {
       // If it exists, use the existing repository
       processData(localPath);
   } else {
       console.error('Local repository does not exist.');
   }

   function processData(path) {
       simpleGit(path).log()
           .then(log =&amp;gt; {
               const messages = log.all.map(commit =&amp;gt; commit.message);

               // Write commit messages to a JSON file
               fs.writeFileSync('./public/commitMessages.json', JSON.stringify(messages, null, 2));
               console.log('Commit messages have been written to commitMessages.json');
           })
           .catch(err =&amp;gt; console.error('Error in analyzing repository:', err));
   } 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;lastly create a &lt;code&gt;src&lt;/code&gt; folder with a components folder and &lt;code&gt;index.js&lt;/code&gt; file in the root of &lt;code&gt;src&lt;/code&gt; and a &lt;code&gt;CommitChart.js&lt;/code&gt; file in the root of &lt;code&gt;components&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export { default as CommitChart } from './components/CommitChart';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CommitChart.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useEffect, useRef, useState } from "react";
import { Bar } from "react-chartjs-2";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
} from "chart.js";

const emojiMap = {
  bugs: "🐛",
  features: "✨",
  chores: "🧹", // Example emoji for chores
  fixes: "🔧", // Example emoji for fixes
  // others: "📦", // Example emoji for others
  docs: "📝",
  refactor: "♻️",
};

const emojiPlugin = {
  id: "emojiPlugin",
  afterDraw: (chart) =&amp;gt; {
    const ctx = chart.ctx;

    chart.data.datasets.forEach((dataset, datasetIndex) =&amp;gt; {
      const meta = chart.getDatasetMeta(datasetIndex);
      meta.data.forEach((bar, index) =&amp;gt; {
        const emoji = emojiMap[chart.data.labels[index]];
        const { x, y, base, width, height } = bar;
        const dataValue = dataset.data[index]; // The number of emojis we want to show

        // Calculate the number of rows and columns of emojis based on the data value
        const numRows = Math.ceil(Math.sqrt(dataValue));
        const numCols = Math.ceil(dataValue / numRows);

        // Calculate the size of each emoji to fit the bar's dimensions
        const emojiWidth = width / numCols;
        const emojiHeight = height / numRows;
        const emojiSize = Math.min(emojiWidth, emojiHeight); // Use the smallest to fit both dimensions

        ctx.font = `${emojiSize}px Arial`;

        // Draw the emojis in a grid pattern
        // for (let row = 0; row &amp;lt; numRows; row++) {
        //   for (let col = 0; col &amp;lt; numCols; col++) {
        //     if (row * numCols + col &amp;lt; dataValue) { // Ensure we don't draw more emojis than the data value
        //       const emojiX = x - width / 2 + col * emojiWidth;
        //       const emojiY = base - (numRows - row) * emojiHeight; // Start from the bottom of the bar
        //       ctx.fillText(emoji, emojiX, emojiY);
        //     }
        //   }
        // }
        // ... rest of your plugin code

        // Draw the emojis in a grid pattern
        for (let row = 0; row &amp;lt; numRows; row++) {
          for (let col = 0; col &amp;lt; numCols; col++) {
            if (row * numCols + col &amp;lt; dataValue) {
              // Ensure we don't draw more emojis than the data value
              const emojiX = x - width / 2 + col * emojiWidth;
              const emojiY = base - emojiHeight - row * emojiHeight; // Adjusted to start from the bottom
              // Save the context and rotate the canvas around the emoji's center
              ctx.save();
              ctx.translate(emojiX + emojiSize / 2, emojiY + emojiSize / 2);
              ctx.rotate(Math.PI); // Rotate 180 degrees
              ctx.fillText(emoji, -emojiSize / 2, -emojiSize / 2);
              ctx.restore(); // Restore the context to the original state for the next emoji
            }
          }
        }

        // ... rest of your plugin code
      });
    });
  },
};

// Register the necessary components for Chart.js
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
);
ChartJS.register(emojiPlugin); // Register the custom plugin

const CommitChart = () =&amp;gt; {
  const [commitData, setCommitData] = useState({
    labels: [],
    datasets: [],
  });

  const chartRef = useRef(null);

  useEffect(() =&amp;gt; {
    const fetchData = async () =&amp;gt; {
      try {
        const response = await fetch("/commitMessages.json");
        if (!response.ok) {
          throw new Error("Network response was not ok");
        }
        const commitMessages = await response.json();

        // Process your commit messages to get the data for the chart
        // Assuming you process the data to the format Chart.js expects
        // Example: { labels: ['Label1', 'Label2'], datasets: [{ data: [1, 2] }] }
        const processedData = processData(commitMessages);

        // If the chart already exists, update it
        if (chartRef.current) {
          chartRef.current.data = processedData;
          chartRef.current.update();
        } else {
          setCommitData(processedData); // Set data for initial chart rendering
        }
      } catch (error) {
        console.error("Failed to fetch commit messages:", error);
      }
    };

    fetchData();
  }, []); // Empty dependency array means this effect runs once after the first render

  const options = {
    scales: {
      x: {
        beginAtZero: true,
      },
      y: {
        // Additional options for the Y-axis can be set here
      },
    },
    maintainAspectRatio: true,
    responsive: true,
    plugins: {
      // ... other plugin configurations
      emojiPlugin: {}, // This is just to enable the plugin
    },
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;Commit Chart&amp;lt;/h2&amp;gt;
      &amp;lt;Bar ref={chartRef} data={commitData} options={options} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default CommitChart;

// Helper function to process the raw data
function processData(commitMessages) {
  const counts = {
    bugs: 0,
    features: 0,
    chores: 0,
    fixes: 0,
    // others: 0,
    docs: 0,
    refactor:0,
  };

  commitMessages.forEach((msg) =&amp;gt; {
    if (msg.includes("bug:") || msg.includes("🐛")) {
      counts.bugs++;
    } else if (msg.includes("feat:") || msg.includes("✨")) {
      counts.features++;
    } else if (msg.includes("chore:")) {
      counts.chores++;
    } else if (msg.includes("fix:")) {
      counts.fixes++;
    } else if (msg.includes("docs:")) {
      counts.docs++;
    } else if (msg.includes("refactor:")) {
      counts.refactor++;
    }
    //  else {
    // counts.others++; // Count all other commits as 'others'
    // }
  });

  return {
    labels: Object.keys(counts),
    datasets: [
      {
        label: "Number of Commits",
        data: Object.values(counts),
        backgroundColor: [
          "rgba(255, 99, 132, 0.2)", // color for bugs
          "rgba(54, 162, 235, 0.2)", // color for features
          "rgba(255, 206, 86, 0.2)", // color for chores
          "rgba(75, 192, 192, 0.2)", // color for fixes
          "rgba(153, 102, 255, 0.2)", // color for others
        ],
        borderColor: [
          "rgba(255, 99, 132, 1)",
          "rgba(54, 162, 235, 1)",
          "rgba(255, 206, 86, 1)",
          "rgba(75, 192, 192, 1)",
          "rgba(153, 102, 255, 1)",
        ],
        borderWidth: 1,
      },
    ],
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Finally install yalc globaly &lt;code&gt;npm i -g yalc&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With yalc you can run &lt;code&gt;yalc publish&lt;/code&gt; and it will create a local link that you can add to your project like running &lt;code&gt;npm i pakcage-name&lt;/code&gt; open up your React App, and for now I think it works best in a next JS app running React 17 will be fixing this for all versions. Naviagte there and run &lt;code&gt;yalc add&lt;/code&gt;your-package-name` and now you can import the component and add the script for extract-commits to your package.json&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The flow works like this&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;add script&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;"scripts": {&lt;br&gt;
    "extract-commits": "extract-commits"&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;run &lt;code&gt;npm run extract-commits&lt;/code&gt; should see a message that it a file was added to public folder&lt;/p&gt;

&lt;p&gt;Got to a page an import the component&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import {CommitGraph} from "your-package-name"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;use it&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;return (&lt;/p&gt;



&lt;p&gt;)&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it 😃&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, the use of this NPM package could help you know where your time is spent.&lt;/p&gt;

&lt;p&gt;the approach of integrating an emoji-based commit chart offers an engaging and visually intuitive way to track changes and progress. This feature not only adds an element of fun but also enhances the clarity and comprehensibility of project tracking.&lt;/p&gt;

&lt;p&gt;I would say if you have something you built internally publish it to NPM, a great way to advertise your other work.&lt;/p&gt;

&lt;p&gt;Kindly,&lt;/p&gt;

&lt;p&gt;Anders from Wispy Company&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
