<?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: with-heart</title>
    <description>The latest articles on DEV Community by with-heart (@with_heart).</description>
    <link>https://dev.to/with_heart</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%2F50928%2F59f61eb5-0b11-4075-914d-02ec8daea102.png</url>
      <title>DEV Community: with-heart</title>
      <link>https://dev.to/with_heart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/with_heart"/>
    <language>en</language>
    <item>
      <title>Set up our pnpm monorepo</title>
      <dc:creator>with-heart</dc:creator>
      <pubDate>Sun, 29 May 2022 19:47:59 +0000</pubDate>
      <link>https://dev.to/with_heart/set-up-our-pnpm-monorepo-5cjo</link>
      <guid>https://dev.to/with_heart/set-up-our-pnpm-monorepo-5cjo</guid>
      <description>&lt;p&gt;In this post, we'll implement the basic monorepo structure we mentioned at the end of &lt;a href="https://dev.to/with_heart/high-level-technical-overview-5hc0"&gt;High level technical overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a quick refresher, we'll have three packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll only generate a basic package structure for each. Here's what the end structure will look like (with some files/directories excluded to focus on the changes we'll be making):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── app
│  ├── package.json
│  ├── pages
│  ├── public
│  ├── README.md
│  └── tsconfig.json
├── bot
│  ├── package.json
│  ├── src
│  │  └── index.ts
│  └── tsconfig.json
├── db
│  ├── package.json
│  ├── src
│  │  └── index.ts
│  └── tsconfig.json
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md
└── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional monorepo setup like using the packages as dependencies of each other, build tooling, etc. will come later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create &lt;code&gt;pnpm&lt;/code&gt; workspace
&lt;/h2&gt;

&lt;p&gt;Since we're using &lt;code&gt;pnpm&lt;/code&gt;, monorepo setup doesn't require much setup on our part.&lt;/p&gt;

&lt;p&gt;Essentially, we just need to create a &lt;code&gt;pnpm-workspace.yaml&lt;/code&gt; file that defines the package directories in our monorepo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bot'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;db'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this change, &lt;code&gt;pnpm&lt;/code&gt; knows that the &lt;code&gt;app&lt;/code&gt;, &lt;code&gt;bot&lt;/code&gt;, and &lt;code&gt;db&lt;/code&gt; directories are packages in our monorepo.&lt;/p&gt;

&lt;p&gt;Now that we have this file in place, we can set up our package directories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the &lt;code&gt;app&lt;/code&gt; package
&lt;/h2&gt;

&lt;p&gt;Since &lt;code&gt;app&lt;/code&gt; is a Next.js app, we'll use &lt;code&gt;create-next-app&lt;/code&gt; to generate that package: &lt;code&gt;pnpm create next-app --ts app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The setup runs as expected and dependencies are added to the root &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;We can start our app to test that it works: &lt;code&gt;pnpm --filter app run dev&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;I just really want to note how cool &lt;code&gt;pnpm&lt;/code&gt; is. The &lt;code&gt;--filter&lt;/code&gt; flag allows us to zoom in on specific packages to execute our commands. In the above command, we use &lt;code&gt;--filter app&lt;/code&gt; to target &lt;code&gt;pnpm run dev&lt;/code&gt; to the &lt;code&gt;app&lt;/code&gt; directory.&lt;/p&gt;




&lt;p&gt;Before we move on, we need to fix an &lt;code&gt;eslint&lt;/code&gt; error in the &lt;code&gt;app&lt;/code&gt; directory: &lt;code&gt;Parsing error: Cannot find module 'next/babel'&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To resolve that, we need to update &lt;code&gt;app/.eslintrc.json&lt;/code&gt; to extend from &lt;code&gt;next/babel&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&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="s2"&gt;"next/babel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next/core-web-vitals"&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;h2&gt;
  
  
  Create the &lt;code&gt;bot&lt;/code&gt; and &lt;code&gt;db&lt;/code&gt; packages
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prepare &lt;code&gt;typescript&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Since &lt;code&gt;bot&lt;/code&gt; and &lt;code&gt;db&lt;/code&gt; will both be libs and not React apps, we can configure &lt;code&gt;typescript&lt;/code&gt; at the root and have both packages extend their configuration from that.&lt;/p&gt;

