<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Dave is Coding]]></title><description><![CDATA[Dave is Coding]]></description><link>https://daveiscoding.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 18:42:48 GMT</lastBuildDate><atom:link href="https://daveiscoding.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Why do you need an init process inside your Docker container (PID 1)]]></title><description><![CDATA[When you run your application inside a Docker container, it will be assigned process identifier (PID) 1. This particular PID is special in the Unix world. PID 1 is assigned to the very first process that the kernel starts, therefore it takes a specia...]]></description><link>https://daveiscoding.com/why-do-you-need-an-init-process-inside-your-docker-container-pid-1</link><guid isPermaLink="true">https://daveiscoding.com/why-do-you-need-an-init-process-inside-your-docker-container-pid-1</guid><category><![CDATA[Docker]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Dávid Szabó]]></dc:creator><pubDate>Wed, 07 Sep 2022 19:34:48 GMT</pubDate><content:encoded><![CDATA[<p>When you run your application inside a Docker container, it will be assigned process identifier (PID) 1. This particular PID is special in the Unix world. PID 1 is assigned to the very first process that the kernel starts, therefore it takes a special role in the system.</p>
<h2 id="heading-why-is-pid-1-so-special">Why is PID 1 so special?</h2>
<ul>
<li><p>The default action is ignored when receiving signals, which means your process will not implicitly terminate on <code>SIGINT</code>or <code>SIGTERM</code>. Usually, operating systems terminate your process when processes receive <code>SIGINT</code>or <code>SIGTERM</code>.</p>
</li>
<li><p>Any orphaned process is adopted by PID 1.</p>
</li>
</ul>
<p>Let's discuss these more in-depth, especially in the Docker world.</p>
<h3 id="heading-signals">Signals</h3>
<p>Do you know what actually happens behind the scenes when you run <code>docker stop</code>?</p>
<p>The main process (PID 1) inside the container will receive <code>SIGTERM</code>, and after a grace period, <code>SIGKILL</code> signal.</p>
<p>By default, Docker waits 10 seconds after <code>SIGTERM</code> before killing it with <code>SIGKILL</code>.</p>
<p>Did you ever have a container that took a long time to stop? Did it take 10 seconds accurately? That means your application doesn't handle signals explicitly!</p>
<p>Here is a simple command that spins up a Node.js container that runs forever:</p>
<pre><code class="lang-bash">docker run \
  -d \
  --rm \
  --name node-app \
  node:alpine \
  node -e <span class="hljs-string">"setInterval(() =&gt; {}, 1000);"</span>
</code></pre>
<p>Now stop the container and measure the time:</p>
<pre><code class="lang-bash">time docker stop node-app
</code></pre>
<p>On my system, it took 10.644 seconds to stop the container.</p>
<pre><code class="lang-text">real    0m10.644s
user    0m0.030s
sys     0m0.062s
</code></pre>
<p>It means the container couldn't be stopped gracefully, it had to be killed.</p>
<p>This is all because your Node.js application runs as PID 1, which doesn't run the default actions of signals, which would be process termination in this case.</p>
<p>Now let's handle the <code>SIGTERM</code> signal in the Node.js application:</p>
<pre><code class="lang-bash">docker run \
  -d \
  --rm \
  --name node-app \
  node:alpine \
  node -e <span class="hljs-string">"
      const interval = setInterval(() =&gt; {}, 1000); 
      process.on('SIGTERM', () =&gt; clearInterval(interval));
  "</span>
