<?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: Paula Santamaría</title>
    <description>The latest articles on DEV Community by Paula Santamaría (@paulasantamaria).</description>
    <link>https://dev.to/paulasantamaria</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%2F61042%2F0a24f53f-aae9-4516-ac05-8f1cde9566db.jpg</url>
      <title>DEV Community: Paula Santamaría</title>
      <link>https://dev.to/paulasantamaria</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulasantamaria"/>
    <language>en</language>
    <item>
      <title>Debugging TypeScript projects with VSCode in 2023</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Sun, 16 Apr 2023 17:19:34 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/debugging-typescript-projects-with-vscode-in-2023-1fcn</link>
      <guid>https://dev.to/paulasantamaria/debugging-typescript-projects-with-vscode-in-2023-1fcn</guid>
      <description>&lt;p&gt;I finally decided to invest some time in properly setting up a debugger for my Node + TS projects. &lt;code&gt;console.log&lt;/code&gt; is cool and all, but it just wasn't cutting it anymore.&lt;/p&gt;

&lt;p&gt;So, I've been reading documentation (plus so many GitHub issues), and trying different solutions, and I ended up with a config that I'm actually happy with!&lt;/p&gt;

&lt;p&gt;Keep reading to learn how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a project with Node.js and TypeScript.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install a package (&lt;code&gt;ts-node-dev&lt;/code&gt;) and configure it so it can run your project and auto-restart on save.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure the debugger in VSCode so it uses &lt;code&gt;ts-node-dev&lt;/code&gt; to both enable the debugging of &lt;code&gt;.ts&lt;/code&gt; files and provide auto-restarts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, stop relying on &lt;code&gt;console.log('here!')&lt;/code&gt; to debug your code :D&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Initialize the project
&lt;/h2&gt;

&lt;p&gt;Create a directory for your project and initialize it:&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;mkdir &lt;/span&gt;ts-starter
&lt;span class="nb"&gt;cd &lt;/span&gt;ts-starter
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install TypeScript
&lt;/h2&gt;

&lt;p&gt;Install TypeScript and the type definitions for node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;typescript @types/node &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup your tsconfig
&lt;/h2&gt;

&lt;p&gt;To create the &lt;code&gt;tsconfig.json&lt;/code&gt; file, run the following command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The default &lt;code&gt;tsconfig.json&lt;/code&gt; generated should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES2016"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&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;"forceConsistentCasingInFileNames"&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;"strict"&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;"skipLibCheck"&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;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;
  
  
  Install ts-node-dev
&lt;/h2&gt;

&lt;p&gt;Were going to hook up &lt;code&gt;ts-node-dev&lt;/code&gt; with the VSCode debugger to get both the debugger and auto-restarts working!&lt;/p&gt;

&lt;p&gt;Start by installing the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;ts-node-dev &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To quickly try it out, we can create a new script in the &lt;code&gt;package.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="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;"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;"ts-node-dev --respawn src/index.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets write some code to run!&lt;/p&gt;

&lt;p&gt;Create the &lt;code&gt;src&lt;/code&gt; directory and add the following code:&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;// index.ts&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Game&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Super Mario&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Platformer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nintendo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Were adding some types so we can make sure the compiler is working properly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, run the &lt;code&gt;dev&lt;/code&gt; script from your terminal:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9KwMxiw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610527820/e3bea4f7-11a9-4cf6-8026-0a84adb89f3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9KwMxiw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610527820/e3bea4f7-11a9-4cf6-8026-0a84adb89f3a.png" alt="" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, change the values in your &lt;code&gt;game&lt;/code&gt; constant and save the file, to see the automatic restarts in action!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xYQbbnLA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610572334/bc2163d0-fe2b-4ba6-88c5-bde31300d645.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xYQbbnLA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610572334/bc2163d0-fe2b-4ba6-88c5-bde31300d645.gif" alt="" width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice, we can see how the app auto-restarts after we make changes!&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the VSCode Debugger
&lt;/h2&gt;

&lt;p&gt;First, lets generate a &lt;code&gt;launch.json&lt;/code&gt; file. To do that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open the command palette (&lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;P&lt;/code&gt; or &lt;code&gt;Cmd&lt;/code&gt; + &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;P&lt;/code&gt; on Mac) and select Debug: Add Configuration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the next step, select &lt;code&gt;Node.js&lt;/code&gt; for the Debugger&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should get an auto-generated &lt;code&gt;launch.json&lt;/code&gt; file, like this one:&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;"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.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;"configurations"&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;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;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&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;"Launch Program"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"skipFiles"&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;"&amp;lt;node_internals&amp;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;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceFolder}&lt;/span&gt;&lt;span class="se"&gt;\\\\&lt;/span&gt;&lt;span class="s2"&gt;src&lt;/span&gt;&lt;span class="se"&gt;\\\\&lt;/span&gt;&lt;span class="s2"&gt;index.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"outFiles"&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;"${workspaceFolder}/**/*.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;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;Replace it with the following configuration:&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;"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.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;"configurations"&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;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;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&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;"Launch + auto-restart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"runtimeExecutable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceRoot}/node_modules/.bin/ts-node-dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"args"&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;"--respawn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"${workspaceRoot}/src/index.ts"&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;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main properties to notice here are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;runtimeExecutable&lt;/code&gt;: By default, this would be &lt;code&gt;node&lt;/code&gt;, but we are replacing it with &lt;code&gt;ts-node-dev&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;args&lt;/code&gt;: Were adding the &lt;code&gt;--respawn&lt;/code&gt; flag, which tells &lt;code&gt;ts-node-dev&lt;/code&gt; to restart the process when files change, and then we add the path to our startup file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, these two properties produce a result similar to the &lt;code&gt;ts-node-dev --respawn src/index.ts&lt;/code&gt; script we set up earlier in the &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now, lets try it!
&lt;/h2&gt;

&lt;p&gt;Press &lt;code&gt;F5&lt;/code&gt; to start the debugger. You should see the output on VSCode's integrated terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ibJf1WCY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610612870/524ebbb0-6397-472a-bbcc-8c9cee69f112.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ibJf1WCY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610612870/524ebbb0-6397-472a-bbcc-8c9cee69f112.png" alt="" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try adding a breakpoint, and then make some changes and save:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BYqX3x5q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610661284/d92d0133-4214-48a8-bc59-b830e623f084.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BYqX3x5q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1681610661284/d92d0133-4214-48a8-bc59-b830e623f084.png" alt="" width="800" height="624"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app should restart and hit the breakpoint!&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it!
&lt;/h2&gt;

&lt;p&gt;What I love about this config is how simple it is. All we needed was &lt;code&gt;ts-node-dev&lt;/code&gt;, a few tweaks to the debugger config file, and it just works!&lt;/p&gt;

&lt;p&gt;Although, to be completely honest, I'll probably still use &lt;code&gt;console.log('here')&lt;/code&gt; every now and then, just for old times' sake.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.typescriptgamified.com"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wo-Shj7U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.typescriptgamified.com/_app/immutable/assets/banner-01-16795b9b.webp" alt="" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My TypeScript book: &lt;a href="https://typescriptgamified.com"&gt;typescriptgamified.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>typescript</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>A Twitch chatbot powered by Notion</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Tue, 15 Feb 2022 12:00:47 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/a-twitch-chatbot-powered-by-notion-2b6m</link>
      <guid>https://dev.to/paulasantamaria/a-twitch-chatbot-powered-by-notion-2b6m</guid>
      <description>&lt;p&gt;AchoBot is an open-source Twitch chatbot I built for myself. It allows you to manage your chatbot's commands using Notion:&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%2Fuser-images.githubusercontent.com%2F2507959%2F152531729-8b117829-9965-41a6-9455-b10dbf4cda40.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%2Fuser-images.githubusercontent.com%2F2507959%2F152531729-8b117829-9965-41a6-9455-b10dbf4cda40.png" alt="notion-database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It relies on the power of Notion Databases to manage command responses, permissions, and documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The repo&lt;/li&gt;
&lt;li&gt;Features&lt;/li&gt;
&lt;li&gt;Development&lt;/li&gt;
&lt;li&gt;Try it&lt;/li&gt;
&lt;li&gt;Stream&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The repo
&lt;/h2&gt;

&lt;p&gt;Check the repo here 👇&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pawap90" rel="noopener noreferrer"&gt;
        pawap90
      &lt;/a&gt; / &lt;a href="https://github.com/pawap90/acho-bot" rel="noopener noreferrer"&gt;
        acho-bot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🐶💬 A Twitch chatbot powered by Notion 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a href="https://github.com/pawap90/acho-bot/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/pawap90/acho-bot/actions/workflows/build.yml/badge.svg" alt="build"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2507959/152519862-b1d7116e-dade-4ed8-9ac5-f9eefeeff520.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2507959%2F152519862-b1d7116e-dade-4ed8-9ac5-f9eefeeff520.png" alt="achobot-gh-banner"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Use Notion to configure your Twitch chatbot commands!&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/2507959/152531729-8b117829-9965-41a6-9455-b10dbf4cda40.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F2507959%2F152531729-8b117829-9965-41a6-9455-b10dbf4cda40.png" alt="notion-database"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/pawap90/acho-bot#features" rel="noopener noreferrer"&gt;Features&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#manage-your-twitch-chatbot-from-notion" rel="noopener noreferrer"&gt;Manage your Twitch Chatbot from Notion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pawap90/acho-bot#assign-permissions-to-each-command" rel="noopener noreferrer"&gt;Assign Permissions to each Command&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#specific-user-permissions" rel="noopener noreferrer"&gt;Specific user permissions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#commands-view" rel="noopener noreferrer"&gt;Commands view&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#scripted-commands" rel="noopener noreferrer"&gt;Scripted commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#single-account" rel="noopener noreferrer"&gt;Single account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#status-view" rel="noopener noreferrer"&gt;Status view&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#built-in-commands" rel="noopener noreferrer"&gt;Built-in commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#placeholder-commands" rel="noopener noreferrer"&gt;Placeholder commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pawap90/acho-bot#quick-start" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#dependencies" rel="noopener noreferrer"&gt;Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pawap90/acho-bot#development" rel="noopener noreferrer"&gt;Development&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#test" rel="noopener noreferrer"&gt;Test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#extend-it" rel="noopener noreferrer"&gt;Extend it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/acho-bot#environment-variables" rel="noopener noreferrer"&gt;Environment variables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Features&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Manage your Twitch Chatbot from Notion&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Add, update or delete commands in a Notion database. AchoBot will use the information from Notion to answer your viewers when they invoke a command.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Assign Permissions to each Command&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The database includes a &lt;code&gt;Permissions&lt;/code&gt; column where you can specify who can invoke each command. Leave it empty, and everyone will have access to it.
The column accepts one or more of the following values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Broadcaster&lt;/li&gt;
&lt;li&gt;Moderator&lt;/li&gt;
&lt;li&gt;Subscriber&lt;/li&gt;
&lt;li&gt;Viewer&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Specific user permissions&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;You can also create commands that can only be invoked by a single specific user. To do that use the value &lt;code&gt;usr:&amp;lt;username&amp;gt;&lt;/code&gt;, for example: &lt;code&gt;usr:paulasantamaria&lt;/code&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Commands&lt;/h2&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pawap90/acho-bot" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;Here's the complete list of features:&lt;/p&gt;

&lt;h3&gt;
  
  
  Manage your Twitch Chatbot from Notion
&lt;/h3&gt;

&lt;p&gt;Add, update or delete commands in a Notion database. AchoBot will use the information from Notion to answer your viewers when they invoke a command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assign Permissions to each Command
&lt;/h3&gt;

&lt;p&gt;The database includes a &lt;code&gt;Permissions&lt;/code&gt; column where you can specify who can invoke each command. Leave it empty, and everyone will have access to it. &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%2Fl0mw0ssl8od0tcv5ne97.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%2Fl0mw0ssl8od0tcv5ne97.png" alt="Permissions column"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Specific user permissions
&lt;/h4&gt;

&lt;p&gt;You can also create commands that can only be invoked by a single specific user. To do that use the value &lt;code&gt;usr:&amp;lt;username&amp;gt;&lt;/code&gt;, for example: &lt;code&gt;usr:paulasantamaria&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Commands view
&lt;/h3&gt;

&lt;p&gt;Navigate to &lt;code&gt;/commands&lt;/code&gt; to get a list of every public command. Your viewers can use it to see the available commands.&lt;br&gt;
Also, you can navigate to &lt;code&gt;/commands?mode=image&lt;/code&gt; to get a ready-to-download image you can add to your Twitch panels.&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%2Fy2pj9tqs8xlrfl5cu4ty.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%2Fy2pj9tqs8xlrfl5cu4ty.png" alt="A table showing every public commands on localhost:5000/commands"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Scripted commands
&lt;/h3&gt;

&lt;p&gt;You can specify the type of command in the "Type" column. Available options are:&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%2Fpu8lnfos1grku1bpdovr.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%2Fpu8lnfos1grku1bpdovr.png" alt="Command types"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Text commands return a plain text response. E.g: "Welcome to the stream".&lt;/p&gt;

&lt;p&gt;Scripted commands can implement some logic (JavaScript)  and receive parameters.&lt;/p&gt;