&lt;p&gt;To get started, we'll install a few packages: &lt;code&gt;pnpm i typescript @types/node -w @tsconfig/node16-recommended&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-w&lt;/code&gt; flag tells &lt;code&gt;pnpm&lt;/code&gt; that we want to install the dependencies at the root.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@tsconfig/node16-recommended&lt;/code&gt; package contains a &lt;code&gt;tsconfig.json&lt;/code&gt; file with recommended TypeScript configuration. We'll use that in our root &lt;code&gt;tsconfig.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@tsconfig/node16-strictest/tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&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="s2"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also exclude the &lt;code&gt;app&lt;/code&gt; directory because Next.js uses its own &lt;code&gt;tsconfig.json&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the &lt;code&gt;bot&lt;/code&gt; package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bot
├── package.json
├── src
│  └── index.ts
└── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll create the &lt;code&gt;bot&lt;/code&gt; folder and a &lt;code&gt;package.json&lt;/code&gt; inside of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bot"&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;"0.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also create &lt;code&gt;bot/tsconfig.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&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;"outDir"&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"&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;"include"&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="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration includes &lt;code&gt;src&lt;/code&gt; in the files TypeScript will compile and the &lt;code&gt;extends&lt;/code&gt; option means we reuse the configuration specified in our root &lt;code&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the &lt;code&gt;db&lt;/code&gt; package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;db
├── package.json
├── src
│  └── index.ts
└── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create this package, we'll do everything we did to create the &lt;code&gt;bot&lt;/code&gt; package except we replace the word &lt;code&gt;bot&lt;/code&gt; with &lt;code&gt;db&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;Awesome! We now have a monorepo! This gives us a lot of flexibility and clarity in how we configure and use our packages.&lt;/p&gt;

&lt;p&gt;To check out the PR for this post, look here: &lt;a href="https://github.com/Developer-DAO/discord-server-info/pull/10"&gt;https://github.com/Developer-DAO/discord-server-info/pull/10&lt;/a&gt;&lt;/p&gt;

</description>
      <category>monorepo</category>
      <category>pnpm</category>
    </item>
    <item>
      <title>Prepare our repo</title>
      <dc:creator>with-heart</dc:creator>
      <pubDate>Sun, 29 May 2022 16:52:58 +0000</pubDate>
      <link>https://dev.to/with_heart/prepare-our-repo-gkb</link>
      <guid>https://dev.to/with_heart/prepare-our-repo-gkb</guid>
      <description>&lt;p&gt;Before we start building our application, we need to spend a little time preparing our repo.&lt;/p&gt;