</code></pre>
<p>When <code>SIGTERM</code> is received, the interval will be stopped, so the process will exit because there are no things to do.</p>
<p>Stop the container again:</p>
<pre><code class="lang-bash">time docker stop node-app
</code></pre>
<p>On my system, it took about half a second this time.</p>
<pre><code class="lang-plaintext">real    0m0.613s
user    0m0.000s
sys     0m0.060s
</code></pre>
<p>This means the Node.js application has been gracefully stopped instead of being killed after the 10 seconds timeout.</p>
<h3 id="heading-orphaned-processes">Orphaned processes</h3>
<p>When a process dies, all of its children become an orphan and are adopted by PID 1. Now it's more interesting what happens when that child finishes its execution or dies for whatever reason.</p>
<p>Let's run a command that showcases orphaned processes. The following command will launch an Ubuntu container and runs the <code>sh -c "sleep 10 &amp; exec sleep 1000"</code> command. This command creates a shell and executes <code>sleep 10 &amp; exec sleep 1000</code>. The shell creates <code>sleep 10</code> process in the background and replaces the shell via <code>exec</code> with <code>sleep 1000</code>. Therefore PID 1, in the beginning, is <code>sh</code>, but <code>sleep 1000</code> takes its place.</p>
<pre><code class="lang-bash">docker run -d --rm --name orph ubuntu sh -c <span class="hljs-string">"sleep 10 &amp; exec sleep 1000"</span>
</code></pre>
<p>Right after running the command, print out the processes in the container:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> orph ps -eaf
</code></pre>
<p>If you run it in 10 seconds, you should see a similar output:</p>
<pre><code class="lang-text">UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 16:34 ?        00:00:00 sleep 1000
root         7     1  0 16:34 ?        00:00:00 sleep 10
</code></pre>
<p>As you can see, <code>sh</code> doesn't exist because <code>sleep 1000</code> replaced it.</p>
<p>Now wait until <code>sleep 10</code> finishes, and print out the processes again:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> orph ps -eaf
</code></pre>
<p>This time you should see a different output:</p>
<pre><code class="lang-text">UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 16:34 ?        00:00:00 sleep 1000
root         7     1  0 16:34 ?        00:00:00 [sleep] &lt;defunct&gt;
</code></pre>
<p>As you can see, <code>sleep 10</code> finished, and it became <strong>defunct</strong>. Terminated processes are supposed to be cleaned up by their parent processes. This process's parent terminated, and the current PID 1 process doesn't take care of this process because it doesn't know about it - and <code>sleep</code> doesn't handle child processes. These processes are called <strong>zombie processes</strong>.</p>
<p>In normal circumstances, you should never see zombie processes in your process list. PID 1 should take care of removing zombie processes from the process table.</p>
<h2 id="heading-using-init">Using init</h2>
<p>When you boot up a Unix-based operating system, the PID 1 will be an init process. This process takes care of reaping the zombie processes throughout your system's uptime.</p>
<p>Since orphaned processes are always adopted by PID 1 - the init process -, it can take care of those zombie processes easily.</p>
<p>In a Docker container, an init process should also take care of forwarding the signals to your application as well.</p>
<p>Let's rewrite the previous command a little bit:</p>
<pre><code class="lang-bash"> docker run -d --rm --name orph ubuntu sh -c <span class="hljs-string">"sh -c 'sleep 10 &amp; exec sleep 1' &amp; exec sleep 1000"</span>
</code></pre>
<p>This command will produce the following process list:</p>
<pre><code class="lang-text">UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 21:42 ?        00:00:00 sleep 1000
root         7     1  0 21:42 ?        00:00:00 sleep 1
root         8     7  0 21:42 ?        00:00:00 sleep 10
</code></pre>
<p><code>sleep 1000</code> becomes PID 1, <code>sleep 1</code> is a child of <code>sleep 1000</code> and <code>sleep 10</code> is a child of <code>sleep 1</code>.</p>
<p><code>sleep 1</code> immediately finishes, then you will see the following process list:</p>
<pre><code class="lang-text">UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 21:42 ?        00:00:00 sleep 1000
root         7     1  0 21:42 ?        00:00:00 [sleep] &lt;defunct&gt;
root         8     1  0 21:42 ?        00:00:00 sleep 10
</code></pre>
<p>As you can see, <code>sleep 10</code> was adopted by <code>sleep 1000</code>. Because <code>sleep 1</code> terminated, which also became a zombie process.</p>
<p>After 10 seconds, when <code>sleep 10</code> finishes, it becomes a zombie process as well:</p>
<pre><code class="lang-text">UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 18:59 ?        00:00:00 sleep 1000
root         7     1  0 18:59 ?        00:00:00 [sleep] &lt;defunct&gt;
root         8     1  0 18:59 ?        00:00:00 [sleep] &lt;defunct&gt;
</code></pre>
<p>This time use the <code>--init</code> flag. This flag will boot up <a target="_blank" href="https://github.com/krallin/tini">Tini</a>, a lightweight init system in the container as PID 1.</p>
<pre><code class="lang-bash"> docker run --init -d --rm --name orph ubuntu sh -c <span class="hljs-string">"sh -c 'sleep 10 &amp; exec sleep 1' &amp; exec sleep 1000"</span>
</code></pre>
<p>You will immediately see that <code>sleep 1</code> became a zombie process and <code>sleep 10</code> is adopted by PID 1:</p>
<pre><code class="lang-text">UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 21:45 ?        00:00:00 /sbin/docker-init -- sh -c sh -c 'sleep 10 &amp; exec sleep 1' &amp; exec sleep 1000
root         7     1  0 21:45 ?        00:00:00 sleep 1000
root         8     7  0 21:45 ?        00:00:00 [sleep] &lt;defunct&gt;
root         9     1  0 21:45 ?        00:00:00 sleep 10
</code></pre>
<p>As you can see, <code>sleep 1</code> is a zombie process and stays as-is until <code>sleep 1000</code> finishes. This is because <code>sleep</code> doesn't take care of cleaning up child processes.</p>
<p>After 10 seconds, <code>sleep 10</code> is terminated and disappears from the process list.</p>
<pre><code class="lang-text">UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 21:45 ?        00:00:00 /sbin/docker-init -- sh -c sh -c 'sleep 10 &amp; exec sleep 1' &amp; exec sleep 1000
root         7     1  0 21:45 ?        00:00:00 sleep 1000
root         8     7  0 21:45 ?        00:00:00 [sleep] &lt;defunct&gt;
</code></pre>
<p>This proves that <code>tini</code> takes care of reaping zombie processes.</p>
<p>Now to prove that <code>tini</code> forwards signals, let's stop the docker container:</p>
<pre><code class="lang-bash">docker stop orph
</code></pre>
<p>The container stopped immediately, instead of waiting 10 seconds. Therefore signal forwarding is proven as well.</p>
<h2 id="heading-why-is-this-important-at-all">Why is this important at all?</h2>
<p>Zombie processes reserve the process ID until they are properly cleaned up. Your operating system has a finite amount of process IDs. Therefore they can fill up the process table, and the chaos begins!</p>
<p>This isn't an issue if you are not starting new processes from your application. If you don't have child processes, it's impossible to have zombie processes. Although I still recommend using <code>tini</code> because it's available in Docker by default, and it's easy to switch on for both individual Docker commands (<code>--init</code>) and Docker Compose services (<code>init: true</code>).</p>
<h2 id="heading-example-nodejs-project">Example Node.js project</h2>
<p>A repository is available at <a target="_blank" href="https://github.com/david-szabo97/node-docker-init-or-not-to-init">GitHub/david-szabo97/node-docker-init-or-not-to-init</a>, which showcases zombie processes in a single command.</p>
]]></content:encoded></item><item><title><![CDATA[Separate Node.js unit, integration, and E2E tests using Jest projects]]></title><description><![CDATA[The classic test pyramid is made up of the unit, integration, and end-to-end tests. Unit tests are supposed to run fast after each change in your IDE and give immediate feedback. In contrast, integration and E2E tests are slow and are run on-demand. ...]]></description><link>https://daveiscoding.com/separate-nodejs-unit-integration-and-e2e-tests-using-jest-projects</link><guid isPermaLink="true">https://daveiscoding.com/separate-nodejs-unit-integration-and-e2e-tests-using-jest-projects</guid><category><![CDATA[Jest]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Dávid Szabó]]></dc:creator><pubDate>Mon, 22 Aug 2022 17:31:24 GMT</pubDate><content:encoded><![CDATA[<p>The classic test pyramid is made up of the unit, integration, and end-to-end tests. Unit tests are supposed to run fast after each change in your IDE and give immediate feedback. In contrast, integration and E2E tests are slow and are run on-demand. Sometimes you want to run all your tests, sometimes you want to run your E2E tests and nothing else. Fortunately, Jest provides a feature called projects that solves this problem relatively easily.</p>
<p>Whatever test pyramid you follow, you will always have faster-slower tests. So let's figure out how to separate them using Jest projects.</p>
<h2 id="heading-initialize-the-project">Initialize the project</h2>
<p>Create an empty directory and initialize your Node.js project:</p>
<pre><code class="lang-bash">mkdir jest-projects
<span class="hljs-built_in">cd</span> jest-projects
npm init -y
</code></pre>
<p>Of course, you will need Jest to run your tests, so install it as a development dependency:</p>
<pre><code class="lang-bash">npm install -D jest
</code></pre>
<p>By default, Jest treats any JS file in the <code>__test__</code> directory as a test file, and Jest will try to run it. First, create the directory and create a <code>test.js</code> file.</p>
<pre><code class="lang-bash">mkdir __tests__
touch __tests__/test.js
</code></pre>
<p>Add a simple test </p>
<p><code>__tests__/test.js</code></p>
<pre><code>it(<span class="hljs-string">"test works"</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  expect(<span class="hljs-number">1</span> <span class="hljs-operator">+</span> <span class="hljs-number">1</span>).toBe(<span class="hljs-number">2</span>);
});
</code></pre><p>Now ask Jest to run the test:</p>
<pre><code class="lang-bash">npx jest
</code></pre>
<p>You should see a successful test run.</p>
<pre><code class="lang-text"> PASS  __tests__/test.js
  √ test works (2 ms)