&lt;p&gt;For example, imagine the following command &lt;code&gt;!say &amp;lt;text&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&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;textToPrint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`You said: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;textToPrint&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a viewer invokes it: &lt;code&gt;!say hello&lt;/code&gt;&lt;br&gt;
Output: &lt;code&gt;You said: hello&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This feature is new and still has some limitations and issues. Writing code in a Notion DB cell is not ideal 😅.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Single account
&lt;/h3&gt;

&lt;p&gt;The chatbot service will work only for the accounts and channels specified in the environment variables. &lt;br&gt;
If any other account tries to log in using the chatbot authorization endpoints, it will get an authorization error. &lt;/p&gt;

&lt;p&gt;The service is not currently designed to handle multiple accounts. This is a limitation, but it also makes the hosting and configuration of the service easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Status view
&lt;/h3&gt;

&lt;p&gt;Use the status view to see if your bot's Twitch account is correctly authorized. &lt;br&gt;
You can use this view in OBS to get real-time feedback on your chatbot status (it auto-updates every minute).&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in commands
&lt;/h3&gt;

&lt;p&gt;Built-in commands are defined in the codebase instead of Notion. They usually require more logic and access to resources unavailable to the Notion commands. There are currently two built-in commands that you can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;!help&lt;/code&gt; prints a list of available commands.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!refresh&lt;/code&gt; invalidates the command cache, so next time a command is triggered, the app needs to go to Notion to retrieve the commands, thus updating them to the latest version. Only available for Broadcaster and Moderators. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Placeholder commands
&lt;/h3&gt;

&lt;p&gt;Some commands are triggered via code but defined in Notion. This allows AchoBot to react to certain events (like a user logging in or subscribing) while also allowing each user to specify what they want AchoBot to do in each case.&lt;br&gt;
Right now, the only placeholder command is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;!welcome&lt;/code&gt;: Executed when AchoBot joins the chat room immediately after a stream starts. If you define this command in Notion when the bot logs in, it can say hi and leave some relevant information in the chat. E.g., "Hi, I'm AchoBot! Type !help to see what I can do". &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;AchoBot runs on Node.js. It was developed with TypeScript and uses Nodemon to run the development server. It relies on &lt;a href="https://tmijs.com/" rel="noopener noreferrer"&gt;tmi.js&lt;/a&gt; to connect and interact with Twitch's chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Once the service starts, AchoBot retrieves the commands from Notion (and other sources) and stores them in cache. When viewers execute a command, we search for it in the cache and execute it to generate a response:&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%2Fyxacjea2w0q16ab1jn8n.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%2Fyxacjea2w0q16ab1jn8n.png" alt="AchoBot flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Extensible
&lt;/h3&gt;

&lt;p&gt;The project is designed to be easily extensible. It uses the command pattern to define the list of commands. You can extend this list using different strategies depending on what you aim to achieve. To know more: &lt;a href="https://github.com/pawap90/acho-bot/wiki/Extend-AchoBot" rel="noopener noreferrer"&gt;Extend AchoBot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Here's a detailed step-by-step guide on how to run AchoBot: &lt;a href="https://github.com/pawap90/acho-bot/wiki/AchoBot-setup---Full-guide" rel="noopener noreferrer"&gt;AchoBot Setup - Full Guide&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Stream
&lt;/h2&gt;

&lt;p&gt;I developed this chatbot live on stream. Join me if you are interested in &lt;strong&gt;live coding sessions&lt;/strong&gt; where I experiment with &lt;strong&gt;different tech stacks&lt;/strong&gt; and build &lt;strong&gt;open-source&lt;/strong&gt; software!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/paulasantamaria" rel="noopener noreferrer"&gt;👉 twitch.tv/paulasantamaria&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right now, I'm working on a new portfolio with &lt;em&gt;Svelte-kit, markdown, and TailwindCSS&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>opensource</category>
      <category>twitch</category>
      <category>notion</category>
    </item>
    <item>
      <title>Using GitHub Actions to turn contributors into heroes of an open source game</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Thu, 09 Dec 2021 01:05:34 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/using-github-actions-to-turn-contributors-into-heroes-of-an-open-source-game-4cei</link>
      <guid>https://dev.to/paulasantamaria/using-github-actions-to-turn-contributors-into-heroes-of-an-open-source-game-4cei</guid>
      <description>&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;We already know open source contributors are the heroes of the tech industry. They make our lives easier with their libraries, frameworks, and more. I wanted to use this opportunity to make them into a more literal type of hero inside a game. &lt;/p&gt;

&lt;p&gt;So, here's how it works:&lt;/p&gt;

&lt;p&gt;My &lt;strong&gt;GitHub Actions&lt;/strong&gt; workflow &lt;em&gt;extracts information from the contributor's PR&lt;/em&gt; and uses it to dynamically generate a file that the game will use after being deployed. &lt;br&gt;
To obtain this information from the PR the workflow uses &lt;strong&gt;GitHub's API&lt;/strong&gt; through &lt;code&gt;octokit/request-action@v2.x&lt;/code&gt; and saves the relevant data using environment variables that are then used to generate a &lt;code&gt;Contributor.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Then the workflow builds the whole project, including the generated file, and deploys it to &lt;strong&gt;GitHub Pages&lt;/strong&gt;. So the latest deployment includes the latest PR and contributor information!&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%2Fi.imgur.com%2FAsrJRKQ.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%2Fi.imgur.com%2FAsrJRKQ.png" alt="Screenshot showing how the info is used in the game's main menu. The avatar and username are presented as " title=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, the workflow also generates a copy of each contribution and stores it in an "archive" folder that can be accessed from the deployed website, so contributors can access their contributions live even after another merged PR overrode them.&lt;/p&gt;

&lt;p&gt;So, if you contributed, you can always access the deployment from your contribution in &lt;code&gt;https://pawap90.github.io/space-commit/archive/{your-username}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The game is called "Space commit". It's a simple, side-scroller, open-source game I developed especially for this submission. For the development I used Phaser with TypeScript, ESLint, and Snowpack. I decided to make our heroes astronauts because I thought it captured the essence of open source contributors: They are explorers and science lovers; They get themselves in unexplored territories and enjoy learning.&lt;/p&gt;

&lt;p&gt;Here's a quick demo:&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%2Fi.imgur.com%2Fke2Ht2o.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fke2Ht2o.gif" alt="A gif showing the game's main menu with the info from the latest PR and then the gameplay, which is an astronaut floating over a planet and dodging spikey things"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Wacky Wildcards&lt;/p&gt;

&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CICD&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

      &lt;span class="c1"&gt;# Retrieve contribution data from GitHub API&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octokit/request-action@v2.x&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Retrieve contribution data&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get_contribution_data&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls&lt;/span&gt;
          &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pawap90&lt;/span&gt;
          &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;space-commit&lt;/span&gt;
          &lt;span class="na"&gt;commit_sha&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="c1"&gt;# Generate env variables to easily access the contribution data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Store contribution data in env vars&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt; 
          &lt;span class="s"&gt;echo "username=${{ fromJson(steps.get_contribution_data.outputs.data)[0].user.login }}" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
          &lt;span class="s"&gt;echo "avatar_url=${{ fromJson(steps.get_contribution_data.outputs.data)[0].user.avatar_url }}" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
          &lt;span class="s"&gt;echo "commit_sha=${{ github.sha }}" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
          &lt;span class="s"&gt;echo "message=${{ fromJson(steps.get_contribution_data.outputs.data)[0].title }}" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;


      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print collected data&lt;/span&gt; 
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt; 
          &lt;span class="s"&gt;echo "data collected &lt;/span&gt;
                &lt;span class="s"&gt;username ${{ env.username }}&lt;/span&gt;
                &lt;span class="s"&gt;avatar ${{ env.avatar_url }}&lt;/span&gt;
                &lt;span class="s"&gt;commit sha ${{ env.commit_sha }}&lt;/span&gt;
                &lt;span class="s"&gt;message ${{ env.message }}"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;14'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate contributor file&lt;/span&gt; 
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "export const Contributor = {&lt;/span&gt;
              &lt;span class="s"&gt;username: '${{ env.username }}',&lt;/span&gt;
              &lt;span class="s"&gt;avatar_url: '${{ env.avatar_url }}',&lt;/span&gt;
              &lt;span class="s"&gt;commit: '${{ env.commit_sha }}',&lt;/span&gt;
              &lt;span class="s"&gt;message: '${{ env.message }}'&lt;/span&gt;
          &lt;span class="s"&gt;};" &amp;gt; src/Contributor.ts&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build project 🔧&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt; 
          &lt;span class="s"&gt;npm install&lt;/span&gt;
          &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="c1"&gt;# Deploy latest contribution to GitHub pages&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy 🚀&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JamesIves/github-pages-deploy-action@4.1.5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gh-pages&lt;/span&gt;
          &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;_build&lt;/span&gt;
          &lt;span class="na"&gt;clean&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;clean-exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;archive&lt;/span&gt;

      &lt;span class="c1"&gt;# Store build permanently in archive folder so everyone can check previous contributions by username&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Send to archive 📁&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JamesIves/github-pages-deploy-action@4.1.5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gh-pages&lt;/span&gt; 
          &lt;span class="na"&gt;folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;_build&lt;/span&gt;
          &lt;span class="na"&gt;clean&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;clean-exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;archive&lt;/span&gt;
          &lt;span class="na"&gt;target-folder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;archive/${{ env.username }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;Here's the game repo where this workflow is used: &lt;a href="https://github.com/pawap90/space-commit" rel="noopener noreferrer"&gt;github.com/pawap90/space-commit&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And you can play the latest version here: &lt;a href="https://pawap90.github.io/space-commit" rel="noopener noreferrer"&gt;pawap90.github.io/space-commit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, I built most of this game &lt;strong&gt;live on Twitch&lt;/strong&gt;. You can check it our here: &lt;a href="https://twitch.tv/paulasantamaria" rel="noopener noreferrer"&gt;twitch.tv/paulasantamaria&lt;/a&gt;&lt;/p&gt;

</description>
      <category>actionshackathon21</category>
      <category>github</category>
      <category>opensource</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Create games using TypeScript, Snowpack, and ESLint with this template</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Sun, 16 May 2021 15:00:22 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/create-games-using-typescript-snowpack-and-eslint-with-this-template-12ef</link>
      <guid>https://dev.to/paulasantamaria/create-games-using-typescript-snowpack-and-eslint-with-this-template-12ef</guid>
      <description>&lt;p&gt;NOTE: ⚠️ The GitHub template presented in this post was archived. I created a new one that has exactly the same features (TS, ESLint, hot reload, GitHub Pages) but instead of Snowpack uses Vite, since Snowpack is now deprecated.&lt;/p&gt;

&lt;p&gt;I also took the opportunity to update all dependencies to the latest available version:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pawap90" rel="noopener noreferrer"&gt;
        pawap90
      &lt;/a&gt; / &lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint" rel="noopener noreferrer"&gt;
        phaser3-ts-vite-eslint
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A modern Phaser template with TypeScript, Vite, ESLint, and GitHub Pages
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint/actions/workflows/deploy.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/pawap90/phaser3-ts-vite-eslint/actions/workflows/deploy.yml/badge.svg" alt="Deploy"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/pawap90/phaser3-ts-vite-eslint/actions/workflows/build.yml/badge.svg" alt="Build"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A modern Phaser 3 template: Phaser 3 + TypeScript + Vite + ESLint + GitHub Pages&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint./package.json#L24" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/10149e28b867f0f3047b80fc2d36b315c9cfd8e8d44a906a1db342d168c5ef48/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5068617365722532302d25323076332e38302e312532302d2532302532333430343935313f6c6162656c436f6c6f723d253233393933383863267374796c653d666c61742d737175617265" alt="Phaser Version 3.80.1"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint./package.json#L21" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a5039a8ba324cfa9720ecffd58c687abbe77c883cdba5d100c8b7958b978519d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742532302d25323076352e322e322532302d2532302532333430343935313f6c6162656c436f6c6f723d253233333137386336267374796c653d666c61742d737175617265" alt="TypeScript Version 5.2.2"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint./package.json#L20" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3ef0781cb60267fb9a400e2204d1dddc18105b89ba8597aa70fabd1a2196ac71/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f566974652532302d25323076352e332e342532302d2532302532333430343935313f6c6162656c436f6c6f723d253233666663383230267374796c653d666c61742d737175617265" alt="Vite Version 5.3.4"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint./package.json#L19" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/73d72b55e60e03e42687d9a047062fc0fdb08826fa9d7ea3f30aabbbc9bef47b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f45534c696e742532302d25323076382e35372e302532302d2532302532333430343935313f6c6162656c436f6c6f723d253233343933306264267374796c653d666c61742d737175617265" alt="ESLint Version 8.57.0"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/2507959/353456126-3718c0b8-fad6-4dda-9ad1-54bfb496f128.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mjg0ODgwNjgsIm5iZiI6MTcyODQ4Nzc2OCwicGF0aCI6Ii8yNTA3OTU5LzM1MzQ1NjEyNi0zNzE4YzBiOC1mYWQ2LTRkZGEtOWFkMS01NGJmYjQ5NmYxMjgucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MTAwOSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDEwMDlUMTUyOTI4WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NjU5ZjY3YTc3ODg5YTg5NmFlNzU4YmIzZmNhODYzYjgxNDI4ODliYmVlOTEyODhlMzQ1MjU3ZGZkYWVhMjZhNiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.2i_ryDc2e7Uu65xucQvZ5cCvpE93recpBW1-ZTdf1Vw"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F2507959%2F353456126-3718c0b8-fad6-4dda-9ad1-54bfb496f128.png%3Fjwt%3DeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mjg0ODgwNjgsIm5iZiI6MTcyODQ4Nzc2OCwicGF0aCI6Ii8yNTA3OTU5LzM1MzQ1NjEyNi0zNzE4YzBiOC1mYWQ2LTRkZGEtOWFkMS01NGJmYjQ5NmYxMjgucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MTAwOSUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDEwMDlUMTUyOTI4WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NjU5ZjY3YTc3ODg5YTg5NmFlNzU4YmIzZmNhODYzYjgxNDI4ODliYmVlOTEyODhlMzQ1MjU3ZGZkYWVhMjZhNiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.2i_ryDc2e7Uu65xucQvZ5cCvpE93recpBW1-ZTdf1Vw" alt="A modern phaser 3 tempalte with typescript, vite, eslint, and GitHub pages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#dependencies" rel="noopener noreferrer"&gt;Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#quick-start" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#project-structure" rel="noopener noreferrer"&gt;Project structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#typescript" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#vite" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#npm-scripts" rel="noopener noreferrer"&gt;NPM Scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint#github-pages" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Dependencies&lt;/h1&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/" rel="nofollow noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Quick start&lt;/h1&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get this template: Press over the "Use this template" button. This will allow you to create a new repo with this project's structure on your Github account. Then you can clone it to your local machine.&lt;/p&gt;
&lt;p&gt;Alternatively, you can clone this repo to your machine using the following command.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/pawap90/phaser3-ts-vite-eslint.git&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Install dependencies: Run the following command from the project's root folder:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Start the local development server:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm run dev&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Go to your browser and navigate to &lt;a href="http://localhost:5173" rel="nofollow noopener noreferrer"&gt;http://localhost:5173&lt;/a&gt;. You should see this beauty:&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/589980a5fa48c5a9507f394b7aedda38073ddcb123dff6176a88dc6e6827da96/68747470733a2f2f692e696d6775722e636f6d2f625956637253722e676966"&gt;&lt;img src="https://camo.githubusercontent.com/589980a5fa48c5a9507f394b7aedda38073ddcb123dff6176a88dc6e6827da96/68747470733a2f2f692e696d6775722e636f6d2f625956637253722e676966" alt="Acho the pup bouncing around"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Project structure&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;├── /.github
│   └── /workflows
│           ├── build.yml
│           └── deploy.yml
├── /public
│       ├── acho.png
│       └── ground.png
├── /src
│   ├── /scenes
│&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pawap90/phaser3-ts-vite-eslint" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;p&gt;If you follow me on &lt;a href="https://twitter.com/pauxdsantamaria" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.instagram.com/pau.codes/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, you may know I've been trying out &lt;a href="https://phaser.io/" rel="noopener noreferrer"&gt;Phaser&lt;/a&gt; lately. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phaser is an open-source game framework&lt;/strong&gt; that allows you to develop games using HTML and Javascript. &lt;/p&gt;

&lt;p&gt;I've always been interested in Game development, but I hadn't found the time in the past to really get into it. I tried some things with Unity over the years, but I was focused on University and work, so those projects always got left behind at some point. Now that I graduated, I have more time to explore these kinds of things.&lt;/p&gt;

&lt;p&gt;So I started going through some tutorials and doing some experiments with the framework. To get started, all you need is a local web server and getting Phaser through NPM or a CDN, for example. Browsing online, I found some templates that used No.jsde as a web server and installed Phaser through NPM, so I decided to make my own template using a modern tech stack.&lt;/p&gt;

&lt;p&gt;And here it is:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pawap90" rel="noopener noreferrer"&gt;
        pawap90
      &lt;/a&gt; / &lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint" rel="noopener noreferrer"&gt;
        phaser3-ts-snowpack-eslint
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A modern Phaser 3 template with TypeScript, Snowpack &amp;amp; ESLint
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-alert markdown-alert-warning"&gt;
&lt;p class="markdown-alert-title"&gt;Warning&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This repository has been archived.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you're looking for a Phaser Template, please use this one: &lt;a href="https://github.com/pawap90/phaser3-ts-vite-eslint" rel="noopener noreferrer"&gt;github.com/pawap90/phaser3-ts-vite-eslint&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint/actions/workflows/build.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/pawap90/phaser3-ts-snowpack-eslint/actions/workflows/build.yml/badge.svg" alt="Build"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint/actions/workflows/deploy.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/pawap90/phaser3-ts-snowpack-eslint/actions/workflows/deploy.yml/badge.svg" alt="GitHub Pages Deploy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint./package.json#L24" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/00644673720796cdf2a107a14b7ec6a0505baa1f681a054465b916066430c96e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5068617365722532302d25323076332e36302e302532302d2532302532333430343935313f6c6162656c436f6c6f723d253233393933383863267374796c653d666c61742d737175617265" alt="Phaser Version 3.60.0"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint./package.json#L21" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/29364f4fbba7203f0960ec29a4bc743b2bda8cc298700f480243a9281c4b320c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742532302d25323076352e312e362532302d2532302532333430343935313f6c6162656c436f6c6f723d253233333137386336267374796c653d666c61742d737175617265" alt="TypeScript Version 5.1.6"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint./package.json#L20" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/cfd2688219c6ca4f27bb7c576d305c52c247524323d0da68d76e33204ff15ad9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f536e6f777061636b2532302d25323076332e382e382532302d2532302532333430343935313f6c6162656c436f6c6f723d253233326535653832267374796c653d666c61742d737175617265" alt="Snowpack Version 3.8.8"&gt;&lt;/a&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint./package.json#L19" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0388ae7ebc64de68b0304e2a9ea00752d3e5afd16816af893f7284d44f6ba4e4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f45534c696e742532302d25323076382e34362e302532302d2532302532333430343935313f6c6162656c436f6c6f723d253233343933306264267374796c653d666c61742d737175617265" alt="ESLint Version 8.46.0"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/86858e93878ffc5513cc2cf656f472ae50cbc35c77a49a87087403afd083ffb4/68747470733a2f2f692e696d6775722e636f6d2f5271334a7251582e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/86858e93878ffc5513cc2cf656f472ae50cbc35c77a49a87087403afd083ffb4/68747470733a2f2f692e696d6775722e636f6d2f5271334a7251582e706e67" alt="A modern Phaser 3 template: Phaser 3 + TypeScript + Snowpack + ESLint"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A modern Phaser 3 template: Develop your game using Typescript, keep your codebase clean with ESLint, and enjoy lightning-fast live updates thanks to Snowpack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#dependencies" rel="noopener noreferrer"&gt;Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#quick-start" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#project-structure" rel="noopener noreferrer"&gt;Project structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#typescript" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#snowpack" rel="noopener noreferrer"&gt;Snowpack&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#build-optimization" rel="noopener noreferrer"&gt;Build optimization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#npm-scripts" rel="noopener noreferrer"&gt;NPM Scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#github-pages" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pawap90/phaser3-ts-snowpack-eslint#happy-coding" rel="noopener noreferrer"&gt;Happy coding!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Dependencies&lt;/h1&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/" rel="nofollow noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Quick start&lt;/h1&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get this template: Press over the "Use this template" button. This will allow you to create a new repo with this project's structure on your Github account. Then you can clone it to your local machine.&lt;/p&gt;
&lt;p&gt;Alternatively, you can clone this repo to your machine using the following command.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/pawap90/phaser3-ts-snowpack-eslint.git&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Install dependencies: Run the following command from the project's root folder:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install&lt;/pre&gt;

&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Start the local development server:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm start&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Go to your browser and navigate to &lt;a href="http://localhost:8000" rel="nofollow noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;. You should see…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pawap90/phaser3-ts-snowpack-eslint" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  About the template
&lt;/h1&gt;

&lt;p&gt;This template allows you to develop your game using &lt;strong&gt;TypeScript&lt;/strong&gt;, keep your codebase clean with &lt;strong&gt;ESLint&lt;/strong&gt;, and enjoy lightning-fast live updates thanks to &lt;strong&gt;Snowpack&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I decided to use TypeScirpt instead of JavaScript after doing some tutorials. I tried both options, and I just felt more comfortable with TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build it with Snowpack
&lt;/h2&gt;

&lt;p&gt;When the time came to choose a bundler, I decided to give Snowpack a try since I've heard many good things about it. And honestly, I was blown away by how &lt;strong&gt;fast&lt;/strong&gt; it is. The development server starts in 14ms on my machine, and the live updates are fantastic. I can play around with Phaser and see every update on the browser almost instantly.&lt;/p&gt;

&lt;p&gt;I also used &lt;strong&gt;Snowpack's built-in optimizations&lt;/strong&gt; to minify and bundle the build and make it lighter for production. This Snowpack feature is not yet production-ready (according to the docs), but I've done a few tests, published some sample games, and I haven't had any issues with it so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimalistic
&lt;/h2&gt;

&lt;p&gt;The template is very minimalistic, meaning that it only has what's needed to run and build the project, plus a Sample scene to check everything is working correctly. &lt;/p&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├───public/                         Public static files
│   ├───assets/                     Sample assets
│   │   ├───banner.png
│   │   ├───acho.png
│   │   └───ground.png
│   └───index.html                  HTML file where our game will be loaded
├───src/                            Game logic goes here
│   ├───scenes/                     Game scenes
│   │   ├───InitialScene.ts         Initial sample scene
│   │   └───PreloaderScene.ts       Scene preloader
│   └───Main.ts                     Phaser game configuration
├───.eslintignore                   Files that should be ignored by ESLint  
├───.eslintrc.js                    ESLint configuration file
├───.gitignore                      Files that should not be pushed to the repo
├───package.json                    Project scripts, dependencies and metadata
├───snowpack.config.js              Snowpack configuration file
└───tsconfig.json                   Typescript configuration file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can remove everything in the &lt;code&gt;public/assets&lt;/code&gt; folder. But I recommend you first run the project once and make sure everything is installed and running properly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Clean and safe
&lt;/h2&gt;

&lt;p&gt;It's focused on keeping the codebase clean and safe, thanks to TypeScript and ESLint.&lt;/p&gt;

&lt;p&gt;TypeScript's configuration contains some flags that set some checks like "strict" and "noImplicitAny" to reduce errors. You can always change that in the &lt;code&gt;tsconfig.json&lt;/code&gt; file if you prefer different settings.&lt;/p&gt;

&lt;p&gt;I also added a &lt;code&gt;prebuild&lt;/code&gt; script that uses &lt;code&gt;tsc&lt;/code&gt; and &lt;code&gt;ESLint&lt;/code&gt; to compile and lint the project before building it to ensure the build is safe to publish.&lt;/p&gt;

&lt;p&gt;So when you run &lt;code&gt;npm run build&lt;/code&gt;, the &lt;code&gt;prebuild&lt;/code&gt; will be executed first and fail if there are errors. This script could be easily executed in a CI pipeline, for example, to make sure you don't merge or deploy the project if there are issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  ESLint
&lt;/h3&gt;

&lt;p&gt;I added ESLint to keep the codebase clean and consistent. The configuration can be found in &lt;code&gt;.eslint.js&lt;/code&gt; in case you want to add your own rules or modify something. &lt;/p&gt;

&lt;p&gt;There are also a few scripts I added to the &lt;code&gt;package.json&lt;/code&gt; to check for errors or styling issues:&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="c"&gt;# Print the list of problems found&lt;/span&gt;
npm run lint

&lt;span class="c"&gt;# Some of the issues can be automatically fixed using:&lt;/span&gt;
npm run lint:fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Beautiful and classy 🧐🎩
&lt;/h1&gt;

&lt;p&gt;Once you clone the template and install the dependencies, you'll be blessed with this beauty:&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%2Fi.imgur.com%2FbYVcrSr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FbYVcrSr.gif" alt="Acho the pup bouncing around"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you want to know more about my Game Dev journey, follow me on &lt;a href="https://www.instagram.com/pau.codes/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt; or &lt;a href="https://twitter.com/pauxdsantamaria" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. I'll be sharing my progress, tips, and tricks, and probably lots of bloopers and fails.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>gamedev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>5 Ideas to set your portfolio apart</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Mon, 26 Apr 2021 15:01:17 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/5-ideas-to-set-your-portfolio-apart-170c</link>
      <guid>https://dev.to/paulasantamaria/5-ideas-to-set-your-portfolio-apart-170c</guid>
      <description>&lt;p&gt;Tired of building TO-DO lists and e-commerce sites? One of these Chrome extension ideas could help you set your portfolio apart! 👩‍💻&lt;/p&gt;

&lt;p&gt;All you need to know to build your first Chrome Extension is the &lt;strong&gt;basics of HTML, JavaScript, and CSS&lt;/strong&gt;! These are a few fun ideas I came up with that can be created using only those technologies.&lt;/p&gt;

&lt;p&gt;If you're having doubts, check out my video &lt;a href="https://www.instagram.com/tv/CLIRDc4gWyz/" rel="noopener noreferrer"&gt;Creating a simple Chrome Extension in 2 minutes&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Dark mode for your favorite website
&lt;/h1&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%2Fi.imgur.com%2Fpd7BYKa.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%2Fi.imgur.com%2Fpd7BYKa.png" alt="Dark mode icon - The moon, some clouds and a few stars in the sky"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do you regularly use a website that you love but doesn't have dark mode? &lt;/p&gt;

&lt;p&gt;With Chrome extensions, you can create your own CSS stylesheet that overrides some styles in a particular website. This allows you to apply dark mode styles over the website when your extension is enabled.&lt;/p&gt;

&lt;p&gt;To achieve that, you should use &lt;strong&gt;Content Scripts&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Set &lt;code&gt;"run_at": "document_end"&lt;/code&gt;  in the &lt;code&gt;content_scripts&lt;/code&gt; object in the &lt;code&gt;manifest.json&lt;/code&gt; so your styles override the site's.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this project, you'll need to learn about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/mv3/content_scripts/" rel="noopener noreferrer"&gt;Content Scripts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  2. Weather App
&lt;/h1&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%2Fi.imgur.com%2F7XO2kWk.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%2Fi.imgur.com%2F7XO2kWk.png" alt="Weather icon - A cloud, rain and the sun behind it"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could create an extension that shows the current weather for a particular location on your browser. &lt;/p&gt;

&lt;p&gt;You'll need to call an API to retrieve the weather info: &lt;a href="https://openweathermap.org/api/one-call-api" rel="noopener noreferrer"&gt;OpenWeatherMap API&lt;/a&gt; is a good option. They have a free plan.&lt;/p&gt;

&lt;p&gt;Get the latest weather updates every few minutes in the background using an alarm from the &lt;code&gt;chrome.alarms&lt;/code&gt; API handled by a Service Worker.&lt;/p&gt;

&lt;p&gt;Store the results using the &lt;code&gt;chrome.storage&lt;/code&gt; API and display them in your extension's Action Popup.&lt;/p&gt;

&lt;p&gt;To call the API, you can use &lt;code&gt;fetch&lt;/code&gt;, and you'll need to add something like the following to your &lt;code&gt;manifest.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="nl"&gt;"host_permissions"&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;"https://some-weather-api.com/*"&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;For this project you'll need to learn about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/reference/storage/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.storage&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/apps/app_codelab_alarms/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.alarms&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/reference/action/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.action&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/mv3/background_pages/" rel="noopener noreferrer"&gt;Service Workers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  3. Show today's calendar.
&lt;/h1&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%2Fi.imgur.com%2F1N4uIDX.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%2Fi.imgur.com%2F1N4uIDX.png" alt="Calendar icon - A calendar with a clock"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could get easy access to your Calendar events for the day, showing them in your Browser through an Extension. &lt;/p&gt;

&lt;p&gt;As with the previous idea, you'll need to get the data from an external API (Google Calendar's or your favorite calendar API) every few minutes in the background using an alarm from the &lt;code&gt;chrome.alarms&lt;/code&gt; API and a Service Worker. &lt;/p&gt;

&lt;p&gt;You'll also need to store the results of the API request using &lt;code&gt;chrome.storage&lt;/code&gt;. Remember that Service Workers can be unloaded when the extension goes idle, so you can't just store the calendar events in a variable declared in your Service Worker.&lt;/p&gt;

&lt;p&gt;Display the events with their title, time, guests, etc., in your extension's Action Popup.&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;chrome.notifications&lt;/code&gt; API to show a message to the user a few minutes before the event.&lt;/p&gt;

&lt;p&gt;For this project you'll need to learn about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/reference/storage/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.storage&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/apps/app_codelab_alarms/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.alarms&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/reference/action/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.action&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/reference/notifications/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.notifications&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/mv3/background_pages/" rel="noopener noreferrer"&gt;Service Workers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  4. Daily coffee
&lt;/h1&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%2Fi.imgur.com%2FIaSq7MB.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%2Fi.imgur.com%2FIaSq7MB.png" alt="Coffee cup icon - A coffee cup with coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Show the user a random coffee recipe to try every day.&lt;/p&gt;

&lt;p&gt;In this case, you don't need to call an external API (at least at first). You could just store a list of your favorite recipes in a JSON array in your extension and use a &lt;code&gt;Math.random()&lt;/code&gt; to get a random item from the array every day.&lt;/p&gt;

&lt;p&gt;Display the coffee recipe with a nice picture in the Action Popup.&lt;/p&gt;

&lt;p&gt;To make it more robust, you could use &lt;code&gt;chrome.storage&lt;/code&gt; to store the last coffee recipe shown and the date to make sure of two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single coffee recipe is shown every day.&lt;/li&gt;
&lt;li&gt;We don't get the same recipe two days in a row.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this project you'll need to learn about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/reference/action/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.action&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.chrome.com/docs/extensions/reference/storage/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.storage&lt;/code&gt; API&lt;/a&gt; (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  5. E-Commerce wishlist
&lt;/h1&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%2Fi.imgur.com%2FyTvftWv.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%2Fi.imgur.com%2FyTvftWv.png" alt="Wishlist icon - A page with a list of items and a heart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Allow users to add products from Amazon, eBay, etc., to their wishlist: When a user finds a product they're interested in, they add it to their wishlist using a keyboard shortcut or from the &lt;strong&gt;Action popup&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Action popup also shows the previously added items and allows the user to remove them.&lt;/p&gt;

&lt;p&gt;The user should also be able to navigate to each product page in case they want to buy it. For this, you should use &lt;code&gt;chrome.tabs.create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You'll need to store links, name, and price for each product using &lt;code&gt;chrome.storage&lt;/code&gt; API. &lt;/p&gt;

&lt;p&gt;For this project you'll need to learn about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/extensions/reference/action/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.action&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.chrome.com/docs/extensions/reference/storage/" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.storage&lt;/code&gt; API&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.chrome.com/docs/extensions/reference/tabs" rel="noopener noreferrer"&gt;&lt;code&gt;chrome.tabs&lt;/code&gt; API&lt;/a&gt; - Particularly the &lt;a href="https://developer.chrome.com/docs/extensions/reference/tabs/#method-create" rel="noopener noreferrer"&gt;&lt;code&gt;create&lt;/code&gt; method&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope this post was helpful and inspires you to work on your next project! If you're interested in learning about Chrome extensions, check out &lt;a href="https://dev.to/paulasantamaria/creating-a-simple-chrome-extension-36m"&gt;my series about it&lt;/a&gt; where I create a simple chrome extension and gradually improve it. At the same time, I explore most of the &lt;code&gt;chrome&lt;/code&gt; APIs and concepts required to build the ideas mentioned in this post.&lt;/p&gt;

&lt;p&gt;You can also check out my video &lt;a href="https://www.instagram.com/tv/CLIRDc4gWyz/" rel="noopener noreferrer"&gt;Creating a simple Chrome Extension in 2 minutes&lt;/a&gt; to get a grasp of the process.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Icons by Smashicons &amp;amp; Freepik on Flaticon.com&lt;/em&gt;&lt;/p&gt;

</description>
      <category>chromeextension</category>
      <category>javascript</category>
      <category>portfolio</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Command-Line Interfaces: Structure &amp; Syntax</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Mon, 19 Apr 2021 12:30:30 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/command-line-interfaces-structure-syntax-2533</link>
      <guid>https://dev.to/paulasantamaria/command-line-interfaces-structure-syntax-2533</guid>
      <description>&lt;p&gt;If you are a developer, chances are you have used or read about CLIs already (maybe to execute git or npm commands or in some library's documentation). &lt;/p&gt;

&lt;p&gt;I work with multiple CLIs every day (docker, dotnet, npm, git, etc.), but I had never stopped to learn much about their structure or syntax until recently. Reading documentation and examples is usually enough to get by. &lt;/p&gt;

&lt;p&gt;However, I'm currently developing my own CLI for a personal project, so I decided to learn more about &lt;strong&gt;CLI design, structure, syntax, and conventions&lt;/strong&gt;. I did my research, which I documented and decided to share here in this post.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
1. Introduction

&lt;ul&gt;
&lt;li&gt;1.1. Structure&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;2. Arguments&lt;/li&gt;

&lt;li&gt;

3. Options

&lt;ul&gt;
&lt;li&gt;3.1. Flags&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

4. The --help flag

&lt;ul&gt;
&lt;li&gt;4.1. Common aliases for the --help flag:&lt;/li&gt;
&lt;li&gt;4.2. Levels of verbosity&lt;/li&gt;
&lt;li&gt;4.3. The help command&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

5. CLI description syntax conventions

&lt;ul&gt;
&lt;li&gt;5.1. Required Parameters&lt;/li&gt;
&lt;li&gt;5.2. Optional Parameters&lt;/li&gt;
&lt;li&gt;5.3. Arguments that can receive many values&lt;/li&gt;
&lt;li&gt;5.4. Mutually exclusive arguments&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;6. References&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  1. Introduction
&lt;/h1&gt;

&lt;p&gt;A CLI or Command Line Interface is an application that &lt;strong&gt;handles user interaction through the Command Line&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;They don't have graphics (except, maybe, for ASCII Art 😂).&lt;br&gt;
All the &lt;strong&gt;information is presented&lt;/strong&gt; to the user &lt;strong&gt;in the form of text&lt;/strong&gt; and &lt;strong&gt;the user interacts&lt;/strong&gt; with it by &lt;strong&gt;typing in commands&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;CLIs can be faster than GUIs for certain tasks (especially for monotonous tasks and when you're already familiar with the commands). One of the main advantages they have over GUI is that they're &lt;strong&gt;easier to *automate&lt;/strong&gt;* (running "npm install" in a CI pipeline could be a lot trickier without a CLI).&lt;/p&gt;

&lt;h2&gt;
  
  
  1.1. Structure
&lt;/h2&gt;

&lt;p&gt;Most CLIs are designed with the following structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;command&amp;gt; [arguments] [options]&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;ls /home/dir --all&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;&amp;lt;program&amp;gt; &amp;lt;command&amp;gt; [arguments] [options]&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;git fetch origin main --depth=10&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;&amp;lt;program&amp;gt; [arguments] [options]&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;docker -v&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&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%2Fi.imgur.com%2FlG5NuVa.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%2Fi.imgur.com%2FlG5NuVa.png" alt="Example: ls /home/my-dir --all"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FKmzktBL.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%2Fi.imgur.com%2FKmzktBL.png" alt="Example: git fetch origin main --depth=10 "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Commands are usually represented as &lt;em&gt;verbs&lt;/em&gt; and programs as &lt;em&gt;nouns&lt;/em&gt; (as they are usually a short version of the program's name). There are some cases where a second (or third, or more) noun is used to narrow the scope in which the command will operate, for example:&lt;/p&gt;

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

docker container start


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Here, we have the program (docker), the context (container), and the command (start).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  2. Arguments
&lt;/h1&gt;

&lt;p&gt;Arguments in a CLI allow &lt;strong&gt;users to send data&lt;/strong&gt; to the application, sometimes in a command context. They are often called &lt;em&gt;positional arguments&lt;/em&gt; because they are &lt;strong&gt;identified by their position&lt;/strong&gt;, which means users must write the value for each argument in the &lt;em&gt;correct position&lt;/em&gt;.&lt;/p&gt;

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

mycli command argValue1 argValue2
              |_______| |_______|
                  0         1


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

&lt;/div&gt;

&lt;p&gt;They can be required or not. If they are not required, the command's behavior may vary in the absence of the parameter, often using a &lt;em&gt;default value&lt;/em&gt; instead.&lt;/p&gt;

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

&lt;span class="c"&gt;# No argument provided -&amp;gt; Installs all dependencies from the package.json&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Argument value: some-package -&amp;gt; Installs only some-package&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install &lt;/span&gt;some-package


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

&lt;/div&gt;

&lt;p&gt;Another example:&lt;/p&gt;

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


&lt;span class="c"&gt;# No arguments -&amp;gt; Uses default remote and branch.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git fetch

&lt;span class="c"&gt;# Passing a specific remote and branch as arguments.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git fetch origin main


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  3. Options
&lt;/h1&gt;

&lt;p&gt;Options are &lt;strong&gt;named parameters&lt;/strong&gt; that can be passed to a command and are represented by &lt;strong&gt;key-value pairs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike positional arguments, their &lt;strong&gt;position is not important&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You're probably familiar with the following example:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Key: --message &lt;/span&gt;
&lt;span class="c"&gt;# Value: "commit message"&lt;/span&gt;
git commit &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"commit message"&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Options are usually (not always) used to represent &lt;strong&gt;optional&lt;/strong&gt; parameters. In most cases, if a parameter is required, a positional argument is the best way to go.&lt;/p&gt;

&lt;p&gt;Some options have &lt;strong&gt;aliases&lt;/strong&gt;, which are &lt;strong&gt;short versions&lt;/strong&gt; of the same option, easier to type and remember. They're usually identified by a single dash prefix:&lt;/p&gt;

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

&lt;span class="c"&gt;# Long version&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mycli &lt;span class="nt"&gt;--help&lt;/span&gt;

&lt;span class="c"&gt;# Alias&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mycli &lt;span class="nt"&gt;-h&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Different &lt;em&gt;delimiters&lt;/em&gt; are supported, depending on the CLI and Operating System. These are some of the most common:&lt;/p&gt;

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

&lt;span class="c"&gt;# Single space&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mycli &lt;span class="nt"&gt;--file&lt;/span&gt; text.txt

&lt;span class="c"&gt;# =&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mycli &lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;text.txt

&lt;span class="c"&gt;# :&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mycli &lt;span class="nt"&gt;--file&lt;/span&gt;:text.txt


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  3.1. Flags
&lt;/h2&gt;

&lt;p&gt;Options that &lt;strong&gt;don't require a value&lt;/strong&gt; are often called Flags. They are boolean, meaning their presence indicates "true" and their absence "false".&lt;/p&gt;

&lt;p&gt;Some examples of commands using flags:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git push &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt;


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  4. The --help flag
&lt;/h1&gt;

&lt;p&gt;Informing our users about the &lt;strong&gt;available commands&lt;/strong&gt; and their &lt;strong&gt;arguments and options&lt;/strong&gt; can be &lt;em&gt;tricky without a GUI&lt;/em&gt;. &lt;br&gt;
That's when the &lt;code&gt;--help&lt;/code&gt; flag comes in.&lt;/p&gt;

&lt;p&gt;When we include the help flag after a command, we ask the CLI to give us more information about it. &lt;br&gt;
Usually, this information will include a short description of the command, arguments, options, and aliases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--help&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  4.1. Common aliases for the --help flag:
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

-help, -h, -?, -H


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  4.2. Levels of verbosity
&lt;/h2&gt;

&lt;p&gt;Some CLIs allow the user to ask for &lt;strong&gt;different levels of help&lt;/strong&gt;. &lt;br&gt;
For example, the dotnet CLI will print the short version when we use &lt;code&gt;-h&lt;/code&gt; or &lt;code&gt;--help&lt;/code&gt; , and the long version when we use &lt;code&gt;-H&lt;/code&gt; or &lt;code&gt;-HELP&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Short version&lt;/span&gt;
dotnet &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;

&lt;span class="c"&gt;# Long version&lt;/span&gt;
dotnet &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Similarly, git CLI prints a summary of the command briefly explaining how to use it when we use &lt;code&gt;-h&lt;/code&gt;, but redirects to the offline HTML documentation when we use &lt;code&gt;--help&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="c"&gt;# Short version&lt;/span&gt;
git &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;

&lt;span class="c"&gt;# Offline HTML docs&lt;/span&gt;
git &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;--help&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Learning about the different levels of verbosity was really helpful (I've been using it frequently), although it's a shame there's not a standard for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4.3. The help command
&lt;/h2&gt;

&lt;p&gt;Some CLIs also provide a &lt;strong&gt;help command&lt;/strong&gt;, which usually gives more detailed information than the &lt;code&gt;--help&lt;/code&gt; flag:  &lt;/p&gt;

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

program &lt;span class="nb"&gt;help&lt;/span&gt; &amp;lt;command-name&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;For example, both dotnet CLI and npm CLI will open a browser and redirect you to the command's full documentation when we use the help command: &lt;/p&gt;

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

dotnet &lt;span class="nb"&gt;help&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

npm &lt;span class="nb"&gt;help&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  5. CLI description syntax conventions
&lt;/h1&gt;

&lt;p&gt;When reading a CLI's documentation or a "man" page, you'll likely come across a particular &lt;strong&gt;syntax used to describe commands&lt;/strong&gt;, their arguments, and options (located often under "Synopsis"). &lt;br&gt;
I used to not pay much attention to it (other than trying and figuring out the general anatomy of the command) and going straight to the examples. But after learning about it and how to interpret it, these descriptions have become much more useful to me.&lt;/p&gt;
&lt;h2&gt;
  
  
  5.1. Required Parameters
&lt;/h2&gt;

&lt;p&gt;Required parameters are often represented using just the name of the param, although in some cases, you may find them between angle brackets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dotnet new &amp;lt;template&amp;gt;&lt;/code&gt;: We can't create a new dotnet project without specifying a template.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mv source dest&lt;/code&gt;: When moving a file/directory, we must specify the source and destination parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  5.2. Optional Parameters
&lt;/h2&gt;

&lt;p&gt;Optional parameters are most commonly represented using square brackets: &lt;code&gt;mycli command [optionalParameter]&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker container ls [options]&lt;/code&gt; We can add some flags and options to alter the command's behavior.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git remote [-v | --verbose]&lt;/code&gt; We can use -v or --verbose to get more information about each git remote.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  5.3. Arguments that can receive many values
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ellipses&lt;/strong&gt; indicate the argument/option expects &lt;strong&gt;many values&lt;/strong&gt;. It can be applied to optional or required parameters &lt;/p&gt;

&lt;p&gt;Here's how it looks like applied to &lt;strong&gt;optional parameters&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

mycli &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;parameter...]


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

&lt;/div&gt;

&lt;p&gt;The previous statement tells us our parameter expects &lt;strong&gt;0 to N values&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required parameters&lt;/strong&gt; that receive many values are usually represented in one of two ways. Here is one of them:&lt;/p&gt;

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

mycli &lt;span class="nb"&gt;command&lt;/span&gt; &amp;lt;myParameter&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;myParameter...]


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

&lt;/div&gt;

&lt;p&gt;This statement tells us the command receives at least a single value for  &lt;code&gt;&amp;lt;myParameter&amp;gt;&lt;/code&gt; but can also receive more values, represented by &lt;code&gt;[myParameter...]&lt;/code&gt; &lt;strong&gt;resulting in 1 to N values&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example from the Docker CLI:&lt;/p&gt;

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

docker container start &amp;lt;container&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;container...]


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;When executing the start command, we must specify at least one container, but we can also start more than one in the same command.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second way of representing required parameters with many 1 to N values is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mycli command &amp;lt;myParameter...&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Or: &lt;code&gt;mycli command myParameter...&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we are not using square brackets, it's clear &lt;code&gt;myParameter&lt;/code&gt; is required, which indicates it needs &lt;strong&gt;at least one value (1 to N)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example:&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;mkdir &lt;/span&gt;directory...


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;We need to specify at least one directory to create when using mkdir, but we can also create more than one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5.4. Mutually exclusive arguments
&lt;/h2&gt;

&lt;p&gt;Some arguments &lt;strong&gt;cannot be used together&lt;/strong&gt; in the same command. Their &lt;em&gt;exclusive relationship&lt;/em&gt; is represented using pipes:&lt;/p&gt;

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

mycli &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;--option1&lt;/span&gt; | &lt;span class="nt"&gt;--option2&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The square brackets tell us we &lt;em&gt;don't have to&lt;/em&gt; use any of those options, and the pipe tells us that, if we decide to use them, we can't use them both at the same time. So any of the following are valid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mycli command&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mycli command --option1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mycli command --option2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the following would be invalid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mycli command --option1 --option2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example from git commit:&lt;/p&gt;

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

git commit &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; | &lt;span class="nt"&gt;--interactive&lt;/span&gt; | &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The previous statement tells us we can use any of those flags (&lt;code&gt;-a&lt;/code&gt;, &lt;code&gt;--interactive&lt;/code&gt; or &lt;code&gt;--patch&lt;/code&gt; ) but not together&lt;/li&gt;
&lt;li&gt;Also, the square brackets indicate we can also decide not to use any of them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what happens when we try to use &lt;code&gt;-a&lt;/code&gt; and &lt;code&gt;--patch&lt;/code&gt; together:&lt;/p&gt;

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

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--patch&lt;/span&gt; 

fatal: Only one of &lt;span class="nt"&gt;--include&lt;/span&gt;/--only/--all/--interactive/--patch can be used.


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

&lt;/div&gt;

&lt;p&gt;This syntax is often used to represent &lt;strong&gt;aliases&lt;/strong&gt;: &lt;code&gt;git remote [ - v | --verbose ]&lt;/code&gt;. Both flags are valid individually, but it doesn't make sense to use them simultaneously.&lt;/p&gt;

&lt;p&gt;Similarly, there are cases when &lt;strong&gt;at least one option must be included&lt;/strong&gt;. In those cases, we use vertical bars, and we group the parameters using curly brackets or parentheses, like so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mycli command { --option1 | --option2 }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Or:  &lt;code&gt;mycli command ( --option1 | --option2 )&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The curly brackets or parentheses indicate that at least one option must be included.&lt;/p&gt;

&lt;p&gt;Here's an example from &lt;code&gt;git remote&lt;/code&gt;:&lt;/p&gt;

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

git remote set-head &amp;lt;name&amp;gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt; | &lt;span class="nt"&gt;--auto&lt;/span&gt; | &lt;span class="nt"&gt;-d&lt;/span&gt; | &lt;span class="nt"&gt;--delete&lt;/span&gt; | &amp;lt;branch&amp;gt;&lt;span class="o"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;parenthesis&lt;/strong&gt; indicates that we must &lt;strong&gt;choose at least one&lt;/strong&gt; of those options&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;pipes&lt;/strong&gt; tell us &lt;strong&gt;we can't choose two or more&lt;/strong&gt; of them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we don't use any of the required options, we get this error:&lt;/p&gt;

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

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; git remote set-head origin 

usage: git remote set-head &amp;lt;name&amp;gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt; | &lt;span class="nt"&gt;--auto&lt;/span&gt; | &lt;span class="nt"&gt;-d&lt;/span&gt; | &lt;span class="nt"&gt;--delete&lt;/span&gt; | &amp;lt;branch&amp;gt;&lt;span class="o"&gt;)&lt;/span&gt; 
    &lt;span class="nt"&gt;-a&lt;/span&gt;, &lt;span class="nt"&gt;--auto&lt;/span&gt;            &lt;span class="nb"&gt;set &lt;/span&gt;refs/remotes/&amp;lt;name&amp;gt;/HEAD according to remote 
    &lt;span class="nt"&gt;-d&lt;/span&gt;, &lt;span class="nt"&gt;--delete&lt;/span&gt;          delete refs/remotes/&amp;lt;name&amp;gt;/HEAD


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

&lt;/div&gt;

&lt;p&gt;Another example from &lt;code&gt;dotnet new&lt;/code&gt;:&lt;/p&gt;

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

dotnet new &amp;lt;template&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-lang&lt;/span&gt;|--language &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"C#"&lt;/span&gt;|&lt;span class="s2"&gt;"F#"&lt;/span&gt;|VB&lt;span class="o"&gt;}]&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;So, the &lt;strong&gt;square brackets&lt;/strong&gt; indicate that passing a &lt;code&gt;-lang&lt;/code&gt;  or &lt;code&gt;--language&lt;/code&gt;  is optional&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;curly braces&lt;/strong&gt; and &lt;strong&gt;pipe&lt;/strong&gt; indicate that we must choose between C#, F#, and VB.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I know I could have developed my CLI without stopping to learn any of this. After all, there are libraries that would take care of the syntax and interpretation of commands. Nonetheless, I found it extremely interesting, and I have been using this new knowledge a lot after acquiring it. Finding and reading CLIs documentation has become a lot easier, and I learned many tips that will definitely help me develop a better, more intuitive CLI following the conventions and standards described in this article.&lt;/p&gt;

&lt;h1&gt;
  
  
  6. References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.codecademy.com/articles/command-line-interface" rel="noopener noreferrer"&gt;Command Line Interface - Codeacademy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/style/code-syntax" rel="noopener noreferrer"&gt;Documenting command-line syntax - Google Developer Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/tools/" rel="noopener noreferrer"&gt;.NET CLI documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs" rel="noopener noreferrer"&gt;Git CLI documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v7/commands" rel="noopener noreferrer"&gt;NPM CLI documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terminal</category>
      <category>cli</category>
      <category>commandline</category>
      <category>documentation</category>
    </item>
    <item>
      <title>Chrome Extensions: Migrating to Manifest v3</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Fri, 26 Mar 2021 11:05:12 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/chrome-extensions-migrating-to-manifest-v3-5e88</link>
      <guid>https://dev.to/paulasantamaria/chrome-extensions-migrating-to-manifest-v3-5e88</guid>
      <description>&lt;p&gt;&lt;strong&gt;Manifest v3&lt;/strong&gt; has been available since the release of &lt;strong&gt;Chrome 88&lt;/strong&gt; earlier this year. If you're planning on building a Chrome extension or if you're currently building one, you should learn about this new version of the Chrome Extensions Manifest in order to &lt;strong&gt;benefit from the new features and vision.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post, we'll go through a &lt;strong&gt;brief overview of Manifest v3&lt;/strong&gt;, then we'll take a look at the &lt;strong&gt;Migration Checklist&lt;/strong&gt; to learn everything we'll need to change to migrate our sample extension. Finally, we'll &lt;strong&gt;apply the changes step by step&lt;/strong&gt; so at the end, our sample extension will be successfully migrated to Manifest v3!&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Manifest v3 Overview
&lt;/h1&gt;

&lt;p&gt;Chrome Extensions launched a decade ago, and, according to the docs, Manifest V3 represents one of the biggest shifts in the extensions platform since then. It includes many changes that bring Chrome Extensions closer to the modern web (like promises and service workers!).&lt;/p&gt;

&lt;h2&gt;
  
  
  1.1. Three pillars
&lt;/h2&gt;

&lt;p&gt;As stated in the docs, Manifest v3 is a step forward in Chrome Extensions' strategic direction. The main focus of this vision is in the following 3 pillars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: The idea here seems to be to let the user know about the extension's activities and how their information is used. And also reduce the need for extensions to have access to user data persistently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Extensions will be required to follow stricter protocols, and, for example, they won't be allowed to access scripts from outside the extension context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Keep good performance in all devices and avoid performance issues when extensions are installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They also state that they will preserve the &lt;strong&gt;"webbiness"&lt;/strong&gt; of Chrome extensions to keep the barriers for developers low and benefit from the advances of the web.&lt;/p&gt;

&lt;p&gt;Finally, they say the idea is to keep the platform &lt;strong&gt;capable&lt;/strong&gt;, powerful, and feature-rich so developers can keep delivering value to users through it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1.2. Main changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Background pages/scripts are replaced by &lt;strong&gt;Service workers&lt;/strong&gt;.
&lt;/h3&gt;

&lt;p&gt;Much like background pages, service workers are scripts that run in the background and are independent of web pages. They don't need interaction with the website or a user.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ Fun fact! Service workers were inspired by the background pages in Chrome Extensions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l3mZ8A8zDS8pKKFgI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l3mZ8A8zDS8pKKFgI/giphy.gif" alt="An animated gif of Janet, from the series "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The new &lt;code&gt;declarativeNetRequest&lt;/code&gt; API handles &lt;strong&gt;Network request modification&lt;/strong&gt;.
&lt;/h3&gt;

&lt;p&gt;This new API is focused on privacy. The request will still be able to be modified and blocked, but in a privacy-preserving way. &lt;/p&gt;

&lt;p&gt;This API is an improvement from the old &lt;code&gt;webRequest&lt;/code&gt; API that fixes privacy, performance, and compatibility issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remotely-hosted code is no longer allowed
&lt;/h3&gt;

&lt;p&gt;This change came to improve security. Since all the code will be available in the extension package, extensions will be more reliably and efficiently reviewed before they are made available for the users.&lt;/p&gt;

&lt;p&gt;The alternative recommended for extensions that require some feature to be handled remotely is using &lt;em&gt;remote configuration files&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Added &lt;strong&gt;Promise support&lt;/strong&gt; for many APIs
&lt;/h3&gt;

&lt;p&gt;We can finally use promises in some of &lt;code&gt;chrome&lt;/code&gt; APIs! 🎈 This was something I was really looking forward to. &lt;/p&gt;

&lt;p&gt;Callbacks are still supported, so you don't need to refactor all your code right away. &lt;/p&gt;

&lt;h3&gt;
  
  
  Other minor changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;browserAction&lt;/code&gt; API and &lt;code&gt;pageAction&lt;/code&gt; API are now unified in a single API called &lt;code&gt;action&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Web-accessible resources&lt;/strong&gt; are no longer available to all websites, which allowed extensions to use fingerprinting to track users.&lt;/li&gt;
&lt;li&gt;The method &lt;code&gt;executeScript()&lt;/code&gt; was moved from the &lt;code&gt;tabs&lt;/code&gt; API into a new &lt;code&gt;scripting&lt;/code&gt; API and no longer allows string scripts. You must provide a script file path or a function.&lt;/li&gt;
&lt;li&gt;Host permissions are specified separately from the &lt;code&gt;permissions&lt;/code&gt; property in the &lt;code&gt;manifest.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;content_security_policy&lt;/code&gt; used to be a string, now it's an object, and you must specify the extension pages (HTML files and service workers) covered by the policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  2. Migrating "Acho, where are we?" to Manifest v3
&lt;/h1&gt;

&lt;p&gt;Now that we know the highlights of Manifest v3 and its vision, we can move on to migrate our sample extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.1. Migration checklist
&lt;/h2&gt;

&lt;p&gt;When migrating our extension to manifest v3, the first thing we should do is check the &lt;a href="https://developer.chrome.com/docs/extensions/mv3/mv3-migration-checklist/" rel="noopener noreferrer"&gt;Manifest V3 migration checklist&lt;/a&gt;. I'll mark each bullet with ✅ when the change applies to our extension or ❌ when it doesn't:&lt;/p&gt;

&lt;p&gt;❌ Do you have host permissions in your manifest?&lt;/p&gt;

&lt;p&gt;✅ Are you using background pages?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace background.page or background.scripts with background.service_worker in manifest.json. Note that the service_worker field takes a string, not an array of strings.&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;background.persistent&lt;/code&gt; from &lt;code&gt;manifest.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Update background scripts to adapt to the service worker execution context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Are you using the browser_action or page_action property in manifest.json?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since these two APIs were unified into a single action API, we must replace these properties with action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Are you using chrome.browserAction or chrome.pageAction JavaScript API?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrate to the chrome.action API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Are you currently using the blocking version of chrome.webRequest?&lt;/p&gt;

&lt;p&gt;❌ Are you using these scripting/CSS methods in chrome.tabs API?&lt;/p&gt;

&lt;p&gt;❌ Are you executing remote code or arbitrary strings?&lt;/p&gt;

&lt;p&gt;❌ Are you executing functions that expect an MV2 background context?&lt;/p&gt;

&lt;p&gt;❌ Are you making CORS requests in content scripts?&lt;/p&gt;

&lt;p&gt;❌ Are you using a custom content_security_policy in manifest.json?&lt;/p&gt;

&lt;h2&gt;
  
  
  2.2. Applying the changes described in the Checklist
&lt;/h2&gt;

&lt;p&gt;Let's review each point from the previous section in-depth and apply the appropriate changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2.1. Set the Manifest version to 3
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;manifest.json&lt;/code&gt; file, set the value of &lt;code&gt;manifest_version&lt;/code&gt; to "3".&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2.2. Replacing background pages with service workers
&lt;/h3&gt;

&lt;p&gt;As we replace our background page with a service worker, we must remember two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service workers are &lt;strong&gt;terminated when inactive&lt;/strong&gt; and &lt;strong&gt;restarted when they're needed&lt;/strong&gt; again.&lt;/li&gt;
&lt;li&gt;Service workers &lt;strong&gt;don't have access to the DOM&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This won't be a problem for us since when I created our background script, I already knew this change was coming, and so I made sure to keep those 2 things in mind in the original design of my background script. &lt;/p&gt;

&lt;p&gt;The first change we need to do is rename the &lt;code&gt;background.js&lt;/code&gt; script to &lt;code&gt;service-worker.js&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now we'll set our new service worker in the &lt;code&gt;manifest.json&lt;/code&gt; file. To do that, we must replace the old &lt;code&gt;background&lt;/code&gt; property with the following:&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;"background"&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;"service_worker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service-worker.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, notice that &lt;strong&gt;the &lt;code&gt;service_worker&lt;/code&gt; property is a string&lt;/strong&gt;. So &lt;em&gt;we can't declare more than one file&lt;/em&gt; there (as far as I know, I didn't find much about this issue in the docs). Because of this change, I couldn't add the other two scripts I needed: &lt;code&gt;acho.js&lt;/code&gt; and &lt;code&gt;page.service.js&lt;/code&gt;. So I found a new way to include them and call them from &lt;code&gt;service-worker.js&lt;/code&gt;: Simply use the &lt;code&gt;importScripts()&lt;/code&gt; method at the top of the &lt;code&gt;service-worker.js&lt;/code&gt; script:&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;// service-worker.js&lt;/span&gt;
&lt;span class="nf"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;acho.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page.service.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* More code */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can see all the changes I applied to replace my background script with a service worker in &lt;a href="https://github.com/pawap90/acho-where-are-we/commit/185c939637ffea25db083b3381a84cf5c72d4ab9#diff-9d9dda0262cb14fb69febe39e1fcc19e6b2e02d6560efad8639ec5d2f152e9db" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.2.3. Replacing "browser_action" by "action" in the manifest
&lt;/h3&gt;

&lt;p&gt;Since these two APIs were unified into a single &lt;code&gt;action&lt;/code&gt; API, we must change the property &lt;code&gt;browser_action&lt;/code&gt; to &lt;code&gt;action&lt;/code&gt; in our &lt;code&gt;manifest.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;"action"&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;"default_popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popup.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default_icon"&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;"16"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"images/icon16.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"24"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"images/icon24.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"32"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"images/icon32.png"&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;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;See the &lt;a href="https://github.com/pawap90/acho-where-are-we/commit/9bfae96327a8be8a368012af398521013dbcab0c?branch=9bfae96327a8be8a368012af398521013dbcab0c" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.2.4. Use the "action" API instead of the "browserAction" API
&lt;/h3&gt;

&lt;p&gt;Similarly to the previous section, we must use the new unified &lt;code&gt;action&lt;/code&gt; API. &lt;/p&gt;

&lt;p&gt;In our sample extension, we had only used the &lt;code&gt;browserAction&lt;/code&gt; API to set the badge color and text, so we'll replace those lines:&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;// acho.js&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Acho&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/* More code */&lt;/span&gt;

    &lt;span class="nx"&gt;growl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;quiet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&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="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* More code */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: The &lt;code&gt;text&lt;/code&gt; property is now required in the &lt;code&gt;setBadge&lt;/code&gt; method, so we can no longer use &lt;code&gt;chrome.action.setBadgeText({ });&lt;/code&gt; to clear the badge text.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See the &lt;a href="https://github.com/pawap90/acho-where-are-we/commit/e745baff590c7c55c8ae2a4976964c69acf893b2#diff-9d9dda0262cb14fb69febe39e1fcc19e6b2e02d6560efad8639ec5d2f152e9db" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.2.5. Specify an URL pattern for Web-accessible resources
&lt;/h3&gt;

&lt;p&gt;This one wasn't in the Checklist, but I realized I needed to make a change because when I tried the extension, I got an error that said: "Invalid value for 'web_accessible_resources[0]'. Entry must be a dictionary value".&lt;/p&gt;

&lt;p&gt;So, I figure out we must explicitly define which pages will have access to our resources. This is done via the &lt;code&gt;matches&lt;/code&gt; property (similarly to content scripts). Here's how the new &lt;code&gt;web_accessible_resources&lt;/code&gt; property looks like in the &lt;code&gt;manifest.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;"web_accessible_resources"&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;span class="nl"&gt;"matches"&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;"&amp;lt;all_urls&amp;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;"resources"&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;"images/icon32.png"&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;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;See the &lt;a href="https://github.com/pawap90/acho-where-are-we/commit/4056176cf8cfbd654ee178bccc71aa8f162c82be#diff-9d9dda0262cb14fb69febe39e1fcc19e6b2e02d6560efad8639ec5d2f152e9db" rel="noopener noreferrer"&gt;commit&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.2.6. Replace the command "_execute_browser_action" with "_execute_action"
&lt;/h3&gt;

&lt;p&gt;This one wasn't in the Checklist either, and I also couldn't find anything related to this change in the docs, but I figure out the change through my own intuition 😂.&lt;/p&gt;

&lt;p&gt;We used to have a &lt;code&gt;command&lt;/code&gt; defined in our &lt;code&gt;manifest.json&lt;/code&gt; called &lt;code&gt;_execute_browser_action&lt;/code&gt; that automatically (without adding any extra code) will trigger our extension's popup (browser action). &lt;/p&gt;

&lt;p&gt;After updating to Manifest v3, this command wasn't working, and I figured it was because of the merge between &lt;code&gt;browserAction&lt;/code&gt; and &lt;code&gt;pageAction&lt;/code&gt; into the new &lt;code&gt;action&lt;/code&gt; API. So I changed &lt;code&gt;_execute_browser_action&lt;/code&gt; to &lt;code&gt;_execute_action&lt;/code&gt;, and it worked 🎉.&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;"commands"&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;"_execute_action"&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;"suggested_key"&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;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alt+Shift+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="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;h3&gt;
  
  
  2.2.7. Refactor to use promises
&lt;/h3&gt;

&lt;p&gt;Finally, after everything else was working, I decided to refactor my code to use promises in the APIs that support them.&lt;/p&gt;

&lt;p&gt;Here are some examples:&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;// Using callback:&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Using promises:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Optional callback:&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;active&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="c1"&gt;// Using promises:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;active&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Using callback:&lt;/span&gt;
    &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tabs&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="c1"&gt;// callback logic&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Using promises:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tabs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Remember: You don't need to refactor your code to use promises right away. Callbacks are still supported in Manifest v3. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One thing to notice is that I couldn't make promises to work with the &lt;code&gt;chrome.storage&lt;/code&gt; API. This may be one of the APIs that don't support promises yet, but I couldn't find more information on the subject in the docs.&lt;/p&gt;

&lt;p&gt;Here's &lt;a href="https://github.com/pawap90/acho-where-are-we/commit/c5234a43794cb38f4316f9af136eeac07457382f#diff-9d9dda0262cb14fb69febe39e1fcc19e6b2e02d6560efad8639ec5d2f152e9db" rel="noopener noreferrer"&gt;the commit&lt;/a&gt; if you're interested.&lt;/p&gt;
&lt;h3&gt;
  
  
  Done!
&lt;/h3&gt;

&lt;p&gt;Our sample extension was successfully migrated to Manifest v3. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/GLViT0g0w1NhC/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/GLViT0g0w1NhC/giphy.gif" alt="Charlie Chaplin audience cheering in black and white"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  The repo
&lt;/h1&gt;

&lt;p&gt;You can find this and all of the examples of this series in my repo:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pawap90" rel="noopener noreferrer"&gt;
        pawap90
      &lt;/a&gt; / &lt;a href="https://github.com/pawap90/acho-where-are-we" rel="noopener noreferrer"&gt;
        acho-where-are-we
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Acho (a cute pup) tells you the title of the current page on your browser. A sample chrome extension.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Hope you found this article useful! &lt;/p&gt;

&lt;p&gt;💬 Let me know what you think in the comments! &lt;/p&gt;

</description>
      <category>chromeextension</category>
      <category>javascript</category>
      <category>chrome</category>
      <category>manifestv3</category>
    </item>
    <item>
      <title>Chrome Extensions: Adding a badge</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Fri, 19 Mar 2021 00:08:25 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/chrome-extensions-adding-a-badge-644</link>
      <guid>https://dev.to/paulasantamaria/chrome-extensions-adding-a-badge-644</guid>
      <description>&lt;p&gt;I thought I should add some new fun features to our sample extension to explore a few more things that can be done with Chrome extensions. I was curious about &lt;strong&gt;badges&lt;/strong&gt; because they seem like an interesting tool to &lt;em&gt;communicate changes&lt;/em&gt; in the state of our extension to our users.&lt;/p&gt;

&lt;h1&gt;
  
  
  About badges
&lt;/h1&gt;

&lt;p&gt;Badges appear over the Browser Action Icon and &lt;strong&gt;include a short text&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The text can include a number of characters, but the badge &lt;em&gt;will only show the amount that fits&lt;/em&gt; in that tiny space (the docs say around 4, but I could fit a maximum of 6). The &lt;strong&gt;characters&lt;/strong&gt; that &lt;strong&gt;don't fit won't be visible&lt;/strong&gt;, so try to keep your badge text short.&lt;/p&gt;

&lt;p&gt;To create a badge all we need to do is &lt;strong&gt;set the text&lt;/strong&gt;, like so:&lt;/p&gt;

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

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myTabId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// or to add it to all tabs:&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;tabId&lt;/code&gt; is optional, but when included, the text specified for the badge will only be visible when that particular tab is active.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;text&lt;/code&gt; is optional too, but the badge won't be visible if we don't include it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, to hide the badge all we need to do is set the text for that particular tab to &lt;code&gt;null&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myTabId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// or, the shorter version:&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myTabId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// or to remove it from all tabs:&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;For extra customization, we can also &lt;strong&gt;change the background color&lt;/strong&gt; of the badge (the default is blue):&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// callback&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  Updating our sample extension
&lt;/h1&gt;

&lt;p&gt;You see, 🐶 Acho gets impatient whenever a new page or tab is loaded and we don't ask him about it right away (I mean, it's his job!). So we'll give him a tool to express himself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a &lt;strong&gt;new tab&lt;/strong&gt; is created, or the &lt;strong&gt;active tab gets updated&lt;/strong&gt;, Acho will let us know he's ready to work by &lt;em&gt;creating a badge&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;After his job is done, &lt;em&gt;the badge will disappear&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how it will look:&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%2Fi.imgur.com%2Fntmu5wL.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fntmu5wL.gif" alt="A badge with the text "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So first, we'll update the Acho class &lt;code&gt;acho.js&lt;/code&gt; to give him the ability to growl and be quiet:&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;// acho.js&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Acho&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;growl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;grr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;quiet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadgeText&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;Then we'll listen for the &lt;code&gt;tabs.onCreated&lt;/code&gt; and &lt;code&gt;tabs.onUpdated&lt;/code&gt; events in our &lt;code&gt;background.js&lt;/code&gt;, and when they're fired we'll let Acho growl using the &lt;code&gt;growl&lt;/code&gt; method we just added:&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;// background.js&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onUpdated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changeInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Acho&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;growl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onCreated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Acho&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;growl&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 finally, we'll ask Acho to be quiet when he fulfills his job. This must be done both in the &lt;code&gt;background.js&lt;/code&gt; file and the &lt;code&gt;popup.js&lt;/code&gt; file since Acho can do his job through the browser action (popup) or a command handled in the background script.&lt;/p&gt;

&lt;p&gt;In the background script, we must add a new line at the end of our &lt;code&gt;barkTitle&lt;/code&gt; method. So, once the notification is sent, we can remove the badge:&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;// background.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;barkTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Acho&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;tab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveTab&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;tabTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;savePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quiet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// 👈&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;In the &lt;code&gt;popup.js&lt;/code&gt;, we will remove the notification after loading all the info in the popup:&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;// popup.js&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialogBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dialog-box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Acho&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;tab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveTab&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;bark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBarkedTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;dialogBox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Store page.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;savePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Display history.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;displayPages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Clear history&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearHistoryBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clear-history&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;clearHistoryBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearPages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;displayPages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quiet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// 👈&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;h1&gt;
  
  
  Done!
&lt;/h1&gt;

&lt;p&gt;That's it! We learned &lt;strong&gt;how to add a badge, hide it and change its color&lt;/strong&gt;, and now Acho can express his frustration when we don't let him fulfill his purpose 😂.&lt;/p&gt;
&lt;h1&gt;
  
  
  The repo
&lt;/h1&gt;

&lt;p&gt;You can find this and all of the examples of this series in my repo:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pawap90" rel="noopener noreferrer"&gt;
        pawap90
      &lt;/a&gt; / &lt;a href="https://github.com/pawap90/acho-where-are-we" rel="noopener noreferrer"&gt;
        acho-where-are-we
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Acho (a cute pup) tells you the title of the current page on your browser. A sample chrome extension.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>chromeextension</category>
      <category>javascript</category>
      <category>chrome</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Publishing a Chrome Extension</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Fri, 12 Mar 2021 11:10:57 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/publishing-a-chrome-extension-2d2</link>
      <guid>https://dev.to/paulasantamaria/publishing-a-chrome-extension-2d2</guid>
      <description>&lt;p&gt;This series wouldn't be complete without a post about how to &lt;strong&gt;publish&lt;/strong&gt; a Chrome Extension, so here it is!&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Prepare the extension to be published
&lt;/h1&gt;

&lt;p&gt;We need to create a &lt;strong&gt;.zip file&lt;/strong&gt; containing the source code for our extension. The only required file is the &lt;code&gt;manifest.json&lt;/code&gt;, but we'll need to include the whole project if we want everything to work correctly.&lt;/p&gt;

&lt;p&gt;We'll later upload this file to the Chrome Developer Dashboard.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Create a Chrome developer account
&lt;/h1&gt;

&lt;p&gt;To register as a Chrome Web Store developer, we'll need to access the &lt;a href="https://chrome.google.com/webstore/devconsole" rel="noopener noreferrer"&gt;developer console&lt;/a&gt;.&lt;br&gt;
Once we do that, we'll have to accept the Developer Agreement and Privacy Policies and pay the $5 registration fee (a one-time payment).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't forget to access the Account panel and fill in your email address. If you don't, you won't be able to publish an extension.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  3. Publish the extension
&lt;/h1&gt;

&lt;p&gt;We'll go to the "Items" panel and click over the "New Item" button to publish our extension.&lt;br&gt;
We'll see a modal where we'll drop our .zip file (the one created in step 1).&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%2Fi.imgur.com%2FqVULKua.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FqVULKua.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After uploading the file, we'll be redirected to the "Store Listing" form. Here we'll have to fill all required fields, which include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Description&lt;/li&gt;
&lt;li&gt;Category&lt;/li&gt;
&lt;li&gt;Language&lt;/li&gt;
&lt;li&gt;Small Icon (128 x 128 px)&lt;/li&gt;
&lt;li&gt;At least one Screenshot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After filling all the required fields, we should go ahead and do the same on the "Privacy practices" form. Here we'll need to explain the &lt;strong&gt;purpose&lt;/strong&gt; of the extension and &lt;strong&gt;justify why we need each of the permissions&lt;/strong&gt; we listed in our &lt;code&gt;manifest.json&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Host Permission Justification&lt;/strong&gt;&lt;br&gt;
Given that we added a content script with access to &lt;em&gt;all the web pages&lt;/em&gt; visited by our users in this example, we'll need to justify why we need that, and &lt;strong&gt;our extension will take longer to get verified&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&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%2Fi.imgur.com%2FEVrd3BA.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FEVrd3BA.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After completing all fields in both forms, check the buttons at the top-right of the screen:&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%2Fi.imgur.com%2FwZLX8AG.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%2Fi.imgur.com%2FwZLX8AG.png" alt="The buttons "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the button "Submit for review" is greyed out, click over "Why can't I submit?" to learn what's missing.&lt;/p&gt;

&lt;p&gt;Once we've met all the requirements, click "Submit for review":&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%2Fi.imgur.com%2FHdRRpe3.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%2Fi.imgur.com%2FHdRRpe3.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our extension is submitted, and we just need to wait for it to be reviewed and approved!&lt;/p&gt;

&lt;p&gt;In this case, since we added a content script that requires access to all web pages, we'll need to wait a little longer for the review. &lt;/p&gt;
&lt;h1&gt;
  
  
  The repo
&lt;/h1&gt;

&lt;p&gt;You can find all of the examples of this series in my repo:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pawap90" rel="noopener noreferrer"&gt;
        pawap90
      &lt;/a&gt; / &lt;a href="https://github.com/pawap90/acho-where-are-we" rel="noopener noreferrer"&gt;
        acho-where-are-we
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Acho (a cute pup) tells you the title of the current page on your browser. A sample chrome extension.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>chromeextension</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>chrome</category>
    </item>
    <item>
      <title>Chrome extensions: Local storage</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Wed, 24 Feb 2021 11:34:40 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/chrome-extensions-local-storage-1b34</link>
      <guid>https://dev.to/paulasantamaria/chrome-extensions-local-storage-1b34</guid>
      <description>&lt;p&gt;I'm back with another post about &lt;strong&gt;Chrome extensions&lt;/strong&gt;! This time I wanted to explore how to &lt;em&gt;store data locally&lt;/em&gt; using the &lt;code&gt;chrome.storage&lt;/code&gt; API.&lt;/p&gt;

&lt;p&gt;In this post, we're going to add yet another feature to our original extension (Acho, where are we?). This new feature will &lt;strong&gt;store the Title and URL of the page&lt;/strong&gt; each time we call Acho to tell us where we are. We will then &lt;strong&gt;list&lt;/strong&gt; all of the pages and allow the user to &lt;strong&gt;navigate&lt;/strong&gt; to one of them or &lt;strong&gt;clear&lt;/strong&gt; the list.&lt;/p&gt;

&lt;p&gt;Here's a quick demo:&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%2Fi.imgur.com%2FZJJgxaC.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FZJJgxaC.gif" alt="When the user opens the popup, a list of the previously visited pages appears. The user can navigate to each page clicking over it, or they can clear the list using a button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let's get started!&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Add the storage permission to the manifest.json
&lt;/h1&gt;

&lt;p&gt;As usual, the first thing we need to update is our &lt;code&gt;manifest.json&lt;/code&gt;. This time we're going to add the &lt;code&gt;storage&lt;/code&gt; permission:&lt;/p&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;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;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;"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;"Acho, where are we?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&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;"tabs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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;This will allow our extension to use the &lt;code&gt;storage&lt;/code&gt; API.&lt;/p&gt;
&lt;h1&gt;
  
  
  2. Create the Page Service
&lt;/h1&gt;

&lt;p&gt;Since we already know how to reuse code in chrome extensions, we will create the data access logic in a separate class called &lt;code&gt;PageService&lt;/code&gt;. Here we will add the following methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getPages&lt;/code&gt;: Will return the list of stored pages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;savePage&lt;/code&gt;: Will receive the page data and store it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clearPages&lt;/code&gt;: Will remove all the pages from the storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  About the storage API
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;chrome.storage&lt;/code&gt; API allows us to store objects using a &lt;em&gt;key&lt;/em&gt; that we will later use to retrieve said objects. This API is a bit more robust than the &lt;code&gt;localStorage&lt;/code&gt; API, but it's not as powerful as an actual database, so we will need to manage some things ourselves.&lt;/p&gt;

&lt;p&gt;To save an object we will define a &lt;em&gt;key-value pair&lt;/em&gt; and use the &lt;code&gt;set&lt;/code&gt; method. Here's an example:&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stored name: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;And to retrieve our value we will use the &lt;code&gt;get&lt;/code&gt; method and the &lt;em&gt;key&lt;/em&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Retrieved name: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;Finally, to clear the storage we have two options:&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;// Completely clear the storage. All items are removed.&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Everything was removed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Remove items under a certain key&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Removed items for the key: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;Another thing to have in mind when working with this API is &lt;strong&gt;error handling&lt;/strong&gt;. When an error occurs using the &lt;code&gt;get&lt;/code&gt; or &lt;code&gt;set&lt;/code&gt; methods, the property &lt;code&gt;chrome.runtime.lastError&lt;/code&gt; will be set. So we need to check for that value after calling the get/set methods. A few examples:&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error setting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stored name: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error getting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Retrieved name: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Don't worry, I promise the actual implementation will be better than a &lt;code&gt;console.log&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And, before we move on to the real implementation, I wanted to show you something else. I like to work with &lt;code&gt;async/await&lt;/code&gt; instead of &lt;code&gt;callbacks&lt;/code&gt;. So I created a simple function to promisify the callbacks and still handle errors properly. Here it is:&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;toPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&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;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage example: &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveData&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Now we can await it:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;saveData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


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

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You can replace &lt;code&gt;chrome.storage.local&lt;/code&gt; with &lt;code&gt;chrome.storage.sync&lt;/code&gt; to sync the data automatically to any Chrome browser where the user is logged into (if they have the sync feature enabled). But keep in mind that there are &lt;a href="https://developer.chrome.com/docs/extensions/reference/storage/#property-sync" rel="noopener noreferrer"&gt;limits, as specified in the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chrome.storage.local&lt;/code&gt; also has limits in the amount of data that can be stored, but that limit can be ignored if we include the &lt;code&gt;unlimitedStorage&lt;/code&gt; permission in the &lt;code&gt;manifest.json&lt;/code&gt; (check &lt;a href="https://developer.chrome.com/docs/extensions/reference/storage/#property-local" rel="noopener noreferrer"&gt;the docs&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's move on to our actual implementation!&lt;/p&gt;
&lt;h2&gt;
  
  
  PageService class
&lt;/h2&gt;

&lt;p&gt;As I said before, our PageService will have 3 methods to store, retrieve and remove our &lt;code&gt;pages&lt;/code&gt;. So here they are:&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;PAGES_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;getPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;PAGES_KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&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;researches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pages&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
                &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;researches&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;static&lt;/span&gt; &lt;span class="nx"&gt;savePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPages&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;updatedPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;PAGES_KEY&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;updatedPages&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;           
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedPages&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;static&lt;/span&gt; &lt;span class="nx"&gt;clearPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;toPromise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;PAGES_KEY&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;A few things to notice about this class:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are using the &lt;code&gt;toPromise&lt;/code&gt; function we talked about earlier.&lt;/li&gt;
&lt;li&gt;We are storing an &lt;em&gt;array of &lt;code&gt;pages&lt;/code&gt;&lt;/em&gt;, so every time we add a new page to the storage, we need to &lt;strong&gt;retrieve the entire array&lt;/strong&gt;, &lt;strong&gt;add our new element&lt;/strong&gt; at the end &lt;strong&gt;and replace the original array&lt;/strong&gt; in storage. This is one of a few options I came up with to work with arrays and the &lt;code&gt;chrome.storage&lt;/code&gt; API since it doesn't allow me to directly push a new element to the array.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  3. Make our PageService available to our components
&lt;/h1&gt;

&lt;p&gt;As we saw in the previous posts of this series, we need to make some changes to allow our new class to be &lt;strong&gt;used by our extension's&lt;/strong&gt; different components.&lt;/p&gt;

&lt;p&gt;First, we will add it as a script to our &lt;code&gt;popup.html&lt;/code&gt; so we can later use it in &lt;code&gt;popup.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="c"&gt;&amp;lt;!-- popup.html --&amp;gt;&lt;/span&gt;

&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;/head&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;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;'popup.js'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;'acho.js'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;'page.service.js'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- 👈 --&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;p&gt;This will allow us to save pages, retrieve them and clear them from the &lt;em&gt;browser action&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And finally, we'll add it as a &lt;code&gt;background script&lt;/code&gt; in our &lt;code&gt;manifest.json&lt;/code&gt; so we can also call the &lt;code&gt;savePage&lt;/code&gt; method &lt;em&gt;from our background script&lt;/em&gt; when the user uses the shortcut:&lt;/p&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;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;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;"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;"Acho, where are we?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"background"&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;"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="s2"&gt;"background.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;"acho.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;"page.service.js"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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;"persistent"&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="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&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;h1&gt;
  
  
  4. Update our popup.js
&lt;/h1&gt;

&lt;p&gt;Now let's update our popup.js to add the new features.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dialogBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dialog-box&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Acho&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;tab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveTab&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;bark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBarkedTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;dialogBox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Store page.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;savePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Display history.&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;displayPages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Clear history.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clearHistoryBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clear-history&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;clearHistoryBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearPages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;displayPages&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;displayPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;visitedPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPages&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;pageList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;page-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;pageList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;visitedPages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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;pageItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;pageList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageItem&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;pageLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;pageLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;pageLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;pageLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;pageLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;active&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;span class="nx"&gt;pageItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageLink&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 in the previous code, we are using our three methods from &lt;code&gt;PageService&lt;/code&gt; to add the current page to the storage, list the pages on the screen and allow the user to navigate them, and clear the list. &lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;displayPages&lt;/code&gt; method to display the pages: To do that we retrieve the list of pages and generate a &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element and an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element for each page. It's important to notice that we need to override the &lt;code&gt;onclick&lt;/code&gt; event on our &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element because if we leave the default functionality, the extension will try to load the page &lt;em&gt;inside our popup&lt;/em&gt;, which it's not what we want and it will cause an error. Instead, we create a new tab and navigate to the link using &lt;code&gt;chrome.tabs.create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's all we need to do to add the new feature to our popup.&lt;/p&gt;
&lt;h1&gt;
  
  
  5. Saving the page from the background script
&lt;/h1&gt;

&lt;p&gt;Now let's make sure the pages are also stored when we use the command shortcut. To achieve that all we need to do is call the &lt;code&gt;savePage&lt;/code&gt; method when the user executes the command:&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;//background.js&lt;/span&gt;

 &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;duplicate-tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;duplicateTab&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;barkTitle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Command &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not found`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;barkTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Acho&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;tab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveTab&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;tabTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;savePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 👈&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;p&gt;That's it!&lt;/p&gt;