&lt;p&gt;In this post, we'll use &lt;a href="https://pnpm.io/"&gt;&lt;code&gt;pnpm&lt;/code&gt;&lt;/a&gt; to create our &lt;code&gt;package.json&lt;/code&gt; file, install &lt;a href="https://prettier.io/"&gt;&lt;code&gt;prettier&lt;/code&gt;&lt;/a&gt;, and configure &lt;a href="https://github.com/typicode/husky"&gt;&lt;code&gt;husky&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/okonet/lint-staged"&gt;&lt;code&gt;lint-staged&lt;/code&gt;&lt;/a&gt; to format any changed files with &lt;code&gt;prettier&lt;/code&gt; when we commit them. We'll also create a minimal &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create &lt;code&gt;package.json&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;To generate a &lt;code&gt;package.json&lt;/code&gt; file, I ran &lt;code&gt;pnpm init&lt;/code&gt; inside our project directory. This created a &lt;code&gt;package.json&lt;/code&gt; file with a few defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"discord-server-info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;""&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;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 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;"keywords"&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;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These defaults are weak and don't really fit our use case so I updated the file to contain much more information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"discord-server-info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MIT"&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;"Explore Discord server data and statistics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&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="s2"&gt;"discord"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"discord.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;"next.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;"prisma"&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;"repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/developer-dao/discord-server-info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"readme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/Developer-DAO/discord-server-info#readme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bugs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/Developer-DAO/discord-server-info/issues"&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;"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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's look at some of the changes I made:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;removed &lt;code&gt;version&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;since I already know that this &lt;code&gt;package.json&lt;/code&gt; will eventually serve as the root for our monorepo, it won't have a &lt;code&gt;version&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;added &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;keywords&lt;/code&gt;, &lt;code&gt;repository&lt;/code&gt;, &lt;code&gt;readme&lt;/code&gt;, &lt;code&gt;bugs&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;these provide additional context about our package&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;created empty &lt;code&gt;dependencies&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;since this is the root &lt;code&gt;package.json&lt;/code&gt;, we'll only use &lt;code&gt;dependencies&lt;/code&gt; (because the distinction between &lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;devDependencies&lt;/code&gt; doesn't exist at the root)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Create &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;This file will eventually contain many files and folders that we don't want to commit to our repo, but for now we'll just add &lt;code&gt;node_modules&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;node_modules/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Create &lt;code&gt;.prettierrc&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;This file contains the options for how &lt;code&gt;prettier&lt;/code&gt; formats our files. I mostly use the default option, but I prefer a few overrides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"proseWrap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"always"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"semi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"singleQuote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"trailingComma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"all"&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;h1&gt;
  
  
  Install &lt;code&gt;prettier&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;To install &lt;code&gt;prettier&lt;/code&gt;, I ran &lt;code&gt;pnpm i -D prettier&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This adds &lt;code&gt;prettier&lt;/code&gt; to our &lt;code&gt;devDependencies&lt;/code&gt;. As mentioned above, we won't be using &lt;code&gt;devDependencies&lt;/code&gt; but &lt;code&gt;prettier&lt;/code&gt; has to be in this list of the next step to work.&lt;/p&gt;

&lt;p&gt;We'll fix that later.&lt;/p&gt;

&lt;h1&gt;
  
  
  Configure &lt;code&gt;husky&lt;/code&gt; and &lt;code&gt;lint-staged&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;To install and configure &lt;code&gt;husky&lt;/code&gt; and &lt;code&gt;lint-staged&lt;/code&gt;, I ran &lt;code&gt;pnpm dlx mrm@2 lint-staged&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This utility detects a number of tools in our repo that &lt;code&gt;lint-staged&lt;/code&gt; supports and automatically installs + configures the dependencies we need for them.&lt;/p&gt;

&lt;p&gt;In this case, it: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;detected &lt;code&gt;prettier&lt;/code&gt; and installed &lt;code&gt;husky&lt;/code&gt; and &lt;code&gt;lint-staged&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;added a &lt;code&gt;prepare&lt;/code&gt; script which runs &lt;code&gt;husky install&lt;/code&gt; so that &lt;code&gt;husky&lt;/code&gt; will automatically be installed in each contributor's local environment&lt;/li&gt;
&lt;li&gt;installed &lt;code&gt;husky&lt;/code&gt; in my local environment (&lt;code&gt;.husky&lt;/code&gt; directory)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;added a &lt;code&gt;lint-staged&lt;/code&gt; configuration block to &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"lint-staged"&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;"*.{js,css,md}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier --write"&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;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since I know ahead of time the types of files we'll be using in this project, I updated the &lt;code&gt;lint-staged&lt;/code&gt; configuration to target them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"lint-staged"&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;"*.{js,ts,tsx,md,json,yml}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier --write"&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;h1&gt;
  
  
  Rename &lt;code&gt;devDependencies&lt;/code&gt; to &lt;code&gt;dependencies&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Our final step is to change &lt;code&gt;devDependencies&lt;/code&gt; to &lt;code&gt;dependencies&lt;/code&gt; in our &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"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;"husky"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8.0.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;"lint-staged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^12.4.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;"prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.6.2"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to run &lt;code&gt;pnpm i&lt;/code&gt; for this update to take effect.&lt;/p&gt;