Test Suites: 1 passed, 1 total                                                                                                                                                                                                                                                                                                                                                                                        
Tests:       1 passed, 1 total                                                                                                                                                                                                                                                                                                                                                                                        
Snapshots:   0 total
Time:        1.264 s
Ran all test suites.
</code></pre>
<h2 id="heading-integration-and-e2e-tests">Integration and E2E tests</h2>
<p>Right now, Jest runs all the tests in the <code>__tests__</code> folder. So let's create a separate directory for unit, integration, and E2E tests.</p>
<pre><code class="lang-bash">mkdir __tests__/unit
mkdir __tests__/integration
mkdir __tests__/e2e
</code></pre>
<p>Move the existing <code>test.js</code> file into the <code>unit</code> folder and copy-paste the file to the <code>integration</code> and <code>e2e</code> directory.</p>
<p>At this point, you should have the following directory tree:</p>
<pre><code class="lang-text">├── __tests__
│   ├── e2e
│   │   └── test.js
│   ├── integration
│   │   └── test.js
│   └── unit
│       └── test.js
├── node_modules
├── package-lock.json
└── package.json
</code></pre>
<p>If you run Jest, you will see that all tests are run by default.</p>
<pre><code class="lang-text">$ npx jest
 PASS  __tests__/integration/test.js
 PASS  __tests__/e2e/test.js
 PASS  __tests__/unit/test.js

Test Suites: 3 passed, 3 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        1.043 s
Ran all test suites.
</code></pre>
<h2 id="heading-configure-jest-projects">Configure Jest projects</h2>
<p>You will need four different configuration files. One configuration file will be the container file that references all your Jest projects. Rest three files are for unit, integration, and e2e projects.</p>
<p><code>jest.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">projects</span>: [
    <span class="hljs-string">"&lt;rootDir&gt;/jest.unit.config.js"</span>,
    <span class="hljs-string">"&lt;rootDir&gt;/jest.integration.config.js"</span>,
    <span class="hljs-string">"&lt;rootDir&gt;/jest.e2e.config.js"</span>,
  ],
};
</code></pre>
<p><code>jest.unit.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">displayName</span>: <span class="hljs-string">"unit"</span>,
  <span class="hljs-attr">testMatch</span>: [<span class="hljs-string">"**/__tests__/**/unit/**/*.[jt]s?(x)"</span>],
};
</code></pre>
<p><code>jest.integration.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">displayName</span>: <span class="hljs-string">"integration"</span>,
  <span class="hljs-attr">testMatch</span>: [<span class="hljs-string">"**/__tests__/**/integration/**/*.[jt]s?(x)"</span>],
};
</code></pre>
<p><code>jest.e2e.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">displayName</span>: <span class="hljs-string">"e2e"</span>,
  <span class="hljs-attr">testMatch</span>: [<span class="hljs-string">"**/__tests__/**/e2e/**/*.[jt]s?(x)"</span>],
};
</code></pre>
<p>As you can see, these configuration files are very similar, except for the main configuration file that references the projects. These configuration files specify a display name and a glob pattern to match the test files.</p>
<p>Now, run your tests</p>
<pre><code class="lang-text">$ npx jest
 PASS   integration  __tests__/integration/test.js
 PASS   e2e  __tests__/e2e/test.js
 PASS   unit  __tests__/unit/test.js

Test Suites: 3 passed, 3 total                                                                                                                                   
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        0.755 s, estimated 1 s
Ran all test suites in 3 projects.
</code></pre>
<p>Jest successfully recognized the three separate projects. You can see the display name of the project next to the file path.</p>
<p>What if you want to run specific tests? Jest CLI provides many options, but in this case, you should be interested in <code>--selectProjects</code> and <code>--ignoreProjects</code>.</p>
<p>Let's run e2e tests only</p>
<pre><code class="lang-text">$ npx jest --selectProjects e2e
Running one project: e2e
 PASS   e2e  __tests__/e2e/test.js
  √ test works (1 ms)

Test Suites: 1 passed, 1 total                                                                                                                                   
Tests:       1 passed, 1 total                                                                                                                                   
Snapshots:   0 total
Time:        0.363 s, estimated 1 s
Ran all test suites.
</code></pre>
<p>Or run all tests, except e2e tests</p>
<pre><code class="lang-text">$ npx jest --ignoreProjects e2e
Running 2 projects:
- integration
- unit
 PASS   integration  __tests__/integration/test.js
 PASS   unit  __tests__/unit/test.js

