<?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>Forem: Thomas Klein</title>
    <description>The latest articles on Forem by Thomas Klein (@tmkn).</description>
    <link>https://forem.com/tmkn</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%2F257926%2Ffb930275-e644-4016-9824-e2ada2cf0291.jpeg</url>
      <title>Forem: Thomas Klein</title>
      <link>https://forem.com/tmkn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tmkn"/>
    <language>en</language>
    <item>
      <title>Introducing the Node.js package analyzer</title>
      <dc:creator>Thomas Klein</dc:creator>
      <pubDate>Wed, 07 Apr 2021 15:51:02 +0000</pubDate>
      <link>https://forem.com/tmkn/introducing-the-node-js-package-analyzer-3n2l</link>
      <guid>https://forem.com/tmkn/introducing-the-node-js-package-analyzer-3n2l</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;I'm proud to present to you the Node.js package analyzer.&lt;br&gt;
&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/tmkn" rel="noopener noreferrer"&gt;
        tmkn
      &lt;/a&gt; / &lt;a href="https://github.com/tmkn/packageanalyzer" rel="noopener noreferrer"&gt;
        packageanalyzer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A framework to introspect Node.js packages
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
It's a framework designed to easily introspect a Node.js package.

&lt;p&gt;Think of it as &lt;em&gt;eslint&lt;/em&gt; for your Node.js package/project.&lt;/p&gt;

&lt;p&gt;It's also what powered the data behind &lt;a href="https://npmbomb.tmkn.dev/" rel="noopener noreferrer"&gt;npmbomb&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Package Analyzer
&lt;/h1&gt;

&lt;p&gt;While the JS ecosystem is quite mature and diverse, there is no &lt;em&gt;linter&lt;/em&gt; for a Node.js project. If you want to do a license compliance check, it's a separate tool. If you wan't to check the health of a dependency, that's a separate tool. If you want to check for unneeded polyfills/deprecated packages, that's yet another tool.&lt;/p&gt;

&lt;p&gt;The aim of the package analyzer is to provide the framework and toolset to easily answer questions like above within a single tool.&lt;/p&gt;

&lt;p&gt;All while being flexible enough to answer any questions surrounding a Node.js package like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;calculate the number of all dependencies&lt;/li&gt;
&lt;li&gt;find newest/oldest dependency&lt;/li&gt;
&lt;li&gt;find most included dependency&lt;/li&gt;
&lt;li&gt;get weekly downloads&lt;/li&gt;
&lt;li&gt;check for a new version&lt;/li&gt;
&lt;li&gt;release velocity&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;That's why the package analyzer is both an API and a CLI&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another reason why I started this project was security.&lt;/p&gt;

&lt;p&gt;Personally I think there's to much trust put into adding dependencies.&lt;/p&gt;

&lt;p&gt;Any Node.js package that you install can run a &lt;code&gt;postinstall&lt;/code&gt; script and &lt;a href="https://snyk.io/vuln/npm:radar-cms" rel="noopener noreferrer"&gt;people tried to extract credentials via this way for some time now&lt;/a&gt;, yet there is no straight forward way to highlight packages with a &lt;code&gt;postinstall&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;I also envision a system that highlights which API's a particular dependency is using. &lt;code&gt;fs&lt;/code&gt;, &lt;code&gt;http&lt;/code&gt; etc. or highlights differences between version upgrades: A new sub dependency was added? By whom?&lt;/p&gt;

&lt;p&gt;Ideally the package analyzer will be able to answer those questions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The package analyzer is written in TypeScript so TypeScript types are a first class citizen.&lt;/p&gt;
&lt;h3&gt;
  
  
  Package
&lt;/h3&gt;

&lt;p&gt;At the heart of all of it, is the Package class. After you've traversed a package and all of its dependencies you'll get a single instance of the Package class back. Important points of the API are as follows:&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IPackage&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;//each package parent's can be easily accessed&lt;/span&gt;
    &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;//vital information is directly accessible&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;version&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;fullName&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="c1"&gt;//`name@version`&lt;/span&gt;

    &lt;span class="c1"&gt;//dependencies are listed here&lt;/span&gt;
    &lt;span class="nl"&gt;directDependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="c1"&gt;//convenience functions to iterate over the dependency tree&lt;/span&gt;
    &lt;span class="nl"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;includeSelf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;///easily find dependencies&lt;/span&gt;
    &lt;span class="nl"&gt;getPackagesBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="nl"&gt;getPackagesByName&lt;/span&gt;&lt;span class="p"&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="c1"&gt;//access package.json data&lt;/span&gt;
    &lt;span class="nf"&gt;getData&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;//access custom data&lt;/span&gt;
    &lt;span class="nx"&gt;getDecoratorData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;E&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;IDecoratorStatic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;DecoratorType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;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;For more information about the architecture you can checkout the &lt;a href="https://github.com/tmkn/packageanalyzer/blob/master/ARCHITECTURE.md" rel="noopener noreferrer"&gt;Architecture.md&lt;/a&gt; in the GitHub repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLI
&lt;/h2&gt;

&lt;p&gt;If you install the package analyzer globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @tmkn/packageanalyzer@0.9.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get a &lt;code&gt;pkga&lt;/code&gt; command where you can easily introspect packages.&lt;/p&gt;

&lt;p&gt;Some of the things it does currently are as follows:&lt;/p&gt;

&lt;h3&gt;
  
  
  Print Metadata
&lt;/h3&gt;