&lt;h1&gt;
  
  
  The repo
&lt;/h1&gt;

&lt;p&gt;You can find this and all of the previous examples of this series in my repo:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pawap90" rel="noopener noreferrer"&gt;
        pawap90
      &lt;/a&gt; / &lt;a href="https://github.com/pawap90/acho-where-are-we" rel="noopener noreferrer"&gt;
        acho-where-are-we
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Acho (a cute pup) tells you the title of the current page on your browser. A sample chrome extension.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h1&gt;
  
  
  Let me know what you think! 💬
&lt;/h1&gt;

&lt;p&gt;Are you working on or have you ever built a Chrome extension? &lt;br&gt;
How do you manage data storage?&lt;/p&gt;

</description>
      <category>chromeextension</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>chrome</category>
    </item>
    <item>
      <title>Mastering NPM Scripts</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Mon, 15 Feb 2021 13:15:23 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/mastering-npm-scripts-2chd</link>
      <guid>https://dev.to/paulasantamaria/mastering-npm-scripts-2chd</guid>
      <description>&lt;p&gt;You may have come across the &lt;code&gt;scripts&lt;/code&gt; property in the &lt;code&gt;package.json&lt;/code&gt; file and even write some scripts yourself. But do you know all you can do with NPM Scripts? &lt;/p&gt;