Test Suites: 2 passed, 2 total                                                                                                                                   
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.559 s, estimated 1 s
Ran all test suites in 2 projects.
</code></pre>
<h2 id="heading-npm-scripts">NPM scripts</h2>
<p>You will probably get tired of typing the Jest commands all the time. So write a few NPM scripts so your fingers can get some rest.</p>
<p><strong>package.json</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"jest-projects"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"jest"</span>,
    <span class="hljs-attr">"test:unit"</span>: <span class="hljs-string">"jest --selectProjects unit"</span>,
    <span class="hljs-attr">"test:integration"</span>: <span class="hljs-string">"jest --selectProjects integration"</span>,
    <span class="hljs-attr">"test:e2e"</span>: <span class="hljs-string">"jest --selectProjects e2e"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"jest"</span>: <span class="hljs-string">"^28.1.3"</span>
  }
}
</code></pre>
<p>This will provide you with four scripts:</p>
<ul>
<li><code>npm test</code>- Runs all tests (unit, integration and E2E)</li>
<li><code>npm run test:unit</code> - Runs unit tests only</li>
<li><code>npm run test:integration</code> - Runs integration tests only</li>
<li><code>npm run test:e2e</code> - Runs E2E tests only</li>
</ul>
<h2 id="heading-global-setup-and-teardown-modules">Global setup and teardown modules</h2>
<p>Most of the time, integration and E2E tests require some kind of setup and teardown before the test suite. Jest provides <code>globalSetup</code> and <code>globalTeardown</code> options in the configuration file. Let's set it up for the E2E tests.</p>
<p><code>jest.e2e.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">displayName</span>: <span class="hljs-string">"e2e"</span>,
  <span class="hljs-attr">testMatch</span>: [<span class="hljs-string">"**/__tests__/**/e2e/**/*.[jt]s?(x)"</span>],
  <span class="hljs-attr">testPathIgnorePatterns</span>: [
    <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_setup.js"</span>,
    <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_teardown.js"</span>,
  ],
  <span class="hljs-attr">globalSetup</span>: <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_setup.js"</span>,
  <span class="hljs-attr">globalTeardown</span>: <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_teardown.js"</span>,
};
</code></pre>
<p>This config will tell Jest to run the <code>__tests__/e2e/_setup.js</code> before all E2E test suites. And run <code>&lt;rootDir&gt;/__tests__/e2e/_teardown.js</code> after all E2E test suites.</p>
<p>Now, you might wonder why <code>testPathIgnorePatterns</code> contains the <code>_setup</code> and <code>_teardown</code> files. Take a look at the <code>testMatch</code>'s value. It matches any <code>.js</code>, <code>.ts</code>, <code>.jsx</code> and <code>.tsx</code> files in any <code>e2e</code> directory. Therefore this configuration simply matches the setup and teardown files as well. If you don't ignore them, Jest will complain that these files don't contain any tests.</p>
<p>Create the setup and teardown modules</p>
<p><code>__tests__/e2e/_setup.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"E2E setup"</span>);
};
</code></pre>
<p><code>__tests__/e2e/_teardown.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"E2E teardown"</span>);
};
</code></pre>
<p>Now, if you run your E2E tests, you will see the messages in your terminal</p>
<pre><code class="lang-text">$ npm run test:e2e

&gt; jest-projects@1.0.0 test:e2e
&gt; jest --selectProjects e2e

Running one project: e2e
Determining test suites to run...E2E setup
 PASS   e2e  __tests__/e2e/test.js
  √ test works (1 ms)

Test Suites: 1 passed, 1 total                                                                                                                                   
Tests:       1 passed, 1 total                                                                                                                                   
Snapshots:   0 total
Time:        0.336 s, estimated 1 s
Ran all test suites.
E2E teardown
</code></pre>
<h2 id="heading-tests-by-filename-suffixes">Tests by filename suffixes</h2>
<p>You might prefer a different setup for your test files. Some projects use different directories, some projects colocate tests, and some projects just use naming conventions to differentiate between tests.</p>
<p>Previously, you separated your tests by using different directories. Now let's assume you want to do the following:</p>
<ul>
<li><code>my-test.unit.js</code> - should be in the <code>unit</code> project</li>
<li><code>my-test.integration.js</code> - should be in the <code>integration</code> project</li>
<li><code>my-test.e2e.js</code> - should be in the <code>e2e</code> project</li>
</ul>
<p>Create an empty <code>lib</code> directory, this is where you will store your source code files:</p>
<pre><code class="lang-bash">mkdir lib
</code></pre>
<p>Write a simple <code>add</code> function:</p>
<p><code>lib/add.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports.add = <span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a + b;
</code></pre>
<p>Create a unit, integration, and E2E test file for this simple <code>add</code> function.</p>
<p><code>lib/add.unit.js</code></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { add } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./add"</span>);

it(<span class="hljs-string">"add(1, 1) should return 2"</span>, <span class="hljs-function">() =&gt;</span> {
  expect(add(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>)).toBe(<span class="hljs-number">2</span>);
});
</code></pre>
<p><code>lib/add.int.js</code></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { <span class="hljs-built_in">setTimeout</span> } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"node:timers/promises"</span>);
<span class="hljs-keyword">const</span> { add } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./add"</span>);

it(<span class="hljs-string">"add(1, 1) should return 2 (1s delay)"</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-number">1000</span>);
  expect(add(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>)).toBe(<span class="hljs-number">2</span>);
});
</code></pre>
<p><code>lib/add.e2e.js</code></p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { <span class="hljs-built_in">setTimeout</span> } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"node:timers/promises"</span>);
<span class="hljs-keyword">const</span> { add } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./add"</span>);