&lt;p&gt;You can use the &lt;code&gt;analyze&lt;/code&gt; option to print metadata.&lt;br&gt;
If you don't provide a version number it will use the latest release.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkga analyze --package react //use latest version
pkga analyze --package react@16.12.0 //use specific version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F6f2khzn1an8dd0vbujfb.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%2F6f2khzn1an8dd0vbujfb.png" alt="pkga analyze"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the &lt;code&gt;--full&lt;/code&gt; option to print additional data like oldest/newest package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkga analyze --package react --full
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fl39t0bivms1t7sra6czn.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%2Fl39t0bivms1t7sra6czn.png" alt="pkga analyze full"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to analyze a local project use the &lt;code&gt;--folder&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkga analyze --folder path/to/your/package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Print Dependency Tree
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkga tree --package react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fd7wly4vd4ethho3kbiiq.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%2Fd7wly4vd4ethho3kbiiq.png" alt="pkga tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Print Weekly Downloads
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;downloads&lt;/code&gt; command will print the weekly downloads for a package for NPM&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkga downloads --package react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Frz1cs0hlyu9dkxc6a0lh.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%2Frz1cs0hlyu9dkxc6a0lh.png" alt="pkga downloads"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cyclic Dependencies
&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;loops&lt;/code&gt; command to print cyclic dependencies in the dependency tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkga loops --package webpack@4.46.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2FgZFx8mA.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%2FgZFx8mA.png" alt="pkga loops image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  API
&lt;/h2&gt;

&lt;p&gt;In addition to the CLI, the Package Analyzer also offers an API.&lt;br&gt;
All the commands in the CLI are done via this API as well.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Here's how you can use it to list all dependencies of &lt;code&gt;fastify&lt;/code&gt; that come with built int &lt;code&gt;TypeScript&lt;/code&gt; support.&lt;br&gt;
Type declarations are either marked via the &lt;code&gt;types&lt;/code&gt; or &lt;code&gt;typings&lt;/code&gt; field in the &lt;code&gt;package.json&lt;/code&gt;, so all we need to do is ask if those fields are set and collect them:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Visitor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;npmOnline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OraLogger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tmkn/packageanalyzer&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="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;try&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;visitor&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;Visitor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;npmOnline&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;OraLogger&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;pkg&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;visitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&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;matches&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;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&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;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;true&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="s2"&gt;Built in TypeScript support:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for &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;name&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;matches&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="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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;visitor&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;Visitor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fastify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;npmOnline&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;OraLogger&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we need to create a &lt;code&gt;Visitor&lt;/code&gt; that traverses the dependency tree.&lt;/p&gt;

&lt;p&gt;The 1st argument is a tuple that specifies the package, if you don't provide a version. e.g.&lt;code&gt;["fastify", "3.14.1"]&lt;/code&gt; it will default to the latest like here.&lt;/p&gt;

&lt;p&gt;The 2nd argument is the &lt;code&gt;Provider&lt;/code&gt;. Whenever the &lt;code&gt;Visitor&lt;/code&gt; wants data about a package it will ask the &lt;code&gt;Provider&lt;/code&gt;. In this case we are asking the NPM registry, but you could also write a &lt;code&gt;Provider&lt;/code&gt; that gets the information from the filesystem e.g. &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;The 3rd argument is the logger. All output is routed to this logger.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkg&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;visitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&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 simply call the async &lt;code&gt;visit&lt;/code&gt; function from the &lt;code&gt;Visitor&lt;/code&gt; to start traversing the dependency tree. We will end up with a single top level &lt;code&gt;Package&lt;/code&gt; class.&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="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&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;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;Package&lt;/code&gt; class provides utility functions like &lt;code&gt;visit&lt;/code&gt; to iterate over each dependency and the &lt;code&gt;getData&lt;/code&gt; method to access the respective &lt;code&gt;package.json&lt;/code&gt;. So here we iterate over each dependency and check if the respective &lt;code&gt;package.json&lt;/code&gt; contains entries for &lt;code&gt;types&lt;/code&gt; or &lt;code&gt;typings&lt;/code&gt;. If yes, collect &lt;code&gt;dep.fullName&lt;/code&gt; which is a string formatted to &lt;code&gt;packagename@version&lt;/code&gt;.&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="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="s2"&gt;Built in TypeScript support:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for &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;name&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;matches&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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards we just print out our findings, done.&lt;/p&gt;

&lt;p&gt;If you want to know more I recommend to check out the &lt;a href="https://github.com/tmkn/packageanalyzer/blob/master/ARCHITECTURE.md" rel="noopener noreferrer"&gt;Architecture.md&lt;/a&gt; in the GitHub repository. But do note that the API is not yet stable and will likely undergo changes as it's a first preview.&lt;/p&gt;

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

&lt;p&gt;Since I have a lot of ideas where to take this and since it's a first preview I would really appreciate feedback :)&lt;/p&gt;

&lt;p&gt;In the beginning I said to think of it as &lt;code&gt;eslint&lt;/code&gt; for packages, however defining your own set of checks that you want to run is not yet possible and something that I want work on next.&lt;/p&gt;

&lt;p&gt;I'm also thinking about a web interface, that allows to better visually present the results and where you can jump back and forth between packages/reports in an easy manner.&lt;/p&gt;

&lt;p&gt;Apart from that, any suggestions are highly welcomed.&lt;/p&gt;

&lt;p&gt;For updates you can follow me on &lt;a href="https://twitter.com/tmkndev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or follow the project on GitHub: &lt;a href="https://github.com/tmkn/packageanalyzer" rel="noopener noreferrer"&gt;https://github.com/tmkn/packageanalyzer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>showdev</category>
    </item>
    <item>
      <title>What's new in npmbomb?</title>
      <dc:creator>Thomas Klein</dc:creator>
      <pubDate>Thu, 27 Aug 2020 19:39:25 +0000</pubDate>
      <link>https://forem.com/tmkn/what-s-new-in-npmbomb-1g2</link>
      <guid>https://forem.com/tmkn/what-s-new-in-npmbomb-1g2</guid>
      <description>&lt;p&gt;After the initial release I found some time to do some quality of life changes, among them are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visualizing the dependency tree&lt;/li&gt;