&lt;p&gt;I've been using NPM Scripts for years, but I wanted to pass a parameter to a script a few weeks ago and realized &lt;em&gt;I didn't know how to do that&lt;/em&gt;. That's when I decided to learn everything I could about NPM scripts and write this article.&lt;/p&gt;

&lt;p&gt;In this article, I'll share my research about how to take full advantage of NPM scripts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Introduction

&lt;ul&gt;
&lt;li&gt;npm run&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Built-in scripts and Aliases&lt;/li&gt;
&lt;li&gt;Executing multiple scripts&lt;/li&gt;
&lt;li&gt;Understanding errors&lt;/li&gt;
&lt;li&gt;
Run scripts silently or loudly

&lt;ul&gt;
&lt;li&gt;About log levels&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Referencing scripts from files&lt;/li&gt;
&lt;li&gt;Pre &amp;amp; Post&lt;/li&gt;
&lt;li&gt;Access environment variables&lt;/li&gt;
&lt;li&gt;
Passing arguments

&lt;ul&gt;
&lt;li&gt;Arguments as environment variables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Naming conventions

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


&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;NPM Scripts are a &lt;strong&gt;set of built-in and custom scripts&lt;/strong&gt; defined in the &lt;code&gt;package.json&lt;/code&gt; file. Their goal is to provide a simple way to &lt;strong&gt;execute repetitive tasks&lt;/strong&gt;, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running a linter tool on your code&lt;/li&gt;
&lt;li&gt;Executing the tests&lt;/li&gt;
&lt;li&gt;Starting your project locally&lt;/li&gt;
&lt;li&gt;Building your project&lt;/li&gt;
&lt;li&gt;Minify or Uglify JS or CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also use these scripts in your CI/CD pipeline to simplify tasks like build and generate test reports.&lt;/p&gt;