&lt;p&gt;With all of these changes made, we now have the tooling in place to automatically format the files in our codebase with &lt;code&gt;prettier&lt;/code&gt; anytime a contributor commits them.&lt;/p&gt;

&lt;p&gt;You can view the PR for these changes here: &lt;a href="https://github.com/Developer-DAO/discord-server-info/pull/9"&gt;https://github.com/Developer-DAO/discord-server-info/pull/9&lt;/a&gt;&lt;/p&gt;

</description>
      <category>prettier</category>
    </item>
    <item>
      <title>Start with README.md</title>
      <dc:creator>with-heart</dc:creator>
      <pubDate>Sun, 29 May 2022 16:20:12 +0000</pubDate>
      <link>https://dev.to/with_heart/start-with-readmemd-2jgc</link>
      <guid>https://dev.to/with_heart/start-with-readmemd-2jgc</guid>
      <description>&lt;p&gt;I firmly believe in &lt;a href="https://tom.preston-werner.com/2010/08/23/readme-driven-development.html"&gt;Readme Driven Development&lt;/a&gt;. This paradigm is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write your Readme first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By writing our Readme first, we force ourselves to think through at a high level what it is we'll be building. This serves as an excellent piece of documentation for ourselves and others to remember what the project is, what it's meant to accomplish. and where it's heading.&lt;/p&gt;

&lt;p&gt;In keeping with this, writing a high level &lt;code&gt;README.md&lt;/code&gt; is &lt;a href="https://github.com/Developer-DAO/discord-server-info/commit/df409dbeaecc59ec7341304b1d35c67670617549"&gt;how I started &lt;code&gt;discord-server-info&lt;/code&gt;&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Let's look at how I structured our &lt;code&gt;README.md&lt;/code&gt;:&lt;/p&gt;

&lt;h1&gt;
  
  
  Heading
&lt;/h1&gt;

&lt;p&gt;Here are the first three lines of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# `discord-server-info`&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Explore Discord server data and statistics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This follows a standard pattern that I tend to follow for any &lt;code&gt;README.md&lt;/code&gt; that I create: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first line is an &lt;code&gt;h1&lt;/code&gt; of the name of the project 

&lt;ul&gt;
&lt;li&gt;since the name of this project matches the name of the repo, I wrapped it in inline code blocks&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;the next line is a blockquote with a one-line description of the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like this pattern because it immediately establishes two key pieces of information for a reader:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to refer to this project&lt;/li&gt;
&lt;li&gt;whether this project is relevant to them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Readers shouldn't have to read too far into our file to know either of those things.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;The next section after the heading is &lt;code&gt;Introduction&lt;/code&gt; which gives a high level overview of the &lt;code&gt;discord-server-info&lt;/code&gt; problem space. &lt;/p&gt;

&lt;p&gt;This is essentially a condensed version of the &lt;a href="https://dev.to/with_heart/introduction-1h0k"&gt;Introduction&lt;/a&gt; post from this series.&lt;/p&gt;

&lt;p&gt;It allows potential contributors to understand what our project is and why we think it's important.&lt;/p&gt;

&lt;h1&gt;
  
  
  Technical Overview
&lt;/h1&gt;

&lt;p&gt;The last section is &lt;code&gt;Technical Overview&lt;/code&gt;. Where the introduction gives the &lt;em&gt;what&lt;/em&gt; and the &lt;em&gt;why&lt;/em&gt;, the technical overview gives the &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is almost identical to the &lt;a href="https://dev.to/with_heart/high-level-technical-overview-5hc0"&gt;High level technical overview&lt;/a&gt; post from this series.&lt;/p&gt;

&lt;p&gt;Since I already have an idea in my head of how we can build this project to accomplish our goals, I've laid out what that looks like at a high level in this section.&lt;/p&gt;