&lt;li&gt;Refactored search to be future proof&lt;/li&gt;
&lt;li&gt;Updated the dataset&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  What is npmbomb?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://npmbomb.tmkn.dev/" rel="noopener noreferrer"&gt;https://npmbomb.tmkn.dev/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;npmbomb is a little website that I built where you can try and guess the number of total dependencies for popular npm modules to shine a light on the growing transitive dependencies number of npm modules. If you add 1 dependency to your project, that dependency might contain a mountain of other dependencies that you don't know of but now will be part of your project.&lt;br&gt;
It does this by simply following every dependency of the &lt;code&gt;dependencies&lt;/code&gt; field in the &lt;code&gt;package.json&lt;/code&gt; down to the very last one. As such, the calculated numbers might surprise you.&lt;br&gt;
If you want to know more, you can find an introduction &lt;a href="https://dev.to/tmkn/building-npmbomb-5ga4"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Visualizing the dependency tree
&lt;/h1&gt;

&lt;p&gt;There is now a &lt;em&gt;Dependency Tree&lt;/em&gt; tab, switching to it will reveal the 🎉 dependency tree.&lt;br&gt;
It will show the transitive dependency count as well as provide a link to npm by clicking on the link icon.&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%2Fi%2Fcaxaufujezwqrry4dhrr.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%2Fi%2Fcaxaufujezwqrry4dhrr.PNG" alt="react dependency tree"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How are the numbers calculated?
&lt;/h2&gt;

&lt;p&gt;The number on the right on each line is the transitive dependency count or total dependency count.&lt;br&gt;
In case of React that number is 8.&lt;br&gt;
Here's how it is calculated:&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%2Fi%2Ff2d5x7cto9apeosmdmoh.jpg" 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%2Fi%2Ff2d5x7cto9apeosmdmoh.jpg" alt="dependency count explanation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3 direct dependencies of React itself + all transitive dependencies of &lt;code&gt;loose-envify&lt;/code&gt; (1) and &lt;code&gt;prop-types&lt;/code&gt; (4). &lt;code&gt;object-assign&lt;/code&gt; doesn't itself define any other dependencies so doesn't contribute to the transitive dependencies count.&lt;/p&gt;

&lt;p&gt;To keep things snappy, the tree uses &lt;a href="https://github.com/bvaughn/react-virtualized" rel="noopener noreferrer"&gt;react-virtualized&lt;/a&gt;.&lt;br&gt;
Apart from that the tree component is custom built.&lt;/p&gt;

&lt;p&gt;While most trees would work just fine without &lt;code&gt;react-virtualized&lt;/code&gt; since they are not that deep and big, Jest for example made the browser sweat.&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%2Fi%2F36e26i69x70h1503kmom.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%2Fi%2F36e26i69x70h1503kmom.PNG" alt="Jest dependency tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the &lt;code&gt;react-virtualized&lt;/code&gt; module was already in use on the search page, it was also used for the tree rendering to solve potential rendering bottlenecks.&lt;/p&gt;

&lt;p&gt;Coming back to Jest and its humongous dependency tree, the very first working version resulted in a 20MB JSON payload just for the tree data.&lt;br&gt;
The straight forward format looked like this:&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IDependencyTree&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;version&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;transitiveCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IDependencyTree&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;Even though a dependency tree like Jest is not the norm, clearly that's too much. Even gzipped it still was 5MB.&lt;br&gt;
When I reduced the length of the keys to single chars it was still 16MB.&lt;/p&gt;

&lt;p&gt;So I changed it to provide a lookup table instead and only reference numbers(id) for the actual nested format:&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%2Fi%2F2rxudk4i8or5eqv3zc03.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%2Fi%2F2rxudk4i8or5eqv3zc03.png" alt="JSON Tree Payload"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;tree&lt;/code&gt; value looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IDependencyTreeConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//lookup&lt;/span&gt;
    &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ITreeData&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="c1"&gt;//nested tree structure&lt;/span&gt;
    &lt;span class="nl"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IDependencyTreeStructure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ITreeData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;version&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;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IDependencyTreeStructure&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IDependencyTreeStructure&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;I could potentially reduce it further by reducing the length of the keys here as well but already with this approach the payload went down to 7MB, while compressed with Brotli it's now around 47kb. For an outlier like Jest, I think it's acceptable.&lt;/p&gt;

&lt;h1&gt;
  
  
  Refactored search to be future proof
&lt;/h1&gt;

&lt;p&gt;The long term goal of npmbomb is to have data for any(most) npm modules.&lt;br&gt;
As of now the dataset is limited to a handful of the most popular modules.&lt;br&gt;
As such the architecture of the search was tailored for this limited data set so it wouldn't scale with growing data.&lt;/p&gt;

&lt;p&gt;To remedy this, the search now uses &lt;code&gt;react-virtualized&lt;/code&gt; to display its results (just like the dependency tree), supporting any number of search results.&lt;/p&gt;

&lt;p&gt;With the old search architecture everything was in memory so it wasn't prone to ajax race conditions and such, so it's implementation was really simple but again with a growing dataset this approach wouldn't be feasible any more. Now you would need to take care of Ajax race conditions, only fire the search request after the user stopped typing etc, dismiss the ongoing ajax request if the user starts typing again, etc, to handle all of this cleanly the search architecture is now powered by &lt;code&gt;RxJs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's the new search:&lt;br&gt;
In fact, it looks like the old one, all the changes are behind the scenes ;)&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%2Fi%2Fvgln3wr4w7ai6dd9gzi6.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%2Fi%2Fvgln3wr4w7ai6dd9gzi6.PNG" alt="search overview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Updated the dataset
&lt;/h1&gt;