&lt;p&gt;To define an NPM script, all you need to do is set its name and write the script in the &lt;code&gt;script&lt;/code&gt; property in your &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="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;"hello-world"&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;Hello World&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;It's important to notice that &lt;strong&gt;NPM makes all your dependencies' binaries available&lt;/strong&gt; in the scripts. So you can access them directly as if they were referenced in your PATH. Let's see it in an example:&lt;/p&gt;

&lt;p&gt;Instead of doing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/.bin/eslint ."&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;You can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&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;h2&gt;
  
  
  npm run
&lt;/h2&gt;

&lt;p&gt;Now all you need to do is run &lt;code&gt;npm run hello-world&lt;/code&gt; on the terminal from your project's root folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run hello-world

&lt;span class="s2"&gt;"Hello World"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also run &lt;code&gt;npm run&lt;/code&gt;, without specifying a script, to get a &lt;strong&gt;list of all available scripts&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; npm run

Scripts available in sample-project via `npm run-script`:
    hello-world
        echo "Hello World"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, &lt;code&gt;npm run&lt;/code&gt; prints both the name and the actual script for each script added to the &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;code&gt;npm run&lt;/code&gt; is an &lt;strong&gt;alias&lt;/strong&gt; for &lt;code&gt;npm run-script&lt;/code&gt;, meaning you could also use &lt;code&gt;npm run-script hello-world&lt;/code&gt;. In this article, we'll use &lt;code&gt;npm run &amp;lt;script&amp;gt;&lt;/code&gt; because it's shorter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Built-in scripts and Aliases