it(<span class="hljs-string">"add(1, 1) should return 2 (3s delay)"</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-number">3000</span>);
  expect(add(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>)).toBe(<span class="hljs-number">2</span>);
});
</code></pre>
<p>Integration and E2E tests include a delay to simulate the slowness of these tests.</p>
<p>At this point, if you try to run any of your test commands, you notice that none of these are running any of the new tests. Let's modify the configuration files to run colocated test files as well in the <code>lib</code> directory.</p>
<p><code>jest.unit.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">displayName</span>: <span class="hljs-string">"unit"</span>,
  <span class="hljs-attr">testMatch</span>: [
    <span class="hljs-string">"**/__tests__/**/unit/**/*.[jt]s?(x)"</span>,
    <span class="hljs-string">"&lt;rootDir&gt;/lib/**/*.unit.[jt]s?(x)"</span>,
  ],
};
</code></pre>
<p><code>jest.integration.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">displayName</span>: <span class="hljs-string">"integration"</span>,
  <span class="hljs-attr">testMatch</span>: [
    <span class="hljs-string">"**/__tests__/**/integration/**/*.[jt]s?(x)"</span>,
    <span class="hljs-string">"&lt;rootDir&gt;/lib/**/*.int.[jt]s?(x)"</span>,
  ],
};
</code></pre>
<p><code>jest.e2e.config.js</code></p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">displayName</span>: <span class="hljs-string">"e2e"</span>,
  <span class="hljs-attr">testMatch</span>: [
    <span class="hljs-string">"**/__tests__/**/e2e/**/*.[jt]s?(x)"</span>,
    <span class="hljs-string">"&lt;rootDir&gt;/lib/**/*.e2e.[jt]s?(x)"</span>,
  ],
  <span class="hljs-attr">testPathIgnorePatterns</span>: [
    <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_setup.js"</span>,
    <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_teardown.js"</span>,
  ],
  <span class="hljs-attr">globalSetup</span>: <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_setup.js"</span>,
  <span class="hljs-attr">globalTeardown</span>: <span class="hljs-string">"&lt;rootDir&gt;/__tests__/e2e/_teardown.js"</span>,
};
</code></pre>
<p>Each configuration has a new element in the <code>testMatch</code> array. Each matches its respective desired file naming format. Note that in this case <code>&lt;rootDir&gt;/lib</code> is used. So the <code>lib</code> directory must reside in the root directory of your project to be matched. Whereas <code>**/__tests__</code> matches nested <code>___tests__</code> directories as well.</p>
<p>Now, let's test what you have worked on</p>
<pre><code class="lang-text">$ npm test

&gt; jest-projects@1.0.0 test
&gt; jest

Determining test suites to run...E2E setup
 PASS   unit  __tests__/unit/test.js
 PASS   integration  __tests__/integration/test.js
 PASS   e2e  __tests__/e2e/test.js                                                                                                                               
 PASS   unit  lib/add.unit.js                                                                                                                                    
 PASS   integration  lib/add.int.js
 PASS   e2e  lib/add.e2e.js

Test Suites: 6 passed, 6 total
Tests:       6 passed, 6 total
Snapshots:   0 total
Time:        4.086 s
Ran all test suites in 3 projects.
E2E teardown
</code></pre>
<p>Jest now runs the tests in the <code>lib</code> directory without any issues! The test took 4 seconds due to delays in the new integration and E2E tests.</p>
<p>Now try to run unit tests only</p>
<pre><code class="lang-text">$ npm run test:unit

&gt; jest-projects@1.0.0 test:unit
&gt; jest --selectProjects unit

Running one project: unit
 PASS   unit  lib/add.unit.js
 PASS   unit  __tests__/unit/test.js