&lt;p&gt;The original data was based on July 2019 so I thought it was fitting to provide an update after a year, so now the data is based on July 2020. While this is already "outdated", the goal of npmbomb is not to provide up to date data. In fact, this would be very challenging to provide, as any new version of any module could alter any existing dependency tree. It's highly volatile. Instead the goal of npmbomb is to provide a ballpark number from mostly up to date data.&lt;/p&gt;

&lt;p&gt;Interestingly the npm dataset grew within this 1 year from &lt;strong&gt;23.9GB&lt;/strong&gt; to &lt;strong&gt;42.2GB&lt;/strong&gt;.&lt;br&gt;
And the total number of modules grew from &lt;strong&gt;1 007 928&lt;/strong&gt; to &lt;strong&gt;1 332 134&lt;/strong&gt;. So within this 1 year npm saw &lt;strong&gt;324 206&lt;/strong&gt; brand new modules.&lt;/p&gt;

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

&lt;p&gt;Things that I have on my mind for npmbomb:&lt;/p&gt;

&lt;h2&gt;
  
  
  Increase dataset
&lt;/h2&gt;

&lt;p&gt;One of the immediate next steps is to increase the dataset aka looking at options to host the data. The dataset is currently hosted together with the webapp via netlify. While an awesome service, it eats into the free tier usage, I would rather like to move the data hosting to somewhere else and only host the web app on netlify. I'm open for suggestions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency tree improvements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Breadcrumbs for tree view
&lt;/h3&gt;

&lt;p&gt;Show the path upon hovering over tree nodes, as with large trees you can get easily lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering
&lt;/h3&gt;

&lt;p&gt;Allow the user to search for specific modules.&lt;br&gt;
Highlight modules where they appear in the tree&lt;/p&gt;

&lt;h2&gt;
  
  
  More information
&lt;/h2&gt;

&lt;p&gt;Display more information for a module, like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maintainer&lt;/li&gt;
&lt;li&gt;release date&lt;/li&gt;
&lt;li&gt;show source code&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  More E2E testing
&lt;/h2&gt;

&lt;p&gt;While there is E2E testing, it's not on a level where I would like it to be. The bulk of the testing is done via unit tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  A11Y
&lt;/h2&gt;

&lt;p&gt;Since the project is past the prototype stage, with a growing audience it makes sense to invest into accessibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Housekeeping
&lt;/h2&gt;

&lt;p&gt;With the tree view added and other small tweaks here and there, it is now a good opportunity to take a step back and do some housekeeping.&lt;/p&gt;




&lt;p&gt;You see there are a lot of ideas and things that I want to do. This is also a friendly reminder that npmbomb is totally &lt;a href="https://github.com/tmkn/npmbomb" rel="noopener noreferrer"&gt;Open Source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Go checkout the new tree view: &lt;a href="https://npmbomb.tmkn.dev/" rel="noopener noreferrer"&gt;https://npmbomb.tmkn.dev/&lt;/a&gt;&lt;br&gt;
Feedback highly appreciated, whether on &lt;a href="https://twitter.com/tmkndev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/tmkn/npmbomb" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or here 🙃&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>npm</category>
    </item>
    <item>
      <title>Building npmbomb</title>
      <dc:creator>Thomas Klein</dc:creator>
      <pubDate>Sun, 19 Jan 2020 16:08:53 +0000</pubDate>
      <link>https://forem.com/tmkn/building-npmbomb-5ga4</link>
      <guid>https://forem.com/tmkn/building-npmbomb-5ga4</guid>
      <description>&lt;p&gt;This is a technical writeup of how I built &lt;a href="https://npmbomb.tmkn.dev/"&gt;npmbomb&lt;/a&gt;, which technologies I chose and why. The problems encountered along the way and how I solved them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://npmbomb.tmkn.dev/"&gt;npmbomb&lt;/a&gt; is a purely client side app where you can guess the number of dependencies for popular NPM packages 😈&lt;br&gt;
I took the packages from the first 3 pages &lt;a href="https://www.npmjs.com/browse/depended"&gt;here&lt;/a&gt; and calculated the dependency tree for each of them.&lt;/p&gt;
&lt;h1&gt;
  
  
  How
&lt;/h1&gt;

&lt;p&gt;For the past few months, after work, I've been working on and off on a NodeJS package analyzer. I wanted to know what is the oldest dependency in my dependency tree, what are the licenses, are there new versions for my dependencies etc. You just point it to a package.json or hand it a package name and it will resolve the dependencies and calculate all sorts of data. Once the analyzer is more polished I'll write more in-depth about it. At one point parts of it used C# as the NodeJS stream API was unstable but that's for another time.&lt;/p&gt;

&lt;p&gt;Ever since the addition of React Hooks, I always wanted to create a little web app that exclusively uses them just to see how it feels and &lt;a href="https://emotion.sh/"&gt;Emotion CSS&lt;/a&gt; for the styling. I just needed a worthwhile idea, ...this was it.&lt;/p&gt;
&lt;h1&gt;
  
  
  Tech Stack
&lt;/h1&gt;

&lt;p&gt;In a nutshell it's a purely client side app, leveraging React, React Router, Emotion.&lt;/p&gt;

&lt;p&gt;Normally I opt for MobX and CSS Modules. While I like MobX, I wanted to see if React Context in conjunction with React Hooks would be a suitable replacement in this case.&lt;/p&gt;

&lt;p&gt;As for CSS Modules I never really quite liked them because I can't easily access CSS properties in my code and vice versa like animation delays etc. Also it's just too much magic, while it works, "importing" CSS files in JavaScript just feels wrong, it's a clever hack. Also using them with TypeScript requires some extra effort. By using Emotion I hoped to improve on all of these pain points.&lt;/p&gt;