&lt;/h1&gt;

&lt;p&gt;In the previous example, we created a &lt;em&gt;custom script&lt;/em&gt; called &lt;code&gt;hello-world&lt;/code&gt;, but you should know that npm also supports some &lt;em&gt;built-in scripts&lt;/em&gt; such as &lt;code&gt;test&lt;/code&gt; and &lt;code&gt;start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Interestingly, unlike our custom scripts, these scripts can be executed using &lt;em&gt;aliases&lt;/em&gt;, making the complete command &lt;strong&gt;shorter and easier to remember&lt;/strong&gt;. For example, all of the following commands will run the &lt;code&gt;test&lt;/code&gt; script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run-script &lt;span class="nb"&gt;test
&lt;/span&gt;npm run &lt;span class="nb"&gt;test
&lt;/span&gt;npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to the &lt;code&gt;test&lt;/code&gt; command, all of the following will run the &lt;code&gt;start&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run-script start
npm run start
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For these built-in scripts to work, we need to define a script for them in the &lt;code&gt;package.json&lt;/code&gt;. Otherwise, they will fail. We can write the scripts just as any other script. Here's an example:&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;"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;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node app.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;"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;"jest ./test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hello-world"&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;Hello World&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;
  
  
  Executing multiple scripts
&lt;/h1&gt;