Test Suites: 2 passed, 2 total                                                                                                                                   
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.542 s, estimated 1 s
Ran all test suites.
</code></pre>
<p>It took half a second, and no integration and E2E tests were run. Looks good!</p>
<h2 id="heading-you-are-done-great-job">You are done! Great job!</h2>
<p>The project's source code is available on <a target="_blank" href="https://github.com/david-szabo97/example-nodejs-jest-projects">GitHub</a>.</p>
<p>Check out the official documentation to learn more about Jest projects:</p>
<ul>
<li><a target="_blank" href="https://jestjs.io/docs/configuration#projects-arraystring--projectconfig">Jest <code>projects</code> documentation</a></li>
<li><a target="_blank" href="https://jestjs.io/docs/cli#--selectprojects-project1--projectn">Jest CLI <code>selectProjects</code> option</a></li>
<li><a target="_blank" href="https://jestjs.io/docs/cli#--ignoreprojects-project1--projectn">Jest CLI <code>ignoreProjects</code> option</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Node.js TypeScript monorepo via NPM workspaces]]></title><description><![CDATA[Monorepos are all the rage right now. Modern projects are all using NX to set up a monorepo. But why would you introduce such a complex tool into your tech stack when something simple is often enough?
Both Yarn and NPM include workspace management in...]]></description><link>https://daveiscoding.com/nodejs-typescript-monorepo-via-npm-workspaces</link><guid isPermaLink="true">https://daveiscoding.com/nodejs-typescript-monorepo-via-npm-workspaces</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[npm]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[monorepo]]></category><dc:creator><![CDATA[Dávid Szabó]]></dc:creator><pubDate>Sun, 14 Aug 2022 11:58:19 GMT</pubDate><content:encoded><![CDATA[<p>Monorepos are all the rage right now. Modern projects are all using <a target="_blank" href="https://nx.dev/">NX</a> to set up a monorepo. But why would you introduce such a complex tool into your tech stack when something simple is often enough?</p>
<p>Both Yarn and NPM include workspace management in their feature-set. Thus you can manage multiple projects in one workspace. In addition, one of the tools is always available in your project, so why not use it?</p>
<h2 id="heading-the-fantastic-project">The fantastic project</h2>
<p>You are working on a fantastic project that you happened to name <code>fantastic</code>. How creative, isn't it?</p>
<p><code>fantastic</code> is a command line application that will showcase how to set up a TypeScript monorepo using NPM workspaces. The <code>fantastic</code> project was a massive success as a CLI application, and many users wanted to have a graphical user interface to use it. So you decided to create a web interface. Your code currently lives in a single module containing the core logic and the CLI entry-point.</p>
<p>Therefore you decided to separate the project into three separate packages:</p>
<ul>
<li><code>core</code> - this package contains the core logic of your <code>fantastic</code> project</li>
<li><code>web</code> - provides a web interface that interacts with the <code>core</code> package</li>
<li><code>cli</code> - provides a command line interface that interacts with the <code>core</code> package</li>
</ul>
<h3 id="heading-initialize-the-project">Initialize the project</h3>
<p>Let's create an empty directory and initialize an NPM package:</p>
<pre><code class="lang-bash">mkdir fantastic
<span class="hljs-built_in">cd</span> fantastic
npm init -y
</code></pre>
<p>Now create the packages:</p>
<pre><code class="lang-bash">npm init -y --scope @fantastic -w packages/core
npm init -y --scope @fantastic -w packages/web
npm init -y --scope @fantastic -w packages/cli
</code></pre>
<p>Define the dependencies between the packages:</p>
<pre><code class="lang-bash">npm install @fantastic/core -w @fantastic/web
npm install @fantastic/core -w @fantastic/cli
</code></pre>
<h3 id="heading-test-it">Test it!</h3>
<p>Now that we have the foundation in place, let's add some code to test it:</p>
<p><strong>packages/core/index.js</strong></p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello from Core!"</span>);
</code></pre>
<p><strong>packages/web/index.js</strong></p>
<pre><code class="lang-js"><span class="hljs-built_in">require</span>(<span class="hljs-string">"@fantastic/core"</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello from Web!"</span>);
</code></pre>
<p><strong>packages/cli/index.js</strong></p>
<pre><code class="lang-js"><span class="hljs-built_in">require</span>(<span class="hljs-string">"@fantastic/core"</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hello from CLI!"</span>);
</code></pre>
<p>Running the CLI outputs the following:</p>
<pre><code class="lang-bash">node packages/cli/index.js
</code></pre>
<pre><code class="lang-text">Hello from Core!
Hello from CLI!
</code></pre>
<p>This confirms that the setup is working fine.</p>
<h3 id="heading-here-comes-typescript">Here comes TypeScript</h3>
<p>Time to turn this project from JavaScript to TypeScript!</p>
<p>First of all, install <code>typescript</code> as a dev dependency in the workspace project:</p>
<pre><code class="lang-bash">npm install -D typescript
</code></pre>
<p>Every package requires its own <code>tsconfig.json</code> file. Since the <code>fantastic</code> project uses the same configuration for all the three packages, create a common <code>tsconfig.base.json</code> file in the root directory.</p>
<p><strong>tsconfig.base.json</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"incremental"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"es2020"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"commonjs"</span>,
    <span class="hljs-attr">"declaration"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"sourceMap"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"node"</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span>,

    <span class="hljs-attr">"composite"</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<p>This is just a typical <code>tsconfig.json</code> file, except for one compiler option: <code>composite</code>. This option makes it possible for TypeScript to determine if a project has been built yet quickly.</p>
<p>Now you have a common TS config file, but you aren't using it yet. Create a <code>tsconfig.json</code> file in each package's root directory:</p>
<p><strong>packages/core/tsconfig.json</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"../../tsconfig.base.json"</span>
}
</code></pre>
<p>The <code>cli</code> and <code>web</code> package is a bit different. You need to list out all your dependencies in the <code>references</code> property:</p>
<p><strong>packages/cli/tsconfig.json</strong> and <strong>packages/web/tsconfig.json</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"../../tsconfig.base.json"</span>,
  <span class="hljs-attr">"references"</span>: [{ <span class="hljs-attr">"path"</span>: <span class="hljs-string">"../core"</span> }]
}
</code></pre>
<p>Now that independent packages are setup, create the <code>tsconfig.json</code> in the root directory:</p>
<p><strong>tsconfig.json</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"files"</span>: [],
  <span class="hljs-attr">"references"</span>: [
    { <span class="hljs-attr">"path"</span>: <span class="hljs-string">"packages/core"</span> },
    { <span class="hljs-attr">"path"</span>: <span class="hljs-string">"packages/cli"</span> },
    { <span class="hljs-attr">"path"</span>: <span class="hljs-string">"packages/web"</span> }
  ]
}
</code></pre>
<p>Empty <code>files</code> array tells TypeScript to ignore all files except those in the references.</p>
<p>Rename all your <code>.js</code> files to <code>.ts</code> and replace <code>require</code> with <code>import</code>:</p>
<pre><code class="lang-js"><span class="hljs-built_in">require</span>(<span class="hljs-string">"@fantastic/core"</span>);
</code></pre>
<p>to</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> <span class="hljs-string">"@fantastic/core"</span>;
</code></pre>
<p>You are ready to compile:</p>
<pre><code class="lang-bash">npx tsc --build
</code></pre>
<p><code>--build</code> flag is required because the project contains multiple projects.</p>
<p>Now that you are done with all these changes, test your app again:</p>
<pre><code class="lang-bash">$ node packages/cli/index.js
Hello from Core!
Hello from CLI!