&lt;p&gt;In its infancy I used &lt;a href="https://nextjs.org/"&gt;NextJS&lt;/a&gt; because I wanted to try it out as they added TypeScript support but dropped it because adding Emotion was giving me some trouble and overall I could just move faster by rolling my own setup instead. While people hate configuring webpack I don't find it that bad as everything I need can be found in their documentation. For NextJS I had to look at stackoverflow etc. Never-mind I will revisit NextJS in the future, it just wasn't a great fit at the time.&lt;/p&gt;
&lt;h2&gt;
  
  
  TypeScript
&lt;/h2&gt;

&lt;p&gt;All the code is written in TypeScript with the &lt;code&gt;strict&lt;/code&gt; option, for me the most invaluable tool for writing maintainable JavaScript. You could replace any part of the tech stack and I'm cool with it except TypeScript.&lt;/p&gt;

&lt;p&gt;I still remember one time at work where I had to replace instances of &lt;code&gt;TextNode&lt;/code&gt; with &lt;code&gt;HTMLNode&lt;/code&gt;. It took me several commits to catch and fix everything. Later I converted the whole code base to TypeScript(just after they announced support for JS files) and even though the compiler complained A LOT and thus I had to touch much more code than when I did the &lt;code&gt;TextNode&lt;/code&gt; to &lt;code&gt;HTMLNode&lt;/code&gt; conversion, the dev experience was just so much better.&lt;/p&gt;

&lt;p&gt;When the compiler said 0 errors, he really meant it. Ever since then TypeScript became invaluable, if TypeScript complains you listen.&lt;/p&gt;
&lt;h2&gt;
  
  
  React
&lt;/h2&gt;

&lt;p&gt;As I said I wanted to build the app solely with Function Components and Hooks/Context.&lt;br&gt;
Overall I found it quite pleasing, however defining Contexts is a little awkward in conjunction with TypeScript. I didn't want to make the actual Context members optional so I had to initialise them with temporary values...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IAppContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IAppState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;setAppState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IAppState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IAppContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;inGameMode&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="na"&gt;guesses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="na"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="na"&gt;packages&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="na"&gt;setAppState&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here all the values are just placeholders to satisfy the TypeScript compiler, &lt;code&gt;setAppState&lt;/code&gt; will essentially do nothing, if I later down the road forget to provide the actual data, it will use my placeholder data with no real warning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAppState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IAppState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;inGameMode&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="na"&gt;guesses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="na"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="na"&gt;packages&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="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IAppContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;setAppState&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&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="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="c1"&gt;//have to make sure I use AppContext.Provider to provide&lt;/span&gt;
        &lt;span class="c1"&gt;//the actual data for child components&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/AppContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;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;I could have simply defined &lt;code&gt;AppContext&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IAppContext&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which would allow me to initialise &lt;code&gt;AppContext&lt;/code&gt; with &lt;code&gt;undefined&lt;/code&gt; and be done with it, but then every-time I would like to consume it I would have to do a check for &lt;code&gt;undefined&lt;/code&gt; which leads to very repetitive code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppContext&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;appContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//do something with appContext.appState&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By not having it &lt;code&gt;undefined&lt;/code&gt; I can consume it much more elegantly, but I have to initialise it with temp data first like shown above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setAppState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Other than that I like the flexibility that Contexts provide, no more passing on props to child components, definitely an improvement.&lt;br&gt;
For this use case it could replace external state management completely.&lt;/p&gt;
&lt;h2&gt;
  
  
  React Router
&lt;/h2&gt;

&lt;p&gt;Just like React I used Hooks for React Router. In conjunction with TypeScript this also simplified typing. Previously you had to setup much more boilerplate code to access router related props in your components like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;RouteComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="na"&gt;package&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With hooks it just boils down to using the appropriate &lt;code&gt;useHistory&lt;/code&gt; or &lt;code&gt;useParams&lt;/code&gt; hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useHistory&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;//in your function component...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useHistory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pkgName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useParams&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkgName&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Emotion
&lt;/h2&gt;

&lt;p&gt;In short it basically solved all the pain points that I had with CSS Modules and it will be my preferred CSS solution going forward.&lt;/p&gt;

&lt;p&gt;I like that with the spread operator it makes for very concise code.&lt;br&gt;
The only downside was that since I was using it directly with TypeScript&lt;br&gt;
 and not with Babel, I had to place a pragma comment at the top of each file where I wanted to use Emotion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @jsx jsx */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jsx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@emotion/core&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;p&gt;This also meant that I couldn't use the shorthand notation &lt;code&gt;&amp;lt;&amp;gt;&amp;lt;/&amp;gt;&lt;/code&gt; anywhere in the code and had to fall back to &lt;code&gt;&amp;lt;React.Fragment&amp;gt;&amp;lt;/React.Fragment&amp;gt;&lt;/code&gt;. While a bummer it wasn't that much of a bummer that I would introduce Babel into the mix.&lt;/p&gt;

&lt;p&gt;Another surprise was that it auto prefixed the CSS out of the box, without me doing anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titleStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@media (min-width: 320px)&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="na"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;turned into&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;320px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;css-bof962-Header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;-webkit-flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;-ms-flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;Quite nice!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sentry
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://sentry.io/welcome/"&gt;Sentry&lt;/a&gt; monitors your application for runtime errors. If there's an error, Sentry collects it and sends it to their servers where you can inspect them and take further actions:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M9SYyl3B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7ajeokdmd2a1r9xhii9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M9SYyl3B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7ajeokdmd2a1r9xhii9j.png" alt="Sentry dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here some error happened in my app, and I get to see details about the browser environment and also a stacktrace etc.&lt;/p&gt;

&lt;p&gt;I also use it to propose me new packages. As said I only analyzed the most popular NPM packages and I'm using Sentrys functionality to gather feedback too, not just runtime errors.&lt;/p&gt;