&lt;p&gt;We may want to &lt;strong&gt;combine&lt;/strong&gt; some of our scripts and run them together. To do that, we can use &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; or &lt;code&gt;&amp;amp;&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To run multiple scripts &lt;strong&gt;sequentially&lt;/strong&gt;, we use &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;. For example: &lt;code&gt;npm run lint &amp;amp;&amp;amp; npm test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To run multiple scripts &lt;strong&gt;in parallel&lt;/strong&gt;, we use &lt;code&gt;&amp;amp;&lt;/code&gt;. Example: &lt;code&gt;npm run lint &amp;amp; npm test&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This only works in Unix environments. In Windows, it'll run sequentially.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;So, for example, we could create a script that combines two other scripts, like so:&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;"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;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&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;"jest ./test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ci"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run lint &amp;amp;&amp;amp; npm test"&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;h1&gt;
  
  
  Understanding errors
&lt;/h1&gt;

&lt;p&gt;When a script finishes with a &lt;strong&gt;non-zero exit code&lt;/strong&gt;, it means an &lt;strong&gt;error&lt;/strong&gt; occurred while running the script, and the execution is terminated.&lt;/p&gt;

&lt;p&gt;That means we can purposefully end the execution of a script with an error by exiting with a non-zero exit code, like so:&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;"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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;This script will fail&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="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;When a script throws an error, we get a few other details, such as the error number &lt;code&gt;errno&lt;/code&gt; and the &lt;code&gt;code&lt;/code&gt;. Both can be useful for googling the error.&lt;/p&gt;

&lt;p&gt;And if we need more information, we can always access the complete log file. The path to this file is provided at the end of the error message. &lt;strong&gt;On failure, all logs are included in this file.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Run scripts silently or loudly
&lt;/h1&gt;

&lt;p&gt;Use &lt;code&gt;npm run &amp;lt;script&amp;gt; --silent&lt;/code&gt; to &lt;strong&gt;reduce logs&lt;/strong&gt; and to &lt;strong&gt;prevent the script from throwing an error&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;--silent&lt;/code&gt; flag (short for &lt;code&gt;--loglevel silent&lt;/code&gt;) can be helpful when you want to run a script that you know may fail, but you don't want it to throw an error. Maybe in a CI pipeline, you want your whole pipeline to keep running even when the &lt;code&gt;test&lt;/code&gt; command fails. &lt;/p&gt;

&lt;p&gt;It can also be used as &lt;code&gt;-s&lt;/code&gt;: &lt;code&gt;npm run &amp;lt;script&amp;gt; -s&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ If we don't want to get an error when the script &lt;em&gt;doesn't exists&lt;/em&gt;, we can use &lt;code&gt;--if-present&lt;/code&gt; instead: &lt;code&gt;npm run &amp;lt;script&amp;gt; --if-present&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  About log levels
&lt;/h2&gt;

&lt;p&gt;We saw how we can reduce logs using &lt;code&gt;--silent&lt;/code&gt;, but what about getting even &lt;strong&gt;more detailed logs&lt;/strong&gt;? Or something in between? &lt;/p&gt;