$ node packages/web/index.js
Hello from Core!
Hello from Web!
</code></pre>
<h3 id="heading-separate-source-code-and-build-output">Separate source code and build output</h3>
<p>First of all, remove all the build outputs from the previous step. The following command will delete all <code>.js</code>, <code>.js.map</code>, <code>.d.ts</code> and <code>.tsbuildinfo</code> files in the <code>packages</code> directory.</p>
<pre><code class="lang-bash">rm packages/**/{*.js,*.js.map,*.d.ts,*.tsbuildinfo}
</code></pre>
<p>Having your source code and build output in different directories is a good practice. Therefore, move each package's source code into an <code>src</code> directory, and change the build output directory to <code>dist</code>.</p>
<p>Extend your <code>packages/*/tsconfig.json</code> files with the following snippet:</p>
<pre><code class="lang-json">  <span class="hljs-string">"compilerOptions"</span>: {
    <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"src"</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"dist"</span>
  },
</code></pre>
<p>As an example, this is how the <code>packages/web/tsconfig.json</code> looks now:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"../../tsconfig.base.json"</span>,
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"src"</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"dist"</span>
  },
  <span class="hljs-attr">"references"</span>: [{ <span class="hljs-attr">"path"</span>: <span class="hljs-string">"../core"</span> }]
}
</code></pre>
<p>This tells TypeScript that your source code files are in the <code>src</code> directory, and the build output should go into the <code>dist</code> directory. These are relative to your <code>tsconfig.json</code> file.</p>
<p>Move your <code>index.ts</code> files into the respective <code>src</code> directory. At this point, you should have the following directory tree:</p>
<pre><code>├── package<span class="hljs-operator">-</span>lock.json
├── package.json
├── packages
│   ├── cli
│   │   ├── package.json
│   │   ├── src
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── core
│   │   ├── package.json
│   │   ├── src
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   └── web
│       ├── package.json
│       ├── src
│       │   └── index.ts
│       └── tsconfig.json
├── tsconfig.base.json
└── tsconfig.json
</code></pre><p>Before building your project, adjust the <code>main</code> property in the <code>package.json</code> of each package. Change <code>index.js</code> to <code>dist/index.js</code> since that's where it lives now.</p>
<p>Now build your project and run your CLI app:</p>
<pre><code class="lang-bash">npx tsc --build
node packages/cli/dist/index.js
</code></pre>
<p>You should see the usual output:</p>
<pre><code class="lang-text">Hello from Core!
Hello from CLI!
</code></pre>
<h2 id="heading-youve-done-it-good-job">You've done it! Good Job!</h2>
<p>Now that you have laid the foundation for your project, go on and create something extraordinary!</p>
<p>The <a target="_blank" href="https://github.com/david-szabo97/example-typescript-npm-workspaces">project's source code</a> is available on GitHub. The repository contains a few little changes. Feel free to explore!</p>
<p>If you would like to learn more about NPM workspaces and TypeScript, check out these links:</p>
<ul>
<li><a target="_blank" href="https://docs.npmjs.com/cli/v8/using-npm/workspaces">NPM Workspaces documentation</a></li>
<li><a target="_blank" href="https://www.typescriptlang.org/docs/handbook/project-references.html">TypeScript Project References documentation</a></li>
</ul>
<p><strong>PS.</strong> <a target="_blank" href="https://nx.dev/">NX</a> is an awesome tool! But sometimes, it's better to use the tools you already have at your disposal. Please take your time to decide whether to use NX or NPM / Yarn workspaces.</p>
]]></content:encoded></item><item><title><![CDATA[My feelings about Go after spending a weekend together]]></title><description><![CDATA[I had a great weekend following the Learn Go with Tests online book. I spent about 15 hours going through the book and firing up a few practice projects.
I know it’s not a lot to conclude, but I had enough experience with other languages to find the ...]]></description><link>https://daveiscoding.com/my-feelings-about-go-after-spending-a-weekend-together</link><guid isPermaLink="true">https://daveiscoding.com/my-feelings-about-go-after-spending-a-weekend-together</guid><category><![CDATA[Go Language]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Dávid Szabó]]></dc:creator><pubDate>Wed, 17 Feb 2021 17:36:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658608565933/d4Xln9dHt.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I had a great weekend following the <a target="_blank" href="https://quii.gitbook.io/learn-go-with-tests/">Learn Go with Tests</a> online book. I spent about 15 hours going through the book and firing up a few practice projects.</p>
<p>I know it’s not a lot to conclude, but I had enough experience with other languages to find the pros and cons of the language. As we all know, Go’s most significant and most exciting features are the goroutines and channels. It’s built for concurrency, and it’s so stupid simple to work with it.</p>
<p>Go comes with its own minor problems. You need to pass Context around down to the deepest of your call stack. At the time of this post, Go doesn’t have generics. It’s on the way though. Check out the <a target="_blank" href="https://blog.golang.org/generics-next-step">last blog post</a> about it on Go’s blog for yourself. Don’t get excited about the syntax…</p>
<pre><code class="lang-go"><span class="hljs-comment">// Print prints the elements of any slice.</span>
<span class="hljs-comment">// Print has a type parameter T and has a single (non-type)</span>
<span class="hljs-comment">// parameter s which is a slice of that type parameter.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Print</span>[<span class="hljs-title">T</span> <span class="hljs-title">interface</span></span>{}](s []T) {
    <span class="hljs-comment">// same as above</span>
}
</code></pre>
<h2 id="heading-whats-the-fuss-about-the-syntax">What’s the fuss about the syntax?</h2>
<p>I’m a JavaScript developer who switched from C, Java, C#, PHP languages in this particular order. If you ever worked with those languages – and I bet you did -, then you know the <strong>drill</strong>:</p>
<pre><code class="lang-java">type variableName = value;
</code></pre>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">functionName</span>(<span class="hljs-params">MyType $val</span>) </span>{}
</code></pre>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{}
</code></pre>
<p>You know, going left to right. Type of the variable then the name of it. Then Go comes to the show and…</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">SayHelloToThePeople</span><span class="hljs-params">(people []Person)</span> <span class="hljs-params">(hellos []<span class="hljs-keyword">string</span>)</span></span> {}
</code></pre>
<p>Left to right, then right to the left. Like, what in the name of Gopher is happening here?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658608565933/d4Xln9dHt.png" alt />I guess you get it now.</p>
<p>Maybe the problem lies in me, I should’ve gone down the route of exotic languages rather than the mainstream ones.</p>
<p>After all, as a full-time JS developer, I’m not sure if these two languages can live together in one <em>muscle</em>. As you get used to writing out <code>type variableName</code> then suddenly switching to <code>variableName []type</code> really twitches my <em>muscle memories</em>. Practice makes perfect though.</p>
<h2 id="heading-simplicity-of-go">Simplicity of Go</h2>
<p>Did you know Go only has 25 keywords? We got 32 in C, 54 in Java, 54 in PHP, 59 in JavaScript, and 102 in C#. That’s according to a <a target="_blank" href="https://www.reddit.com/r/ProgrammerHumor/comments/6inj17/languages_by_number_of_keywords/">mid-2017 Reddit post</a>. God knows how many keywords now C# got… Anyway, I was lazy to look it up on my own, so probably those numbers are off – according to my one-minute Google research C# only got 79 keywords. You still get the point, right? Go has few keywords, and they managed to stuff the magic of Go – concurrency – into those few keywords.</p>
<p>Let’s talk about the included batteries. In my opinion, that’s still something that belongs to the simplicity section. Go comes with all the fun stuff without any external dependencies. Let’s see what we got here:</p>
<ul>
<li>Code formatting (<code>gofmt</code>)</li>
<li>Code linting (<code>golint</code>)</li>
<li>More code static checks (<code>go vet</code>)</li>
<li>Tests (<code>go test</code>)</li>
<li>Test coverage (<code>go test -cover</code>, <code>go test -coverprofile=coverprofile.out</code>, <code>go tool cover</code>)</li>
<li>An awesome standard library</li>
</ul>
<p>If you are just starting with Go then you probably don’t need to do anything other than just install Go. That’s a massive plus for me. Whenever I fire up a new JS project, I need to set up a linter, a formatter, a test runner, and many dependencies (depends on the project though).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658608567228/Px5Nd9DJH.png" alt />I’m sure you are familiar with this.</p>
<p>Our modern world of programming is very bloated, I must say. I miss the good old days of just putting together a few PHP files and uploading them via FTP.</p>
<h2 id="heading-whats-next">What’s next?</h2>
<p>Well, Go 2 is coming, and I’m thrilled but scared at the same time. Hopefully, Go 2 will follow what Go 1 started.</p>
<p>I’m hoping for the best regarding the future of Go. I love the language’s current state, and I’ll probably continue learning it and putting together a few projects with it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658608568349/ckYk2GVUW.jpeg" alt />Let’s hope this won’t happen. Source: <a target="_blank" href="https://www.reddit.com/r/golang/comments/6rxfjo/go_2_please_dont_make_it_happen/">Reddit</a></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/david_szabo97/status/1361596761647820802">https://twitter.com/david_szabo97/status/1361596761647820802</a></div>
]]></content:encoded></item><item><title><![CDATA[Setting up Prettier in a Create React App project]]></title><description><![CDATA[Install dependencies
# Yarn
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier

# NPM
npm install -D prettier eslint-config-prettier eslint-plugin-prettier

By default your eslintConfig looks like this:
"eslintConfig": {
    "extends"...]]></description><link>https://daveiscoding.com/setting-up-prettier-in-a-create-react-app-project</link><guid isPermaLink="true">https://daveiscoding.com/setting-up-prettier-in-a-create-react-app-project</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[create-react-app]]></category><dc:creator><![CDATA[Dávid Szabó]]></dc:creator><pubDate>Sun, 20 Dec 2020 03:00:38 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-install-dependencies">Install dependencies</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># Yarn</span>
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier

<span class="hljs-comment"># NPM</span>
npm install -D prettier eslint-config-prettier eslint-plugin-prettier
</code></pre>
<p>By default your <code>eslintConfig</code> looks like this:</p>
<pre><code class="lang-json"><span class="hljs-string">"eslintConfig"</span>: {
    <span class="hljs-attr">"extends"</span>: [
        <span class="hljs-string">"react-app"</span>,
        <span class="hljs-string">"react-app/jest"</span>
    ]
}
</code></pre>
<p>Let’s add Prettier and Prettier React to the extends array:</p>
<pre><code class="lang-json"><span class="hljs-string">"eslintConfig"</span>: {
    <span class="hljs-attr">"extends"</span>: [
        <span class="hljs-string">"react-app"</span>,
        <span class="hljs-string">"react-app/jest"</span>,
        <span class="hljs-string">"plugin:prettier/recommended"</span>,
        <span class="hljs-string">"prettier/react"</span>
    ]
}
</code></pre>
<p>Your editor should pick up the eslint config change after saving package.json. Let’s add two scripts to our package.json, one for formatting and one for lining.</p>
<pre><code class="lang-json"><span class="hljs-string">"format:js"</span>: <span class="hljs-string">"prettier --write src/**/*.{js,jsx,ts,tsx}"</span>,
<span class="hljs-string">"lint:js"</span>: <span class="hljs-string">"eslint src/**/*.{js,jsx,ts,tsx}"</span>
</code></pre>
<p>(<strong>NOTE</strong>: I used js,jsx,ts,tsx in the patterns above. If you don’t have any files that match any of those patterns, then you will get an error. Feel free to remove extensions that you aren’t using)</p>
<p>If you are using TypeScript then you might want to add type checking to the lint script as well:</p>
<pre><code class="lang-json"><span class="hljs-string">"lint:js"</span>: <span class="hljs-string">"tsc --noEmit &amp;&amp; eslint src/**/*.{js,jsx,ts,tsx}"</span>
</code></pre>
<h2 id="heading-automagically-lint-before-git-commit">Automagically lint before <code>git commit</code></h2>
<p>You might want to automatically run the lint script before git commits. This can be achieved easily by using <code>husky</code> and <code>lint-staged</code>. Let’s start by installing them as dev dependencies:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Yarn</span>
yarn add -D husky lint-staged

<span class="hljs-comment"># NPM</span>
npm install -D husky lint-staged
```bash

Next you just need to add a few lines to your package.json:

```json
<span class="hljs-string">"husky"</span>: {
    <span class="hljs-string">"hooks"</span>: {
        <span class="hljs-string">"pre-commit"</span>: <span class="hljs-string">"lint-staged"</span>
    }
},
<span class="hljs-string">"lint-staged"</span>: {
    <span class="hljs-string">"src/**/*.{js,jsx,ts,tsx}"</span>: [
        <span class="hljs-string">"yarn lint:js"</span>
    ]
}
</code></pre>
]]></content:encoded></item></channel></rss>