&lt;p&gt;I've used datadog in the past, which offers similar functionality but the onboarding was much smoother with Sentry, going forward this will be my preferred choice for collecting runtime errors.&lt;/p&gt;
&lt;h1&gt;
  
  
  Testing
&lt;/h1&gt;

&lt;p&gt;No application is complete without testing, here are the tools that I used:&lt;/p&gt;
&lt;h2&gt;
  
  
  Unit Tests
&lt;/h2&gt;

&lt;p&gt;For unit tests I used &lt;a href="https://testing-library.com/docs/react-testing-library/intro"&gt;React Testing Library&lt;/a&gt;. Not much needs to be said, I like the API that they are offering to test various parts of your app.&lt;/p&gt;
&lt;h2&gt;
  
  
  E2E Tests
&lt;/h2&gt;

&lt;p&gt;For E2E tests I used &lt;a href="https://testing-library.com/docs/react-testing-library/intro"&gt;cypress&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Visual Regression Testing
&lt;/h2&gt;

&lt;p&gt;For a per pixel check of the app I use &lt;a href="https://percy.io/"&gt;Percy&lt;/a&gt;. It's a cloud service that compares screenshots on a per pixel basis. I instrument cypress to take screenshots at certain points in the app and with different breakpoints and Percy then shows me the differences, if there are any.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="c1"&gt;//take a screenshot of the homepage with&lt;/span&gt;
    &lt;span class="c1"&gt;//3 different breakpoints&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:8080&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percySnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;home page&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="na"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1200&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5d7nGebk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/y9znue2wuuo96cq8x315.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5d7nGebk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/y9znue2wuuo96cq8x315.png" alt="Percy Interface"&gt;&lt;/a&gt;&lt;br&gt;
I can then approve it or go back into the code and fix it.&lt;/p&gt;

&lt;p&gt;What is also nice is that it's integrated into GitHub, it checks every pull request for changes automatically&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m7k3uumA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ihgyul004u93ka3xbibt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m7k3uumA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ihgyul004u93ka3xbibt.png" alt="Percy GitHub"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code Coverage
&lt;/h2&gt;

&lt;p&gt;Code Coverage is visualized via &lt;a href="https://codecov.io/gh"&gt;Codecov&lt;/a&gt;, I like their UI better than coveralls, it's less cluttered.&lt;br&gt;
It's also integrated into GitHub and run on every pull request:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vjWxcUwL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fxdknfppgwsdouluf5yc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vjWxcUwL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fxdknfppgwsdouluf5yc.png" alt="Codecov GitHub"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Continuous Integration
&lt;/h1&gt;

&lt;p&gt;To make sure the tests are working and to upload the code coverage and percy results I'm using &lt;a href="https://azure.microsoft.com/en-us/services/devops/pipelines/"&gt;Azure Pipelines&lt;/a&gt;.&lt;br&gt;
I specified to run the tests on Linux, Mac and Windows even though there is no explicit platform specific code since it's a web app I do it because I use all of these 3 operating systems to develop.&lt;br&gt;
It's also integrated into GitHub, every push to master/pull request triggers the pipelines:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2KX9BnjI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gyiwp2xj3iv724pahfan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2KX9BnjI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gyiwp2xj3iv724pahfan.png" alt="Azure Pipelines"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setting it up was fairly straight forward, the biggest problem I had was to tell it to only upload the code coverage report on the Linux run. It took me several tries to get it correct.&lt;/p&gt;
&lt;h1&gt;
  
  
  Deployment
&lt;/h1&gt;