&lt;p&gt;There are different &lt;em&gt;log levels&lt;/em&gt;: "silent", "error", "warn", "notice", "http", "timing", "info", "verbose", "silly". The default is "notice". The log level determines &lt;strong&gt;which logs will be displayed&lt;/strong&gt; in the output. Any logs of a higher level than the currently defined will be shown. &lt;/p&gt;

&lt;p&gt;We can explicitly define which loglevel we want to use when running a command, using &lt;code&gt;--loglevel &amp;lt;level&amp;gt;&lt;/code&gt;. As we saw before, the &lt;code&gt;--silent&lt;/code&gt; flag is the same as using &lt;code&gt;--loglevel silent&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now, if we want to get more detailed logs, we'll need to use a higher level than the default ("notice"). For example: &lt;code&gt;--loglevel info&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;There are also short versions we can use to simplify the command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-s&lt;/code&gt;, &lt;code&gt;--silent&lt;/code&gt;, &lt;code&gt;--loglevel silent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-q&lt;/code&gt;, &lt;code&gt;--quiet&lt;/code&gt;, &lt;code&gt;--loglevel warn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt;, &lt;code&gt;--loglevel info&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-dd&lt;/code&gt;, &lt;code&gt;--verbose&lt;/code&gt;, &lt;code&gt;--loglevel verbose&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-ddd&lt;/code&gt;, &lt;code&gt;--loglevel silly&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So to get the highest level of detail we could use &lt;code&gt;npm run &amp;lt;script&amp;gt; -ddd&lt;/code&gt; or &lt;code&gt;npm run &amp;lt;script&amp;gt; --loglevel silly&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Referencing scripts from files
&lt;/h1&gt;

&lt;p&gt;You can execute scripts from files. This can be useful for especially &lt;em&gt;complex scripts&lt;/em&gt; that would be hard to read in the &lt;code&gt;package.json&lt;/code&gt; file. However, it doesn't add much value if your script is short and straightforward.&lt;/p&gt;

&lt;p&gt;Consider this example:&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;"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;"hello: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;"node scripts/helloworld.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;"hello:bash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash scripts/helloworld.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hello:cmd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd scripts &amp;amp;&amp;amp; helloworld.cmd"&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;We use &lt;code&gt;node &amp;lt;script-path.js&amp;gt;&lt;/code&gt; to execute JS files and &lt;code&gt;bash &amp;lt;script-path.sh&amp;gt;&lt;/code&gt; to execute bash files.&lt;/p&gt;

&lt;p&gt;Notice that you can't just call &lt;code&gt;scripts/helloworld.cmd&lt;/code&gt; for CMD and BAT files. You'll need to navigate to the folder using &lt;code&gt;cd&lt;/code&gt; first. Otherwise, you'll get an error from NPM.&lt;/p&gt;

&lt;p&gt;Another advantage of executing scripts from files is that, if the script is complex, it'll be easier to maintain in a separate file than in a single line inside the &lt;code&gt;package.json&lt;/code&gt; file. &lt;/p&gt;

&lt;h1&gt;
  
  
  Pre &amp;amp; Post
&lt;/h1&gt;

&lt;p&gt;We can create "pre" and "post" scripts for &lt;em&gt;any of our scripts&lt;/em&gt;, and NPM will automatically &lt;strong&gt;run them in order&lt;/strong&gt;. The only requirement is that the script's name, following the "pre" or "post" prefix, matches the main script. For example:&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;"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;"prehello"&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;--Preparing greeting&lt;/span&gt;&lt;span class="se"&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;"hello"&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;Hello World&lt;/span&gt;&lt;span class="se"&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;"posthello"&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;--Greeting delivered&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;If we execute &lt;code&gt;npm run hello&lt;/code&gt;, NPM will execute the scripts in this order: &lt;code&gt;prehello&lt;/code&gt;, &lt;code&gt;hello&lt;/code&gt;, &lt;code&gt;posthello&lt;/code&gt;. Which will result in the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; script-test@1.0.0 prehello
&amp;gt; echo "--Preparing greeting"

"--Preparing greeting"

&amp;gt; script-test@1.0.0 hello
&amp;gt; echo "Hello World"

"Hello World"

&amp;gt; script-test@1.0.0 posthello
&amp;gt; echo "--Greeting delivered"

"--Greeting delivered"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ If we run &lt;code&gt;prehello&lt;/code&gt; or &lt;code&gt;posthello&lt;/code&gt; individually, NPM &lt;strong&gt;&lt;em&gt;will not&lt;/em&gt;&lt;/strong&gt; automatically execute any other scripts. It only works if you run the "main" script, in this case, &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Access environment variables
&lt;/h1&gt;

&lt;p&gt;While executing an NPM Script, NPM makes available a set of &lt;em&gt;environment variables&lt;/em&gt; we can use. These environment variables are generated by taking data from NPM Configuration, the package.json, and other sources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration&lt;/strong&gt; parameters are put in the environment using the &lt;code&gt;npm_config_&lt;/code&gt; prefix. Here are a few examples:&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;"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;"config:loglevel"&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;Loglevel: $npm_config_loglevel&lt;/span&gt;&lt;span class="se"&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;"config:editor"&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;Editor: $npm_config_editor&lt;/span&gt;&lt;span class="se"&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;"config:useragent"&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;User Agent: $npm_config_user_agent&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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 see what we get after executing the above commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run config:loglevel
&lt;span class="c"&gt;# Output: "Loglevel: notice"&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run config:editor
&lt;span class="c"&gt;# Output: "Editor: notepad.exe"&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run config:useragent
&lt;span class="c"&gt;# Output: "User Agent: npm/6.13.4 node/v12.14.1 win32 x64"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ You can also run &lt;code&gt;npm config ls -l&lt;/code&gt; to get a &lt;strong&gt;list of all the configuration parameters&lt;/strong&gt; available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Similarly, &lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt; fields&lt;/strong&gt;, such as &lt;code&gt;version&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt;, are included with the &lt;code&gt;npm_package_&lt;/code&gt; prefix. Let's see a few examples:&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;"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;"package: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;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Main: $npm_package_main&lt;/span&gt;&lt;span class="se"&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;"package: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;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Name: $npm_package_name&lt;/span&gt;&lt;span class="se"&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;"package: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;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Version: $npm_package_version&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results from these commands will be something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run package:main
&lt;span class="c"&gt;# Output: "Main: app.js"&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run package:name
&lt;span class="c"&gt;# Output: "Name: npm-scripts-demo"&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run package:version
&lt;span class="c"&gt;# Output: "Version: 1.0.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can add &lt;strong&gt;your own environment variables&lt;/strong&gt; using the &lt;em&gt;&lt;code&gt;config&lt;/code&gt; field&lt;/em&gt; in your &lt;code&gt;package.json&lt;/code&gt; file. The values setup there will be added as environment variables using the &lt;code&gt;npm_package_config&lt;/code&gt; prefix.&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;"config"&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;"my-var"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Some value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1234&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;"script"&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;"packageconfig:port"&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;Port: $npm_package_config_port&lt;/span&gt;&lt;span class="se"&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;"packageconfig:myvar"&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;My var: $npm_package_config_my_var&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;If we execute both commands we'll get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run packageconfig:port
&lt;span class="c"&gt;# Output: "Port: 1234"&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run packageconfig:myvar
&lt;span class="c"&gt;# Output: "My var: Some value"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ In Windows' &lt;code&gt;cmd&lt;/code&gt; instead of &lt;code&gt;$npm_package_config_port&lt;/code&gt; you should use &lt;code&gt;%npm_package_config_port%&lt;/code&gt; to access the environment variables.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Passing arguments
&lt;/h1&gt;

&lt;p&gt;In some cases, you may want to pass some &lt;strong&gt;arguments&lt;/strong&gt; to your script. You can achieve that using &lt;code&gt;--&lt;/code&gt; that the end of the command, like so: &lt;code&gt;npm run &amp;lt;script&amp;gt; -- --argument="value"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's see a few examples:&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;"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;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&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;"jest ./test"&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;If I wanted to run only the tests that changed, I could do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--onlyChanged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if I wanted to run the linter and save the output in a file, I could execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run lint &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--output-file&lt;/span&gt; lint-result.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Arguments as environment variables
&lt;/h2&gt;

&lt;p&gt;Another way of &lt;strong&gt;passing arguments&lt;/strong&gt; is &lt;strong&gt;through environment variables&lt;/strong&gt;. Any key-value pairs we add to our script will be translated into an environment variable with the &lt;code&gt;npm_config&lt;/code&gt; prefix. Meaning we can create a script like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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;"hello"&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;Hello $npm_config_firstname!&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;And then use it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm run hello &lt;span class="nt"&gt;--firstname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Paula
&lt;span class="c"&gt;# Output: "Hello Paula"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Naming conventions
&lt;/h1&gt;

&lt;p&gt;There are no specific guidelines about how to name your scripts, but there are a few things we can keep in mind to make our scripts easier to pick up by other developers.&lt;/p&gt;

&lt;p&gt;Here's my take on the subject, based on my research:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep it &lt;strong&gt;short&lt;/strong&gt;: If you take a look at Svelte's NPM Scripts, you'll notice that most script names are &lt;em&gt;one word only&lt;/em&gt;. If we can manage to keep our script names short, it'll be easier to remember them when we need them.&lt;/li&gt;
&lt;li&gt;Be &lt;strong&gt;consistent&lt;/strong&gt;: You may need to use more than one word to name your script. In that case, choose a &lt;em&gt;naming style and stick to it&lt;/em&gt;. It can be camelCase, kebab-case, or anything you prefer. But avoid mixing them. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prefixes
&lt;/h2&gt;

&lt;p&gt;One convention that you may have seen is using a &lt;strong&gt;prefix and a colon to group scripts&lt;/strong&gt;, for example, "build:prod". This is simply a naming convention. It doesn't affect your scripts' behavior but can be helpful to create groups of scripts that are &lt;em&gt;easier to identify by their prefixes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Example:&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;"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;"lint:check"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&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:fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint . --fix"&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: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;"..."&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:prod"&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="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;h1&gt;
  
  
  Documentation
&lt;/h1&gt;

&lt;p&gt;Consider adding documentation for your scripts so other people can easily understand &lt;em&gt;how and when to use them&lt;/em&gt;. I like to add a few lines explaining each script on my Readme file.  &lt;/p&gt;

&lt;p&gt;The documentation for each available script should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Script name&lt;/li&gt;
&lt;li&gt;Description&lt;/li&gt;
&lt;li&gt;Accepted arguments (optional)&lt;/li&gt;
&lt;li&gt;Links to other documentation (optional): For example, if your script runs &lt;code&gt;tsc --build&lt;/code&gt;, you may want to include a link to Typescript docs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This is all I managed to dig up about NPM Scripts. I hope you find it useful! I certainly learned a lot just by doing this research. It took me way more time than I thought it would, but it was totally worth it.&lt;/p&gt;

&lt;p&gt;Let me know if there's anything missing that you'll like to add to make this guide even more complete! 💬&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>npm</category>
    </item>
    <item>
      <title>4 podcasts for aspiring entrepreneurs</title>
      <dc:creator>Paula Santamaría</dc:creator>
      <pubDate>Thu, 04 Feb 2021 11:53:46 +0000</pubDate>
      <link>https://dev.to/paulasantamaria/4-podcasts-for-aspiring-entrepreneurs-47hc</link>
      <guid>https://dev.to/paulasantamaria/4-podcasts-for-aspiring-entrepreneurs-47hc</guid>
      <description>&lt;p&gt;I love podcasts. I love them for many reasons, but the most important is that a few years ago, listening to podcasts helped me find the motivation I needed to start working on my own projects.&lt;/p&gt;

&lt;p&gt;I don't even remember how I first got into it, but somehow I started listening to podcasts about freelancing and entrepreneurship on my bus back home every day after work. Listening to hundreds of stories about different people who managed to make a living in unconventional ways and chasing their dreams was really inspiring and eye-opening. At that point in my life, I had already worked 5 years for a company and was realizing that I didn't want to do the typical 9 to 5 job. These podcasts were precisely the push I needed to move on.&lt;/p&gt;

&lt;p&gt;I still have a long way to go with my career, but I'm really happy with how it's developing so far. &lt;/p&gt;

&lt;p&gt;So I decided to write this post to share some of my favorite podcasts about entrepreneurship in the hope that they inspire you and give you the courage to keep working towards your dreams too:&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Hack the Entrepreneur
&lt;/h1&gt;

&lt;p&gt;I've been listening to HTE for years. I never get tired of it. Every episode, the host, Jon Nastor, &lt;strong&gt;interviews&lt;/strong&gt; an entrepreneur. The guests range between solo entrepreneurs and multimillion-dollar startup founders. During these ~40 minute interviews, Jon asks them about their growth, business, motivations, etc. &lt;/p&gt;

&lt;p&gt;I found this podcast to be not only inspiring but also really &lt;strong&gt;informative&lt;/strong&gt;. The guests are not always from the tech world, so you may think that their interviews won't give you as much value, but I realized it's the opposite. I learned a lot by listening to how people from different backgrounds manage their business.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hacktheentrepreneur.com/podcasts"&gt;Hack the Entrepreneur Website&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  2. She Did It Her Way
&lt;/h1&gt;

&lt;p&gt;I came across this podcast recently and was instantly hooked. Most episodes are between 15 and 25 minutes long, and every single minute is &lt;em&gt;full of valuable content&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;The host, Amanda Boleyn, talks about business and offers &lt;strong&gt;practical tips&lt;/strong&gt; on how to get your own business running. She also interviews other women entrepreneurs who talk about their stories, how they became entrepreneurs, and their businesses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shediditherway.com"&gt;She Did It Her Way Website&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  3. The Ground Up Show
&lt;/h1&gt;

&lt;p&gt;One of the things I got interested in when I started thinking about freelancing was &lt;em&gt;minimalism&lt;/em&gt;. Many freelancers, digital nomads, and entrepreneurs use minimalism to reduce their living costs and focus on what's important to them. &lt;/p&gt;

&lt;p&gt;Matt D'Avella was the filmmaker behind the Netflix documentary about minimalism and later started The Ground Up Show.&lt;/p&gt;

&lt;p&gt;In this podcast, Matt meets with creatives and interviews them about their content and their businesses. &lt;/p&gt;

&lt;p&gt;You can also watch the interviews on YouTube if you prefer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mattdavella.com/podcast"&gt;The Ground Up Show Website&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Company of One
&lt;/h1&gt;

&lt;p&gt;The Company of One podcast has only 21 episodes, and they're all great. What I love about this podcast it's how it shows you how business can succeed by &lt;em&gt;challenging the common belief that bigger is better.&lt;/em&gt; It's fascinating to see how small businesses can apply creative solutions to solve their issues and succeed.&lt;/p&gt;

&lt;p&gt;The host is Paul Jarvis, author of "Company of One: Why Staying Small Is the Next Big Thing for Business." Episodes range between 5 and 15 minutes long and will leave you thinking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ofone.co/company-of-one-podcast/"&gt;Company of One Podcast Website&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What podcasts do you listen to?
&lt;/h1&gt;

&lt;p&gt;I love to learn about new podcasts! Do you have any recommendations?&lt;/p&gt;

</description>
      <category>podcast</category>
      <category>career</category>
      <category>entrepreneurship</category>
    </item>
  </channel>
</rss>