&lt;p&gt;This will allow potential contributors to understand the direction we're taking and the general structure of the project.&lt;/p&gt;




&lt;p&gt;There's a lot more information I could have included in our Readme but didn't, like specific details for how each package should be built or some of the technical pitfalls we'll potentially face.&lt;/p&gt;

&lt;p&gt;Instead, we'll treat our Readme like everything else in the repo and &lt;em&gt;iterate on it&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;As we reach the point where we're ready to zoom in on one of the packages and implement it, I'll add a high level overview of what that looks like to our Readme.&lt;/p&gt;

&lt;p&gt;This allows us to gain the benefits of Readme Driven Development without getting too far ahead of ourselves.&lt;/p&gt;

</description>
      <category>discord</category>
      <category>nextjs</category>
      <category>prisma</category>
    </item>
    <item>
      <title>High level technical overview</title>
      <dc:creator>with-heart</dc:creator>
      <pubDate>Sun, 29 May 2022 16:14:13 +0000</pubDate>
      <link>https://dev.to/with_heart/high-level-technical-overview-5hc0</link>
      <guid>https://dev.to/with_heart/high-level-technical-overview-5hc0</guid>
      <description>&lt;p&gt;To build &lt;code&gt;discord-server-info&lt;/code&gt;, we'll need a few separate pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fi_zudGV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9davjf2uj8bz0fcpnkiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fi_zudGV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9davjf2uj8bz0fcpnkiw.png" alt="A diagram showing the bot, db, and app pieces and how they relate to each other" width="880" height="145"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;code&gt;bot&lt;/code&gt; will be a Discord bot. This bot reads data from the servers it's connected to and stores it in our database (&lt;code&gt;db&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;db&lt;/code&gt; is a Prisma database that provides an API for storing and accessing server data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app&lt;/code&gt; is a Next.js application that allows users to authenticate with Discord and explore data from their servers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're wondering why we need this structure rather than having the app read data directly from the Discord API, it's because the Discord API intentionally provides access to very little data. A bot account is required for full access.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;discord-server-info&lt;/code&gt; repo will eventually be a monorepo with a package for each piece.&lt;/p&gt;

</description>
      <category>discord</category>
      <category>nextjs</category>
      <category>prisma</category>
    </item>
    <item>
      <title>Introduction</title>
      <dc:creator>with-heart</dc:creator>
      <pubDate>Sun, 29 May 2022 15:52:41 +0000</pubDate>
      <link>https://dev.to/with_heart/introduction-1h0k</link>
      <guid>https://dev.to/with_heart/introduction-1h0k</guid>
      <description>&lt;p&gt;Hi!&lt;/p&gt;

&lt;p&gt;Welcome to my series on building a &lt;code&gt;discord.js&lt;/code&gt; bot with a connected Next.js dashboard.&lt;/p&gt;

&lt;p&gt;In this series, we'll be building &lt;a href="https://github.com/Developer-DAO/discord-server-info"&gt;&lt;code&gt;discord-server-info&lt;/code&gt;&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;The idea for &lt;code&gt;discord-server-info&lt;/code&gt; came from my friend &lt;a href="https://twitter.com/michelleBakels"&gt;Michelle Bakels&lt;/a&gt;. Michelle is attempting to do a major analysis of the &lt;a href="https://developerdao.com/"&gt;Developer DAO&lt;/a&gt; Discord server so she can find ways for us to reduce the size and scope of our channels, roles, etc.&lt;/p&gt;

&lt;p&gt;Unfortunately, the information provided by Discord &lt;a href="https://support.discord.com/hc/en-us/articles/360032807371-Server-Insights-FAQ"&gt;Server Insights&lt;/a&gt; isn't detailed enough for her to reason about and make informed decisions about our server.&lt;/p&gt;

&lt;p&gt;For example, one of the changes she'd like to make to improve access to our existing channels is to require each channel to fill out its description field informing other members of its purpose. How can she create a list of all of our channels currently missing a description?&lt;/p&gt;

&lt;p&gt;By hand. glhf Michelle!&lt;/p&gt;

&lt;p&gt;This is the type of problem that &lt;code&gt;discord-server-info&lt;/code&gt; aims to solve. By enabling server managers to explore more granular server data, &lt;code&gt;discord-server-info&lt;/code&gt; can help them analyze, reason about, and better manage their server.&lt;/p&gt;

</description>
      <category>discord</category>
      <category>nextjs</category>
      <category>prisma</category>
    </item>
    <item>
      <title>How to scale Excalidraw diagrams for Twitter posts</title>
      <dc:creator>with-heart</dc:creator>
      <pubDate>Thu, 19 May 2022 04:42:07 +0000</pubDate>
      <link>https://dev.to/with_heart/how-to-scale-excalidraw-diagrams-for-twitter-posts-2nba</link>
      <guid>https://dev.to/with_heart/how-to-scale-excalidraw-diagrams-for-twitter-posts-2nba</guid>
      <description>&lt;p&gt;I've been using Excalidraw to create diagrams that I use in Twitter threads. Unfortunately it's difficult to manually scale diagrams to the correct aspect ratio in Excalidraw.&lt;/p&gt;

&lt;p&gt;I finally figured out a solution to this problem, at least for Twitter!&lt;/p&gt;

&lt;p&gt;Here's how I did it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://i.ibb.co/17nzvLB/excalidraw-frame.png"&gt;Save this frame image&lt;/a&gt; and then import it into Excalidraw (I usually drag the image file onto the canvas)&lt;/li&gt;
&lt;li&gt;For each diagram you want to use on Twitter, duplicate the frame and scale your content to fit within its border&lt;/li&gt;
&lt;li&gt;When you're ready to export, first set the opacity of the artboard to &lt;code&gt;0%&lt;/code&gt; and then select both the hidden artboard &lt;strong&gt;and&lt;/strong&gt; the content&lt;/li&gt;
&lt;li&gt;Add your new image to your Twitter post!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This works because the size of the frame matches the maximum size of Twitter post images, so as long as your content fits in the frame, it will be sized to fit correctly in a Twitter post.&lt;/p&gt;




&lt;p&gt;If you followed these steps correctly, here's what the export pane looks like in Excalidraw:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BvFc1Wq3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebhxgq5a66j8zrhjvid0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BvFc1Wq3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebhxgq5a66j8zrhjvid0.png" alt="Export pane of Excalidraw showing a correctly sized image" width="880" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you followed the steps incorrectly, the export pane of Excalidraw will look like one of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;incorrectly scaled: be sure to select the artboard &lt;strong&gt;and&lt;/strong&gt; the content before exporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Td6k4q6B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu4xwrk2xpzhehgdec86.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Td6k4q6B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu4xwrk2xpzhehgdec86.png" alt="Excalidraw export that is incorrectly scaled because the artboard wasn't selected" width="470" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;black border is in the image: be sure to set the artboard's opacity to &lt;code&gt;0%&lt;/code&gt; before selecting it and the contents for export&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tfj02GEs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n285inblim3h69z5dect.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tfj02GEs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n285inblim3h69z5dect.png" alt="Excalidraw export with the border in the image" width="880" height="441"&gt;&lt;/a&gt; &lt;/p&gt;




&lt;p&gt;You can see the difference in scaling between the first post (correct; using this method) and the second post (incorrect; exporting the content directly):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r_5VSi5Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fa7ct4hhd204v9wncrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r_5VSi5Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fa7ct4hhd204v9wncrb.png" alt="Excalidraw image that is correctly sized to fit within a Twitter post" width="593" height="669"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nI0xvT6w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wkytvrq8fqtkwoxu37u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nI0xvT6w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wkytvrq8fqtkwoxu37u.png" alt="Excalidraw image that is not correctly sized to fit within a Twitter post so the sides of the image are cut off" width="596" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>excalidraw</category>
      <category>twitter</category>
    </item>
  </channel>
</rss>