&lt;p&gt;The whole app is hosted on &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;. It's the first time using the platform and it was very easy to integrate. Each push to master deploys it automatically.&lt;br&gt;
You just have to specify the build command and which folder contains the compiled output, it couldn't be easier!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r6MIfrzq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/psdgzcldhquky7bt2r89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r6MIfrzq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/psdgzcldhquky7bt2r89.png" alt="Netlify"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I only had to add a &lt;code&gt;netlify.toml&lt;/code&gt; to specify redirect rules so urls like &lt;a href="https://npmbomb.tmkn.dev/package/jest@24.8.0"&gt;https://npmbomb.tmkn.dev/package/jest@24.8.0&lt;/a&gt; resolve correctly since I'm using client side routing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[redirects]]&lt;/span&gt;
  &lt;span class="py"&gt;from&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/*"&lt;/span&gt;
  &lt;span class="py"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/index.html"&lt;/span&gt;
  &lt;span class="py"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
  &lt;span class="nn"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;X-From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Netlify"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I also enabled Netlify to run on pull requests:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DlLY8L-N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/s13omil1737zhqgjwcam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DlLY8L-N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/s13omil1737zhqgjwcam.png" alt="Netlify GitHub"&gt;&lt;/a&gt;&lt;br&gt;
I just need to click the link and get a live version of the pull request, super nice!&lt;/p&gt;

&lt;p&gt;That's basically it about the tech stack and how I wrote &lt;a href="https://npmbomb.tmkn.dev"&gt;npmbomb&lt;/a&gt;.&lt;br&gt;
All the tools that I introduced can be used for free, I run all of them on the free tier and they are more than enough for my use case and make my life as a developer much easier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vUaxpqcN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/lfsvufchgknhcrov5612.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vUaxpqcN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/lfsvufchgknhcrov5612.png" alt="pull request"&gt;&lt;/a&gt;&lt;br&gt;
Here I instantly see that the visual design changed thanks to Percy, but everything else like tests ran through and Netlify was able to successfully deploy the pr. So once I fix the issue reported by Percy I can confidently merge this back into master (never-mind the fact that codecov couldn't find a report here).&lt;/p&gt;

&lt;h1&gt;
  
  
  Whats next
&lt;/h1&gt;

&lt;p&gt;Greater test coverage, &lt;del&gt;just kidding, we all know it won't happen. 😐&lt;/del&gt; I actually did it: &lt;a href="https://codecov.io/gh/tmkn/npmbomb"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dP7-idgt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://codecov.io/gh/tmkn/npmbomb/branch/master/graph/badge.svg" alt="codecov"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using the app (at least for me) I found it quite addictive, currently you can guess only 4 packages before it starts over again. I think an endless mode would be cool.&lt;/p&gt;

&lt;p&gt;I also would like to show the dependency tree visually, so that you see which dependencies it pulls in from where etc.&lt;/p&gt;

&lt;p&gt;Thanks for making it to the end and have fun &lt;a href="https://npmbomb.tmkn.dev"&gt;guessing NPM dependencies&lt;/a&gt;!&lt;br&gt;
All the code can be found on &lt;a href="https://github.com/tmkn/npmbomb"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>JavaScript and bit masks</title>
      <dc:creator>Thomas Klein</dc:creator>
      <pubDate>Sun, 24 Nov 2019 16:08:04 +0000</pubDate>
      <link>https://forem.com/tmkn/javascript-and-bit-masks-28l5</link>
      <guid>https://forem.com/tmkn/javascript-and-bit-masks-28l5</guid>
      <description>&lt;p&gt;Even though you can use bit masks in JavaScript they are not widely (if at all) known. If you don't know what bit masks are, and want to learn a little bit about programming history, this post is for you:&lt;/p&gt;

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

&lt;p&gt;The primary use case for bit masks is to save memory. They come from a time where memory was scarce, long before the internet.&lt;br&gt;
Programmers back then stored multiple values into a single value. How did they do this? With bit masks.&lt;/p&gt;

&lt;p&gt;They took advantage that the computer uses bits. 1 bit just stores 0 or 1. You might as well think of it as set or unset, true or false or simply a flag.&lt;/p&gt;

&lt;p&gt;If you have 2 bits you can store them in 4 ways, this could be 4 different flags:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit2&lt;/th&gt;
&lt;th&gt;Bit1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If we have 3 bits we can store them in 8 different ways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit3&lt;/th&gt;
&lt;th&gt;Bit2&lt;/th&gt;
&lt;th&gt;Bit1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They follow a power of two:&lt;br&gt;
2bit -&amp;gt; 2^2 = 4&lt;br&gt;
3bit -&amp;gt; 2^3 = 8&lt;br&gt;
32bit -&amp;gt; 2^32 = 4 294 967 296&lt;br&gt;
and so on.&lt;/p&gt;

&lt;p&gt;Unfortunately if you wanted to have a flag in your code, a true-false value, early computer systems didn't allow you to just use 1 bit to specify that flag. For the sake of this introduction the computer always allocated 4bits for every kind of variable.&lt;/p&gt;

&lt;p&gt;Since you only want to store a flag that theoretically only needs 1bit the remaining 3bits are going to waste.&lt;/p&gt;

&lt;p&gt;If you add yet another flag, you will again allocate 4bits where 3bits are wasted.&lt;br&gt;
So now you wasted 6bits because you created 2 separate flags.&lt;br&gt;
This was a &lt;em&gt;huge&lt;/em&gt; deal back in the day.&lt;/p&gt;

&lt;p&gt;The solution to this problem was to store the 2 flags(2bits) not as separate variables but &lt;em&gt;inside&lt;/em&gt; a single (4bit) variable. This way we only waste 2bits out of 4bits in this concrete example.&lt;/p&gt;
&lt;h1&gt;
  
  
  Bit Operators
&lt;/h1&gt;

&lt;p&gt;To achieve this you have to use bit operators. I'm sure you've already seen &lt;code&gt;||&lt;/code&gt; or &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; in action in some JavaScript code so you know they relate to &lt;em&gt;logical or&lt;/em&gt; and &lt;em&gt;logical and&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But did you know there is also this syntax?:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;They operate on a bit level and are the whole secret to bit masks.&lt;/p&gt;

&lt;h2&gt;
  
  
  &amp;amp; (AND)
&lt;/h2&gt;

&lt;p&gt;Given 2 bit sequences they produce the following output:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;0 1 0 1 1&lt;br&gt;
1 1 0 1 0&lt;br&gt;
..............&lt;br&gt;
0 1 0 1 0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If both are 1, the output will also be 1.&lt;br&gt;
If only one of them is 1 it will be set to 0.&lt;/p&gt;
&lt;h2&gt;
  
  
  | (OR)
&lt;/h2&gt;

&lt;p&gt;Given 2 bit sequences they produce the following output:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;0 1 0 1 1&lt;br&gt;
1 1 0 1 0&lt;br&gt;
..............&lt;br&gt;
1 1 0 1 1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If at least one of them is 1, the output will also be set to 1.&lt;/p&gt;

&lt;p&gt;This is all you need to know to work with bit masks.&lt;br&gt;
Pretty similar to logical AND and logical OR.&lt;br&gt;
To enter an if clause with logical AND (&amp;amp;&amp;amp;), &lt;em&gt;both conditions need to be true.&lt;/em&gt;&lt;br&gt;
To enter an if clause with logical OR (||), &lt;em&gt;only 1 condition needs to be true.&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Example
&lt;/h1&gt;

&lt;p&gt;Say we want to have a simple permission system that differentiates between a user and admin.&lt;/p&gt;

&lt;p&gt;We can specify the bit sequences as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bit2&lt;/th&gt;
&lt;th&gt;Bit1&lt;/th&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Unused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Admin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The person is a user, if the bit sequence is &lt;code&gt;01&lt;/code&gt;.&lt;br&gt;
The person has admin rights, if the bit sequence is &lt;code&gt;10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this info we can create our bit masks in JavaScript, we can specify the binary value right away with &lt;code&gt;binary literals&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mb"&gt;0b01&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;ADMIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mb"&gt;0b10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lets create a permission that specifies that someone is a user and admin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userPermission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;USER&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;|&lt;/code&gt; operator to &lt;em&gt;set&lt;/em&gt; values.&lt;br&gt;
This is the "store 2bits in 1 variable" that I talked about before.&lt;br&gt;
Now &lt;code&gt;userPermission&lt;/code&gt; contains info whether &lt;code&gt;USER&lt;/code&gt; is set and whether &lt;code&gt;ADMIN&lt;/code&gt; is set.&lt;/p&gt;

&lt;p&gt;But how do we check the permission? With the &lt;code&gt;&amp;amp;&lt;/code&gt; operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;userPermission&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ADMIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//confirmed admin&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;How did this work?&lt;br&gt;
Let's look at the &lt;code&gt;&amp;amp;&lt;/code&gt; operation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;1 1 //userPermission&lt;br&gt;
1 0 //ADMIN&lt;br&gt;
.....&lt;br&gt;
1 0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We get the bit sequence &lt;code&gt;10&lt;/code&gt;, then we check if this sequence matches ADMIN and yes it does, thus we are confirmed admin :)&lt;/p&gt;

&lt;p&gt;To check for a user we use the same pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;userPermission&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//confirmed user&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 how programmers back in the day tried to save memory, while it's not that useful on the web today due to the way JavaScript works and overall technological advancements it's always nice to know the history of things.&lt;/p&gt;

&lt;p&gt;If you're feeling intrigued, here's more info about bit operators (in JavaScript): &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators"&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators&lt;/a&gt;&lt;br&gt;
I only covered the very top, there are more operators that allow more use cases but that would exceed the scope for this post.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>computerscience</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A better puppeteer.evaluate</title>
      <dc:creator>Thomas Klein</dc:creator>
      <pubDate>Mon, 28 Oct 2019 21:41:56 +0000</pubDate>
      <link>https://forem.com/tmkn/a-better-puppeteer-evaluate-523h</link>
      <guid>https://forem.com/tmkn/a-better-puppeteer-evaluate-523h</guid>
      <description>&lt;h3&gt;
  
  
  Execute JavaScript with puppeteer
&lt;/h3&gt;

&lt;p&gt;To execute JavaScript code on your puppeteer instance, you can use the provided &lt;code&gt;evaluate&lt;/code&gt; method. e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;title&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;document&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This returns the document title from the puppeteer instance back to the node process.&lt;/p&gt;

&lt;p&gt;But the way this works is actually quite limited and error prone.&lt;br&gt;
This already errors at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`p`&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;paragrapahs&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&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;elements&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//runtime error&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(╯°□°)╯︵ ┻━┻&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;Because the callback that you provide in the &lt;code&gt;evaluate&lt;/code&gt; method gets &lt;strong&gt;serialized&lt;/strong&gt;.&lt;br&gt;
Meaning by the time it arrives at the browser it will have lost all information about closures/imports etc. What a bummer.&lt;/p&gt;

&lt;p&gt;If you're like me and want to separate your evaluate code to an external file, use node modules etc, you're out of luck. It just doesn't work.&lt;/p&gt;

&lt;p&gt;Worse, you will get no indication that this will not work, the IDE says all good, go ahead run it, only to be hit with a runtime error :(&lt;/p&gt;

&lt;p&gt;To be fair though, the above &lt;em&gt;fairly simple&lt;/em&gt; example can be made to work, as you can also specify arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`p`&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;paragrapahs&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&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;elements&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="cm"&gt;/* &amp;lt;- pass selector as argument */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Still, this is very cumbersome to work with, as you just expect closures to be available, after all, that's how JavaScript works.&lt;/p&gt;

&lt;h3&gt;
  
  
  A better puppeteer evaluate
&lt;/h3&gt;

&lt;p&gt;For that reason I published a little library(my first) that lets me do just that: &lt;/p&gt;

&lt;p&gt;Aptly named &lt;a href="https://www.npmjs.com/package/puppeteer-evaluate2"&gt;puppeteer-evaluate2&lt;/a&gt;, it allows me to write my puppeteer callback code like any other JavaScript code, where closures/imports are available.&lt;/p&gt;

&lt;p&gt;It's signature looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;evaluate2&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;any&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;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&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;jsPath&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You just pass in your puppeteer page object as the 1st parameter and the path to your JavaScript file as the 2nd parameter.&lt;/p&gt;

&lt;p&gt;The library then does the rest, it will create a JS bundle, taking the 2nd parameter as the entry point. The finished bundle is then passed on to the actual &lt;code&gt;page.evaluate&lt;/code&gt; method. Closures and imports are then available.&lt;/p&gt;

&lt;p&gt;Here's an example importing lodash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//code.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&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;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash/chunk&lt;/span&gt;&lt;span class="dl"&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;chunk&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&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;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&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;evaluate2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`./code.js`&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;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//[[1, 2], [3, 4]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;voila 🎉&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The only requisite is that your entry file must export a default function!&lt;/em&gt;&lt;br&gt;
Thanks to the TypeScript API there are checks in place that tell you if you're missing a default function export 💪&lt;/p&gt;

&lt;p&gt;Creating a bundle, entry file,.. this sounds a lot like webpack, and you're right! It is actually using the webpack API under the hood to create the bundle, but in memory and on the fly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final
&lt;/h3&gt;

&lt;p&gt;If you're asking why did I import lodash with &lt;code&gt;require&lt;/code&gt; and not via &lt;code&gt;import&lt;/code&gt;? Because I still consider it more of a POC, hence it only has a &lt;code&gt;beta&lt;/code&gt; version right now, as I also want to have TypeScript support. But I wanted to share it early to get some feedback, so let me know what you think :)&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>framework</category>
    </item>
  </channel>
</rss>
