<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Leonardo Faria</title>
    <description>The latest articles on DEV Community by Leonardo Faria (@leonardofaria).</description>
    <link>https://dev.to/leonardofaria</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%2F39347%2F72252861-bc29-47b8-bcdd-55160d689f8d.jpg</url>
      <title>DEV Community: Leonardo Faria</title>
      <link>https://dev.to/leonardofaria</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leonardofaria"/>
    <language>en</language>
    <item>
      <title>Building TypeScript definitions on demand</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Mon, 07 Feb 2022 18:16:50 +0000</pubDate>
      <link>https://dev.to/leonardofaria/building-typescript-definitions-on-demand-3am2</link>
      <guid>https://dev.to/leonardofaria/building-typescript-definitions-on-demand-3am2</guid>
      <description>&lt;p&gt;I recently came across an interesting problem: is it possible to dynamically create TypeScript definitions for a React component library that doesn't use TypeScript at all? Something like a &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped"&gt;DefinitelyTyped package&lt;/a&gt; for a private NPM package?&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Let me detail the problem a bit better. Consider the Button component below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&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;prop-types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`button--&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="cm"&gt;/** Type of the button */&lt;/span&gt;
 &lt;span class="na"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oneOf&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
 &lt;span class="cm"&gt;/** Content for the button */&lt;/span&gt;
 &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRequired&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component is very straightforward: a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; that will be accordingly styled with the value of appearance.&lt;/p&gt;

&lt;p&gt;Now imagine that a new developer joined the company and they shipped their very first feature with the following Button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"link"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Buy now&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new developer used to work with &lt;a href="https://mui.com/components/buttons/"&gt;Material UI&lt;/a&gt; and &lt;code&gt;variant="link"&lt;/code&gt; came from their old days. No one noticed that the Button didn't look like the others in the application.&lt;/p&gt;

&lt;p&gt;It would be great to have a warning in our editor mentioning that &lt;code&gt;variant&lt;/code&gt; is not a valid prop for &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt;. It would be great to have an autocomplete feature in our editor, so the developer could learn the component API as they work in the codebase. TypeScript solves these problems; however, the codebase doesn't use TS. &lt;/p&gt;

&lt;p&gt;In addition, the Button is imported from a private NPM package. If the repository and packages were public, probably someone would create definitions and add them in the &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped"&gt;DefinitelyTyped&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;Going back to the initial question of this post: is it possible to dynamically create TypeScript definitions for a React component library that doesn't use TypeScript at all? YES!&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;I am using the &lt;a href="https://www.npmjs.com/package/react-to-typescript-definitions"&gt;react-to-typescript-definitions&lt;/a&gt; package to create definitions from the compiled files. The tool can map most PropTypes (&lt;code&gt;any&lt;/code&gt;, &lt;code&gt;array&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;func&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;element&lt;/code&gt;, &lt;code&gt;oneOfType&lt;/code&gt;, &lt;code&gt;arrayOf&lt;/code&gt;, &lt;code&gt;symbol&lt;/code&gt;, &lt;code&gt;shape&lt;/code&gt;) and identify the required ones. The package partially support &lt;code&gt;oneOf&lt;/code&gt; PropType and can even re-use your comments.&lt;/p&gt;

&lt;p&gt;You can create definitions from the command line or import the functionality into a script with this tool. Here is one example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;generateFromFile&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-to-typescript-definitions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;generateFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Button.js&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&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;The function &lt;code&gt;generateFromFile&lt;/code&gt; will return something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonAppearance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secondary&lt;/span&gt;&lt;span class="dl"&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;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// All other props&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * Type of the button
     */&lt;/span&gt;
    &lt;span class="nx"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonAppearance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * Content for the button
     */&lt;/span&gt;
    &lt;span class="nl"&gt;children&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;ReactNode&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;Button&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;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonProps&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;p&gt;A few highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The tool reused the comments ("Type of the button", "Content for the button") from the original component;&lt;/li&gt;
&lt;li&gt;The tool identified appearance as optional and children as required props;&lt;/li&gt;
&lt;li&gt;The tool created a variable with the possible values of appearance;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  In real life
&lt;/h3&gt;

&lt;p&gt;We have two options of usage of this package.&lt;/p&gt;

&lt;h4&gt;
  
  
  CLI
&lt;/h4&gt;

&lt;p&gt;CLI can be helpful for one-off scenarios or for when you want to test things out. The example that I posted above could be created by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat src/components/Button/Button.js | yarn --silent react2dts --top-level-module Button
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Node module
&lt;/h4&gt;

&lt;p&gt;Importing the package into your toolbox scripts gives you the flexibility to customize what needs definitions and define what to do with the data. Here is what I am doing:&lt;/p&gt;

&lt;p&gt;I created a script that imports all components from my main &lt;code&gt;index.js&lt;/code&gt; file (&lt;code&gt;import * as componentLibrary from './src/index.js';&lt;/code&gt;). This &lt;code&gt;index.js&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Avatar&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="s1"&gt;./components/Avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Badge&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="s1"&gt;./components/Badge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Button&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="s1"&gt;./components/Button&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;For each of these components, I call the &lt;code&gt;generateFromFile&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;componentLibrary&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&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;definitionsContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;componentLibrary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;componentLibrary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;componentLibrary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;definitions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;componentLibrary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;definitionsContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;definitionsContent&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;definitions&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;definitionsContent&lt;/code&gt; variable might have some duplicated content, for example, multiple &lt;code&gt;import * as React from 'react';&lt;/code&gt;. After cleaning this, I store the content of this variable in the &lt;code&gt;dist/es/index.d.ts&lt;/code&gt;. Finally, in the &lt;code&gt;package.json&lt;/code&gt;, I mention the location of the types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/es/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/es/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dist/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script is executed in CI environmnent. When someone publishes a new version of the component library:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We build the package using rollup, saving the files in the &lt;code&gt;dist&lt;/code&gt; folder;&lt;/li&gt;
&lt;li&gt;We execute the script to create the TypeScript definitions;&lt;/li&gt;
&lt;li&gt;We run &lt;code&gt;npm publish&lt;/code&gt; (currently with Lerna)&lt;/li&gt;
&lt;li&gt;We make release notes with Lerna&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What do you think of this solution? Let me know in the comments.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Also posted on &lt;a href="https://bit.ly/3uxyLms"&gt;my blog&lt;/a&gt;. If you like this content, follow me on &lt;a href="https://twitter.com/leozera"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/leonardofaria"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>ci</category>
    </item>
    <item>
      <title>Enfrentando a síndrome do impostor e problemas de gerenciamento de tempo</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Sat, 28 Nov 2020 02:21:21 +0000</pubDate>
      <link>https://dev.to/leonardofaria/enfrentando-a-sindrome-do-impostor-e-problemas-de-gerenciamento-de-tempo-3ea8</link>
      <guid>https://dev.to/leonardofaria/enfrentando-a-sindrome-do-impostor-e-problemas-de-gerenciamento-de-tempo-3ea8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is the Portuguese version of the post &lt;a href="/2020/04/26/facing-impostor-syndrome-and-time-management-issues"&gt;Facing importor syndrome and time management issues&lt;/a&gt; that I wrote back on April. A few weeks after sharing my thoughts, Glaucio Oliveira translated the post and shared it on &lt;a href="https://www.linkedin.com/pulse/como-lidar-com-s%C3%ADndrome-do-impostor-e-problemas-de-tempo-oliveira/"&gt;his Linkedin&lt;/a&gt;. Now, I am including the translation in my &lt;a href="https://leonardofaria.net/2020/11/27/enfrentando-a-sindrome-do-impostor-e-problemas-de-gerenciamento-de-tempo/"&gt;blog&lt;/a&gt; and here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Frequentemente ouço pessoas falando sobre síndrome do impostor e problemas de gerenciamento de tempo. Às vezes ouço sobre esses problemas por parte de um amigo, um subordinado, e ocasionalmente de mim mesmo. Somos sempre desafiados por esses problemas, e eles podem afetar nosso ânimo, nossa capacidade de ser produtivo, e nosso julgamento.&lt;/p&gt;

&lt;p&gt;Temos dias onde tudo funciona bem, e também temos dias ruins, onde tudo parece dar errado. Essa montanha-russa emocional pode nos fazer duvidar de nossas realizações e pode criar um medo persistente de sermos expostos como uma fraude.&lt;/p&gt;

&lt;p&gt;Existem algumas coisas que você pode fazer para se lembrar de que você é incrível:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mantenha um diário&lt;/strong&gt; com registros de realizações: esse exercício diário treinará seu cérebro para se sentir mais confiante.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Foque em seus pontos fortes&lt;/strong&gt;, especialmente quando você está em uma nova função no trabalho, use seus talentos naturais para agregar valor a sua função.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Busque vitórias rápidas&lt;/strong&gt;, pois elas o ajudarão a construir uma reputação de alguém adepto de uma habilidade específica.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Problemas com gerenciamento de tempo também afetam nosso senso de ter o trabalho concluído. Às vezes, o problema começa no início, quando nos é atribuída uma tarefa que pode não fazer muito sentido. Vamos para por um momento e pensar nos níveis de incerteza de uma tarefa:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bem esclarecido&lt;/li&gt;
&lt;li&gt;Com algumas dúvidas&lt;/li&gt;
&lt;li&gt;Entendido, porém não familiarizado com a abordagem&lt;/li&gt;
&lt;li&gt;Conhece a área, mas não a direção&lt;/li&gt;
&lt;li&gt;Pouco claro no geral&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Como a incerteza afeta o gerenciamento do tempo? Se algo é bem compreendido, é muito improvável que você fique confuso e as coisas naturalmente tomarão o tempo inicialmente estimado. Começando a fase “com algumas dúvidas”, você precisa ter cuidado, pois a incerteza afetará tanto seu gerenciamento de tempo, quanto seu senso de conquistas. A boa notícia é que você sempre pode usar esse problema como uma oportunidade para melhorar.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Com algumas dúvidas&lt;/strong&gt;: Encontre a melhor pessoa qualificada para responder suas perguntas. Se a descrição da tarefa não for clara o suficiente, vá até a pessoa que a criou. Se a questão é técnica, fale com um desenvolvedor da equipe que pode ajudá-lo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entendido, porém não familiarizado com a abordagem&lt;/strong&gt;: Se você entendeu o panorama geral, mas não está confiante com a abordagem, fale com outro desenvolvedor e peça ajuda. Essa é uma boa oportunidade para “programação em par”.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conhece a área, mas não a direção&lt;/strong&gt;: Essa é uma boa oportunidade para conversar com outra pessoa desenvolvedora e ouvir seus pensamentos iniciais. Elas podem ter uma opinião de como começar a atividade, ou serão capazes de sugerir boas referências para consultar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pouco claro&lt;/strong&gt;: Se o problema na atividade não está claro, esclareça. Se a abordagem técnica não está clara, converse com outras pessoas desenvolvedores, considere ter uma sessão de programação em par, e fale com outras pessoas de sua equipe para entender sua abordagem nesse trabalho.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em geral, síndrome do impostor é algo que todos veremos em algum momento de nossas carreiras. É algo que já lidei, mas acho que enfrentar essa luta oferece uma excelente oportunidade para refletir sobre minhas habilidades e descobrir novas áreas de potencial aprendizado. Com essas sugestões em mente, espero que você consiga melhorar seu senso de confiança e auto-eficácia em sua vida profissional.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Photo credit: &lt;a href="https://pixabay.com/photos/roller-coaster-people-thrill-park-1553342/"&gt;roller coaster (Pixalbay)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>carreira</category>
    </item>
    <item>
      <title>Using Flexbox and text ellipsis together</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Mon, 03 Aug 2020 18:49:13 +0000</pubDate>
      <link>https://dev.to/leonardofaria/using-flexbox-and-text-ellipsis-together-98a</link>
      <guid>https://dev.to/leonardofaria/using-flexbox-and-text-ellipsis-together-98a</guid>
      <description>&lt;p&gt;Another day I was asked to build a table containing a list of files uploaded by the users. Imagine a table similar to the list of files in Finder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QeMR8bDP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/07/finder.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QeMR8bDP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/07/finder.jpg" alt="Finder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second item of the table is &lt;code&gt;mobile-phone-screenshot-long-fine-name.png&lt;/code&gt;, however the column is not big enough to show the complete filename. Instead, we see &lt;code&gt;mobile-phone-sc...g-fine-name.png&lt;/code&gt;. In this project, we decided to cut part of the end of the filename and keeping the extension, as shown above: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cV5ijPvr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/07/filename.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cV5ijPvr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/07/filename.gif" alt="Finder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML markup:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filename"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filename__base"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;this-file-has-a-really-really-really-long-filename.&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"filename__extension"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;pdf&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  CSS:
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.filename&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.filename__base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ellipsis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.filename__extension&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&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;The ellipsis effect can be done by combining &lt;code&gt;text-overflow&lt;/code&gt;, &lt;code&gt;white-space&lt;/code&gt; and &lt;code&gt;overflow&lt;/code&gt; properties, however, we still need to figure out the relation between the filename base and its parent, which also contains the file extension. &lt;/p&gt;

&lt;p&gt;The trick is using the property &lt;code&gt;min-width&lt;/code&gt;, as covered in this pen by &lt;a href="https://codepen.io/aj-foster/pen/emBYPW"&gt;AJ Foster&lt;/a&gt; and &lt;a href="https://css-tricks.com/flexbox-truncated-text/"&gt;CSS Tricks&lt;/a&gt;. From the &lt;a href="https://drafts.csswg.org/css-flexbox/#min-size-auto"&gt;Flexbox spec&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The auto keyword, representing an automatic minimum size, is the new initial value of the min-width and min-height properties. The keyword was previously defined in this specification, but is now defined in the CSS Sizing module.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;In general, the content-based minimum size of a &lt;strong&gt;flex item is the smaller of its content size&lt;/strong&gt; suggestion and its specified size suggestion. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By using &lt;code&gt;min-width: 0&lt;/code&gt;, we are changing the minimum size of the flexbox container, which will resize the child elements of the container that don't use the &lt;code&gt;flex-shrink&lt;/code&gt; attribute. Since the filename base element has the &lt;code&gt;text-overflow&lt;/code&gt;, &lt;code&gt;white-space&lt;/code&gt; and &lt;code&gt;overflow&lt;/code&gt; properties, the ellipsis will be rendered properly.&lt;/p&gt;

&lt;p&gt;The full example is available in &lt;a href="https://codepen.io/leonardofaria/pen/rNxZJad"&gt;this Codepen&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/leonardofaria/embed/rNxZJad?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Also posted on &lt;a href="https://bit.ly/2E32HP9"&gt;my blog&lt;/a&gt;. If you like this content, follow me on &lt;a href="https://twitter.com/leozera"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/leonardofaria"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>flexbox</category>
    </item>
    <item>
      <title>Creating your own ESLint config package</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Wed, 17 Jun 2020 04:01:39 +0000</pubDate>
      <link>https://dev.to/leonardofaria/creating-your-own-eslint-config-package-4c2n</link>
      <guid>https://dev.to/leonardofaria/creating-your-own-eslint-config-package-4c2n</guid>
      <description>&lt;p&gt;ESLint is a powerful tool to enforce consistent coding conventions and ensure quality in your JavaScript codebase. Coding conventions are sometimes difficult to decide and having a tool to automate enforcement is great to avoid unnecessary discussions. Ensuring quality is also a welcoming feature: linters are excellent tools for catching bugs, such as those related to variable scope.&lt;/p&gt;

&lt;p&gt;ESLint is designed to be completely configurable, giving you the option of enabling/disabling each rule, or mixing the rules to match your needs. With this in mind, the JavaScript community and companies who use JavaScript can extend the original ESLint config. There are &lt;a href="https://www.npmjs.com/search?q=eslint-config"&gt;several examples&lt;/a&gt; in the npm registry: &lt;a href="https://www.npmjs.com/package/eslint-config-airbnb"&gt;eslint-config-airbnb&lt;/a&gt; is one of the most famous. &lt;/p&gt;

&lt;p&gt;In your daily basis, you will probably combine more than one config, since there is no one-config-fits-all. This post will show how to create your repository of configurations, giving you the option to centralize all your rule definitions.&lt;/p&gt;

&lt;p&gt;The first step is creating a new folder and creating an npm project. &lt;a href="https://eslint.org/docs/developer-guide/shareable-configs"&gt;By convention&lt;/a&gt;, the module name begins with &lt;code&gt;eslint-config-&lt;/code&gt;, such as &lt;code&gt;eslint-config-test&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;eslint-config-test
&lt;span class="nb"&gt;cd &lt;/span&gt;eslint-config-test
npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will have a package.json file that will look like the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint-config-test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, time to add your ESLint dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; eslint eslint-config-airbnb eslint-config-prettier eslint-plugin-import eslint-plugin-jsx eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks prettier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The packages will change according to your needs. In this case, I work with React codebases and I use &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt; to format my code. The &lt;a href="https://eslint.org/docs/developer-guide/shareable-configs#publishing-a-shareable-config"&gt;documentation&lt;/a&gt; mentions that if your shareable config depends on a plugin, you should also specify it as a &lt;code&gt;peerDependency&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Next, I will create a &lt;code&gt;.eslintrc.js&lt;/code&gt; with my configuration - this is similar to what you already do in your apps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;airbnb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eslint:recommended&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;plugin:import/errors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;plugin:react/recommended&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;plugin:jsx-a11y/recommended&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;plugin:prettier/recommended&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prettier/react&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;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-hooks&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;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rules&lt;/code&gt; object stores any rule that you want to override. In the snippet above &lt;code&gt;rules&lt;/code&gt; is empty but feel free to check &lt;a href="https://github.com/leonardofaria/eslint-config-leozera/blob/master/.eslintrc.js#L14:L58"&gt;my overrides&lt;/a&gt;. In the airbnb/javascript repository you can &lt;a href="https://github.com/airbnb/javascript/issues/1089"&gt;find a list of overridden rules&lt;/a&gt; by the community. &lt;/p&gt;

&lt;p&gt;Time to create a &lt;code&gt;.prettierrc&lt;/code&gt; with your custom rules - this is a tricky part since Prettier and ESLint can conflict in a few rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tabWidth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to mention that the &lt;code&gt;.prettierrc&lt;/code&gt; file should live in the root of the project that is using your package. Right now, I am manually copying it. Next step is exporting your config in the &lt;code&gt;index.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eslintrc&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="s1"&gt;./.eslintrc.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;eslintrc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is technically possible to create all configuration in the &lt;code&gt;index.js&lt;/code&gt; file however you wouldn't get the config object linted (insert your &lt;a href="https://www.imdb.com/title/tt1375666/"&gt;Inception&lt;/a&gt; joke here).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Voilà!&lt;/em&gt; That's all you need to start your own package of configurations. You can test locally your config package by running, in a JavaScript project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; /Users/leonardo/path/to/eslint-config-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind that the dependencies of your configuration package may also be installed.&lt;/p&gt;

&lt;p&gt;If everything looks fine, you can publish to the npm registry:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Full example
&lt;/h2&gt;

&lt;p&gt;I have a functional GitHub project showing the setup of this post: &lt;a href="https://github.com/leonardofaria/eslint-config-leozera"&gt;eslint-config-leozera&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  More about it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://eslint.org/docs/user-guide/configuring"&gt;Configuring ESLint&lt;/a&gt;: official ESLint docs. You know, &lt;em&gt;read the docs&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@bretcameron/how-to-publish-your-first-npm-package-b224296fc57b"&gt;How to publish your first NPM package&lt;/a&gt;: quoting the post subtitle", everything you need to know to create a NPM package"&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wesbos/eslint-config-wesbos"&gt;eslint-config-wesbos&lt;/a&gt;: a project by &lt;a href="https://www.wesbos.com/"&gt;Wes Bos&lt;/a&gt; that help me doing this work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Also posted on &lt;a href="https://bit.ly/2AKW42t"&gt;my blog&lt;/a&gt;. If you like this content, follow me on &lt;a href="https://twitter.com/leozera"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/leonardofaria"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>eslint</category>
      <category>linter</category>
    </item>
    <item>
      <title>Using HAR files to analyze performance over time</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Mon, 08 Jun 2020 16:34:41 +0000</pubDate>
      <link>https://dev.to/leonardofaria/using-har-files-to-analyze-performance-over-time-1i9d</link>
      <guid>https://dev.to/leonardofaria/using-har-files-to-analyze-performance-over-time-1i9d</guid>
      <description>&lt;p&gt;When I consider the performance of a website, the first things that come to mind are; looking at the requests of a page, understanding what resources are being loaded, and how long these resources take to be available to users.&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%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fchrome-network.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%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fchrome-network.jpg" alt="Chrome Network tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The network tab will give you a table containing all assets loaded on the page, as well as relevant information about their origin (domain, HTTP status code, size), who initiated the request, and the order in which they were loaded in a waterfall representation. You can add more information to this table by right-clicking one of the table headers and choosing other columns. &lt;/p&gt;

&lt;p&gt;The size, time and waterfall columns will be crucial to understanding the performance of a page. The size value will present the gzipped size of the resource (when applicable), while the time column shows the total duration from the start of the request to the receipt of the final byte in the response. Last, but not least, the waterfall column demonstrates when the asset is loaded along with the other requests.&lt;/p&gt;

&lt;p&gt;Performance improvements are noticeable by changes in your code/environment. So how do we keep track of what is being analyzed by the Network tab? By exporting the page in HAR format.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a HAR file?
&lt;/h2&gt;

&lt;p&gt;A HAR (short for HTTP Archive) file is a JSON file containing all information about a browser's interactions with a page. It will contain the HTML document and its respective JS and CSS files. Along with this content, a HAR file will also contain all headers' information and the browser metadata (i.e. the time of each request). &lt;/p&gt;

&lt;p&gt;It is important to mention here that cookies and form data will also be logged in the file, so be careful to not include sensitive information (personal details, passwords, credit card numbers) while auditing pages. Also, it is preferable to audit pages in private windows, which avoids browsers' extensions. It is invaluable to avoid a browser's extensions since they may modify the loading times of a page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating HAR files
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Google Chrome
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Close all incognito windows in Google Chrome.&lt;/li&gt;
&lt;li&gt;Open a new incognito window in Google Chrome.&lt;/li&gt;
&lt;li&gt;Go to View &amp;gt; Developer &amp;gt; Developers Tools.&lt;/li&gt;
&lt;li&gt;In the Developer Tools panel, choose the Network tab.&lt;/li&gt;
&lt;li&gt;Check the Preserve Log and Disable cache checkboxes to record all interactions.&lt;/li&gt;
&lt;li&gt;Refresh the page.&lt;/li&gt;
&lt;li&gt;Click the Export HAR (down arrow icon) to export the HAR file.&lt;/li&gt;
&lt;li&gt;Save the HAR file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Firefox
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Close all private windows in Firefox.&lt;/li&gt;
&lt;li&gt;Open a new private window in Firefox.&lt;/li&gt;
&lt;li&gt;Go to Tools &amp;gt; Developer &amp;gt; Network or ctrl-shift-E.&lt;/li&gt;
&lt;li&gt;Refresh the page.&lt;/li&gt;
&lt;li&gt;In the Cog icon (upper right side of the page), choose Save All As Har.&lt;/li&gt;
&lt;li&gt;Save the HAR file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Ffirefox-network.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%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Ffirefox-network.jpg" alt="Firefox Network tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Safari
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure that Show Develop menu in menu bar checkbox is checked under Safari &amp;gt; Preferences &amp;gt; Advanced.&lt;/li&gt;
&lt;li&gt;Choose File &amp;gt; Open New Private Window.&lt;/li&gt;
&lt;li&gt;Visit the web page where the issue occurs.&lt;/li&gt;
&lt;li&gt;Choose Develop &amp;gt; Show Web Inspector. The Web Inspector window appears.&lt;/li&gt;
&lt;li&gt;Refresh the page.&lt;/li&gt;
&lt;li&gt;Click Export on the upper right side of the pane.&lt;/li&gt;
&lt;li&gt;Save the HAR file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fsafari-network.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%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fsafari-network.jpg" alt="Safari Network tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading HAR files
&lt;/h2&gt;

&lt;p&gt;Once you have a HAR file, you can try a few HAR viewers online. My personal favourite is the &lt;a href="http://www.softwareishard.com/har/viewer/" rel="noopener noreferrer"&gt;one created by Jan Odavarko&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fhar-viewer.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%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fhar-viewer.jpg" alt="HAR Viewer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I like about this viewer in particular is the fact you can have multiple files open at the same time, which makes it easier to compare them. &lt;/p&gt;

&lt;h2&gt;
  
  
  Using HAR files to analyze the performance of a page
&lt;/h2&gt;

&lt;p&gt;HAR files can be useful to collect information about the assets of a page. Since you have detailed information about their content, you can compare what has improved (or in some cases, not improved) after a new feature is launched or a redesign is completed, for example. During my workflow, I like to keep track of the final size/time values of a few pages of the product that I am working on.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/tools/chrome-devtools/network/resource-loading#view-network-timing-details-for-a-specific-resource" rel="noopener noreferrer"&gt;Measure Resource Loading Times&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/janodvarko/harviewer" rel="noopener noreferrer"&gt;HAR Viewer source code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Also posted on &lt;a href="https://bit.ly/2zbBPud" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. If you like this content, follow me on &lt;a href="https://twitter.com/leozera" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/leonardofaria" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Cover photo by &lt;a href="https://unsplash.com/photos/oWrZoAVOBS0" rel="noopener noreferrer"&gt;William Daigneault/Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>browser</category>
      <category>har</category>
    </item>
    <item>
      <title>The Mentoring Framework</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Mon, 01 Jun 2020 15:42:35 +0000</pubDate>
      <link>https://dev.to/leonardofaria/the-mentoring-framework-db8</link>
      <guid>https://dev.to/leonardofaria/the-mentoring-framework-db8</guid>
      <description>&lt;p&gt;Last October I created a Mentoring Framework at work. The goal was creating a project from scratch using Ruby on Rails and in this journey, learning more about Ruby, Frontend, Project Management and Git, Databases and DevOps. &lt;/p&gt;

&lt;p&gt;This Mentoring Framework is similar to what bootcamps do but the difference here is I am sharing a list of suggested features / things to learn and the person can focus in what is important for them. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In a nutshell: Goals → Project → Execution → Knowledge (and profit)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The document is divided into 4 main sections: Before start, Project ideas, Execution, Examples and Resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before start
&lt;/h2&gt;

&lt;p&gt;What are your main goals? For the following list, pick: "basic understanding", "intermediate understanding", "advanced understanding". This will help to identify where you are going to invest more time and energy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn Ruby / Ruby on Rails&lt;/li&gt;
&lt;li&gt;Learn Frontend&lt;/li&gt;
&lt;li&gt;Learn Project Management and Git&lt;/li&gt;
&lt;li&gt;Learn Databases&lt;/li&gt;
&lt;li&gt;Learn DevOps&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project ideas
&lt;/h2&gt;

&lt;p&gt;Once you know your main goals, you need a project. Check the following two ideas:&lt;/p&gt;

&lt;h3&gt;
  
  
  Book store
&lt;/h3&gt;

&lt;p&gt;Imagine you want to sell books online. What kind of features does a bookstore have?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List books per title&lt;/li&gt;
&lt;li&gt;List books per authors&lt;/li&gt;
&lt;li&gt;List books per categories&lt;/li&gt;
&lt;li&gt;Search books&lt;/li&gt;
&lt;li&gt;Buy books&lt;/li&gt;
&lt;li&gt;List your orders&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pet adoption website
&lt;/h3&gt;

&lt;p&gt;Imagine you want to create a website for an organisation that takes care of animals (in British Columbia we have SPCA). What kind of features does a pet adoption website have?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List pets per type (dog, cat and others)&lt;/li&gt;
&lt;li&gt;List pets per size (or breed, or age)&lt;/li&gt;
&lt;li&gt;List pets per location&lt;/li&gt;
&lt;li&gt;Search pet per name&lt;/li&gt;
&lt;li&gt;Request an adoption&lt;/li&gt;
&lt;li&gt;List your adoption requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;

&lt;p&gt;Once you know your main goals and you have an idea, it is time to work. Check the list of tasks you will be doing:&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn Ruby on Rails
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use scaffolds to create your entities (basic understanding) or create your own controllers, models and views (intermediate)&lt;/li&gt;
&lt;li&gt;Add authentication with Devise (basic understanding) or create your own (intermediate). Or use Devise with Oauth authentication (Facebook, Google, etc - intermediate)&lt;/li&gt;
&lt;li&gt;Add friendly URLs with a gem (basic) or create your own solution (intermediate)&lt;/li&gt;
&lt;li&gt;Add search (intermediate)&lt;/li&gt;
&lt;li&gt;Add tests for all your code&lt;/li&gt;
&lt;li&gt;Create seeds for your data so you don't need to create manually data&lt;/li&gt;
&lt;li&gt;Send email to users (intermediate)&lt;/li&gt;
&lt;li&gt;Create different user roles (intermediate) &lt;/li&gt;
&lt;li&gt;Add pagination (basic)&lt;/li&gt;
&lt;li&gt;Add localization (intermediate)&lt;/li&gt;
&lt;li&gt;Add a tag system (intermediate)&lt;/li&gt;
&lt;li&gt;Add images (intermediate)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Learn Frontend
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use Tailwind CSS (basic) or your own CSS/SASS (intermediate) to create a beautiful layout&lt;/li&gt;
&lt;li&gt;Use Turbolinks (advanced / optional)&lt;/li&gt;
&lt;li&gt;Add basic animations - ex.: when adding a book in the cart show some animation (advanced/optional)&lt;/li&gt;
&lt;li&gt;Add Google Maps in your app (intermediate)&lt;/li&gt;
&lt;li&gt;Add Google Places in your users&lt;/li&gt;
&lt;li&gt;Add a carousel with photos&lt;/li&gt;
&lt;li&gt;Add Pagination with Ajax or endless pagination (advanced)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Learn Project Management and Git
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use Github Project to organize your features&lt;/li&gt;
&lt;li&gt;Use feature branches in Git, create PR for all your features&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Learn Databases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Write plain SQL to find relevant information. A few examples:

&lt;ul&gt;
&lt;li&gt;Find best-sellers authors in the book store&lt;/li&gt;
&lt;li&gt;Find the number of orders or pet requests per city&lt;/li&gt;
&lt;li&gt;Find which day has more sales&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Add charts in your app to expose the data&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Learn DevOps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deploy the website to Heroku (easy/intermediate) or AWS (advanced)&lt;/li&gt;
&lt;li&gt;Setup CI in Heroku (easy/intermediate) &lt;/li&gt;
&lt;li&gt;Add Airbrake to monitor errors in your app (easy)&lt;/li&gt;
&lt;li&gt;Buy a domain, set up Cloudflare (advanced/optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&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%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fthe-mentoring-framework.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%2Fleonardofaria.net%2Fwp-content%2Fuploads%2F2020%2F06%2Fthe-mentoring-framework.png" alt="Website screenshots"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://adopt.spca.bc.ca" rel="noopener noreferrer"&gt;SPCA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.petfinder.com/" rel="noopener noreferrer"&gt;Petfinder&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Courses: &lt;a href="https://www.linkedin.com/learning/ruby-on-rails-5-essential-training" rel="noopener noreferrer"&gt;Ruby on Rails 5 Essential Training&lt;/a&gt; and &lt;a href="https://www.linkedin.com/learning/ruby-on-rails-6-essential-training" rel="noopener noreferrer"&gt;Ruby on Rails 6 Essential Training&lt;/a&gt; - The first course is longer than the second.&lt;/li&gt;
&lt;li&gt;Screencasts: &lt;a href="https://rubytapas.com" rel="noopener noreferrer"&gt;RubyTapas&lt;/a&gt;, &lt;a href="https://gorails.com" rel="noopener noreferrer"&gt;GoRails&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Also posted on &lt;a href="https://bit.ly/2XI2BCE" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. If you like this content, follow me on &lt;a href="https://twitter.com/leozera" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/leonardofaria" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Cover photo by &lt;a href="https://unsplash.com/photos/taiuG8CPKAQ" rel="noopener noreferrer"&gt;Maxwell Nelson at Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mentoring</category>
      <category>career</category>
    </item>
    <item>
      <title>Using Dependabot to keep your environment up to date</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Sat, 02 May 2020 14:41:56 +0000</pubDate>
      <link>https://dev.to/leonardofaria/using-dependabot-to-keep-your-environment-up-to-date-1d08</link>
      <guid>https://dev.to/leonardofaria/using-dependabot-to-keep-your-environment-up-to-date-1d08</guid>
      <description>&lt;p&gt;Adding dependencies in a project is seen as a good way to not reinvent the wheel but at the same time it can be concerning in many different aspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Versioning: sometimes dependencies can require specific versions of other dependencies and this can cause hiccups in your app;&lt;/li&gt;
&lt;li&gt;Bundling: you need to be careful here to not bring extra code that will bloat your bundles;&lt;/li&gt;
&lt;li&gt;Updating: JavaScript moves fast and if you don't update packages regularly, you'll play Jenga in the future.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are different tools to cover the update part like &lt;a href="https://dependencies.io"&gt;Dependencies.io&lt;/a&gt;, &lt;a href="https://snyk.io/"&gt;Snyk&lt;/a&gt; and &lt;a href="https://dependabot.com/"&gt;Dependabot&lt;/a&gt;. Since I have been using Dependabot for a while, I decided to write about my experience.&lt;/p&gt;

&lt;p&gt;Dependabot is a tool acquired by GitHub one year ago that checks dependencies files from different languages (Ruby, JavaScript, Python, PHP, Elixir, to name a few) and finds new versions of libraries you are using in your project. Here is the setup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RHxqWl8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/05/dependabot.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RHxqWl8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/05/dependabot.jpg" alt="Dependabot screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Daily updates can be overwhelming and I think that weekly updates have a better cost/benefit. Also, I assign myself the Pull Requests so I can get notifications as soon they are opened.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Dependabot effectively
&lt;/h2&gt;

&lt;p&gt;Dependabot includes in each PR release notes, changelogs, commits links and vulnerability details whenever available. This is useful because you can take a look at the information and decide to proceed or not.&lt;/p&gt;

&lt;p&gt;However, as pragmatic programmers, we want to ensure things won't break. The PR details are important but more than that, we want a simulation of all (or almost all) deliverables that the project has.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2DOo2HV3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/05/semaphore.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2DOo2HV3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2020/05/semaphore.jpg" alt="CI Integration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This screenshot shows what happens every time a PR is opened in the components library codebase of my work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tests (Jest / Bundle)&lt;/strong&gt;: the Jest task will test the React components while the Bundle task will simulate the bundling commands we run when we want to update the package in the NPM registry;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linters (Stylesheets / JavaScript)&lt;/strong&gt;: the stylesheet files follow a custom sass-lint setup and the JS code follows a series of ESLint rules. If a PR introduces a new version of a linter with new rules we will be able to capture that;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cypress (Screenshot Testing / Accessibility Testing)&lt;/strong&gt;: if a new package introduces changes that may reflect in the look and feel of components, Cypress will capture the difference, screenshot it and store in S3. Since Cypress needs a live version of the documentation website, we also get the Gatsby build process covered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all these steps, it is very unlikely an external package will break our master branch. Kudos to my co-worker Grant Lee that also works in this project.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Also posted on &lt;a href="https://bit.ly/2ZhD9GC"&gt;my blog&lt;/a&gt;. If you like this content, follow me on &lt;a href="https://twitter.com/leozera"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/leonardofaria"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Facing impostor syndrome and time management issues</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Sun, 26 Apr 2020 15:28:33 +0000</pubDate>
      <link>https://dev.to/leonardofaria/facing-impostor-syndrome-and-time-management-issues-1mlj</link>
      <guid>https://dev.to/leonardofaria/facing-impostor-syndrome-and-time-management-issues-1mlj</guid>
      <description>&lt;p&gt;I often hear people talking about impostor syndrome and time management issues. Sometimes I hear about these issues from a friend, a direct report, and occasionally from myself. We are always challenged by them and they can affect our mood, our ability to be productive and our judgement.&lt;/p&gt;

&lt;p&gt;We have days where everything works great and also we have bad days, where everything seems to go wrong. This emotional roller coaster can make us doubt our accomplishments and can create a persistent fear of being exposed as a fraud.&lt;/p&gt;

&lt;p&gt;There a few things that you can do to remember that you are great:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep a daily journal&lt;/strong&gt; with notes of achievements: this daily exercise will train your brain to feel more confident;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on your strengths&lt;/strong&gt;, especially when you are new in a role at work, use your natural strengths to add value to your position;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find quick wins&lt;/strong&gt;, they will help you build a reputation of someone adept in a particular skill.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issues with time management also impact our feeling of getting the job done. Sometimes the problem starts early on when we are assigned a task that may not make much sense. Let's step back for a moment and think about the levels of uncertainty of a task:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Well understood&lt;/li&gt;
&lt;li&gt;Have a few questions&lt;/li&gt;
&lt;li&gt;Understood, but unfamiliar with the approach&lt;/li&gt;
&lt;li&gt;Know the area, but not the direction&lt;/li&gt;
&lt;li&gt;General unclear&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How does uncertainty affect time management? If something is Well understood, it is very unlikely you will get lost in the woods and things will take as long as expected. Starting with the level of Have a few questions, you need to be careful because the uncertainty will affect your time management and your sense of achievement. Good news is, you can always use this problem as an opportunity to improve.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Have a few questions&lt;/strong&gt;: Find the best person to answer your question. If the description is unclear, go to the person who created that task. If the question is technical, talk to a developer on the team that can help you;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understood, but unfamiliar with the approach&lt;/strong&gt;: If you understand the big picture but you are not confident with the approach, talk to another developer and ask for help. This is a good opportunity for pair programming;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Know the area, but not the direction&lt;/strong&gt;: This is a good opportunity to talk to another developer and hear their initial thoughts. They may have an opinion on how to get the task started or be able to suggest a good resource to review;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;General unclear&lt;/strong&gt;: If the problem in the task is generally unclear, ask for clarification. If the technical approach is unclear, talk to other developers, think about having a pair programming session, and speak with other members of your team to understand how they approach their work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, facing imposter syndrome is something that we all face at some point in our careers. It is an issue that I have certainly struggled with but find that facing these struggles provides an excellent opportunity for me to reflect on my skills and discover new areas of potential learning. With these suggestions in mind, I hope that you are able to improve your sense of confidence and self-efficacy in your professional life.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;Also posted on &lt;a href="https://bit.ly/355gluB"&gt;my blog&lt;/a&gt;. If you like this content, follow me on &lt;a href="https://twitter.com/leozera"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/leonardofaria"&gt;GitHub&lt;/a&gt;. Photo credit: roller coaster (Pixalbay)&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>Exploring device detection for better user experiences in 2020</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Wed, 15 Jan 2020 05:16:54 +0000</pubDate>
      <link>https://dev.to/leonardofaria/exploring-device-detection-for-better-user-experiences-in-2020-ken</link>
      <guid>https://dev.to/leonardofaria/exploring-device-detection-for-better-user-experiences-in-2020-ken</guid>
      <description>&lt;p&gt;A few months ago I watched a &lt;a href="https://www.youtube.com/embed/puUPpVrIRkc?start=1389"&gt;great talk in Chrome Dev Summit about performance in slow devices&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It blew my mind all the work done by Facebook in identifying devices to create a better user experience. Fast-forward to now, I decided to study a bit more the topic and see what I could do at Thinkific (the company I work for and it is &lt;a href="http://bit.ly/thnk-senior-front-end-eng"&gt;hiring&lt;/a&gt; &lt;a href="http://bit.ly/thnk-senior-full-stack-eng"&gt;for&lt;/a&gt; &lt;a href="https://www.thinkific.com/careers/"&gt;several positions&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  User agents
&lt;/h2&gt;

&lt;p&gt;User agents are well-known by developers. We use them to detect bots, redirect users to a specific version of our website or append CSS classes on our page so we can create different experiences.&lt;/p&gt;

&lt;p&gt;At Thinkific we already use the &lt;a href="https://github.com/fnando/browser"&gt;browser Ruby gem&lt;/a&gt; to parse the user-agent and get relevant info (bot detection for instance). So, I decided to persist the main info in a visitor_device table – here is the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tenant_id: the course creator school the visitor is checking
raw: the raw ua
type: desktop / mobile / tablet / bot / other
browser_name
browser_version
platform_name
platform_version
hardware: hstore containing memory, processor, device_model, device_name
connection: hstore containing downlink_max, connection_type
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You probably noticed that a few things there are not available in the UA string. Time for new JavaScript APIs:&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting hardware info using JavaScript
&lt;/h2&gt;

&lt;p&gt;As covered in the Chrome Dev Summit video, we can use JS to get this info&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;navigator.deviceMemory&lt;/code&gt; will return a floating-point number. There are things to consider here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It only works over HTTPS&lt;/li&gt;
&lt;li&gt;Support is quite limited (Chrome only basically)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/w3c/device-memory"&gt;Spec from W3C&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/deviceMemory"&gt;MDN Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/#feat=mdn-api_navigator_devicememory"&gt;Can I use deviceMemory&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Processors
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;navigator.hardwareConcurrency&lt;/code&gt; will return the number of logical cores of the user’s CPU. Support for this is &lt;a href="https://caniuse.com/#feat=hardwareconcurrency"&gt;decent&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting connection info using JavaScript
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;navigator.connection&lt;/code&gt; is a new API containing information about the system’s connection, such as the current bandwidth of the user’s device or whether the connection is metered. The support is quite limited (Chrome only basically) but things are promising.&lt;/p&gt;

&lt;p&gt;More about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://googlechrome.github.io/samples/network-information/"&gt;Chrome example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection"&gt;MDN Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/#feat=netinfo"&gt;Can I use Network Information API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Detecting the device model
&lt;/h2&gt;

&lt;p&gt;The user agent &lt;em&gt;may&lt;/em&gt; return some information about the model name. &lt;a href="https://userstack.com/"&gt;userstack&lt;/a&gt; is a service that gives you information based on the user agent. It works well and it is easy to integrate, however, depending on your need, they can’t help.&lt;/p&gt;

&lt;p&gt;Take for instance iDevices. Their user agent is basically the same so you can’t differentiate an iPad Pro from an old iPad that runs the last iOS. For these cases, you may need a better detection based on resolution, pixel density and other hardware information exposed in the browser. I did a quick research on this and found 3 products so far: &lt;a href="https://web.wurfl.io/#wurfl-js"&gt;WURFL.io&lt;/a&gt;, &lt;a href="https://deviceatlas.com/products/web"&gt;DeviceAtlas&lt;/a&gt; and &lt;a href="https://51degrees.com/"&gt;51Degrees&lt;/a&gt;. I didn’t have time to try their products yet, but I am looking forward to doing it (and post about it)&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Question: Why not using Google Analytics / Mixpanel / Kibana / New Relic / your tool here?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We could get browser info inside other tools however as a SaaS product we don’t use our own Google Analytics property (customers add their own). Also, adblockers may block these third-party tools. Last not least, by having this info in our side we can adapter better.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Question: Do you have a list of low-end/high-end devices?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No. Maybe this can be built combining the number of processors and memory but I didn’t invest much time on this. In this project, my colleague created a Rails helper that would determinate if the user would use the lite or default version of a website based on hardware. On this topic, it is important to mention Facebook has a library for Android called &lt;a href="https://github.com/facebook/device-year-class/"&gt;Device Year Class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://leonardofaria.net/2020/01/15/exploring-device-detection-for-better-user-experiences-in-2020/"&gt;Exploring device detection for better user experiences in 2020&lt;/a&gt; appeared first on &lt;a href="https://leonardofaria.net"&gt;leonardofaria.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>api</category>
      <category>performance</category>
    </item>
    <item>
      <title>A simple Sinatra Auth</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Mon, 04 Nov 2019 04:40:07 +0000</pubDate>
      <link>https://dev.to/leonardofaria/a-simple-sinatra-auth-4lfb</link>
      <guid>https://dev.to/leonardofaria/a-simple-sinatra-auth-4lfb</guid>
      <description>&lt;p&gt;Imagine you want to protect the content of a website created with Next.js, Hugo or your favourite static site generator. Github doesn’t offer this feature or Netlify offers authentication only in their paid plans. &lt;a href="https://bit.ly/sinatra-auth"&gt;Decided to create something simple with Sinatra, TailwindCSS and ready to go in Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bit.ly/sinatra-auth"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--78Y0EVkb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2019/11/sinatra-auth.jpg" alt="Sinatra Auth screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also posted on &lt;a href="http://bit.ly/2K1NHBc"&gt;my blog&lt;/a&gt;. Follow me on &lt;a href="https://twitter.com/leozera"&gt;Twitter&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sinatra</category>
      <category>ruby</category>
      <category>heroku</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Creating a website for my Apple Collection</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Thu, 17 Oct 2019 03:36:30 +0000</pubDate>
      <link>https://dev.to/leonardofaria/creating-a-website-for-my-apple-collection-50nc</link>
      <guid>https://dev.to/leonardofaria/creating-a-website-for-my-apple-collection-50nc</guid>
      <description>&lt;p&gt;&lt;em&gt;Also posted on &lt;a href="http://bit.ly/collection-post"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A while ago I started an Apple Collection. After a few laptops and a few iDevices, I decided that I should start collecting info about my iThings. In the beginning, I created a gist containing model, serial number, how did I get the device, minimum/maximum OS, etc.&lt;/p&gt;

&lt;p&gt;The list kept going bigger and bigger and the content started looking messy. The natural way to organize my data would be inside a SQL database, with the information distributed in columns. After organizing the information in tables, I would create a graphQL API (the one cool kids use these days) to give me the data needed to populate my UI – probably written in React, compiled with Babel and packed with webpack.&lt;/p&gt;

&lt;p&gt;Reading the previous paragraph aloud, you can hear many technologies and I even ignored the backend language and UI details like SASS or styled-components. It sounds a bit overwhelming when the ultimate goal is showing a list of items in a nice design.&lt;/p&gt;

&lt;p&gt;That’s being said, I thought: how can I deliver this content without:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An API or any backend work&lt;/li&gt;
&lt;li&gt;Any JS framework/library&lt;/li&gt;
&lt;li&gt;Any JS tooling (webpack, babel, etc)&lt;/li&gt;
&lt;li&gt;Any CSS work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of these constraints, I had a few stretch goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a website with good accessibility&lt;/li&gt;
&lt;li&gt;Create a website that works in old browsers, since I have computers running Mac OS 9.2 and iDevices running iOS 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Challenge accepted. One index.html, a few vanilla JS files, no custom CSS. TL,DR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bit.ly/collection-website"&gt;Final website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bit.ly/collection-source"&gt;Source code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  No API or any backend work
&lt;/h2&gt;

&lt;p&gt;A while ago I saw a SaaS product called &lt;a href="https://steinhq.com/"&gt;Stein&lt;/a&gt;. You create your data inside a Google Sheets document and they give you an endpoint with your data. Their library works like handlebars and it looks perfect for my use case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-stein-url=&lt;/span&gt;&lt;span class="s"&gt;"https://api.steinhq.com/v1/storages/5cc158079ec99a2f484dcb40/Sheet1"&lt;/span&gt; &lt;span class="na"&gt;data-stein-limit=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{{title}}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h6&amp;gt;&lt;/span&gt;By {{author}}&lt;span class="nt"&gt;&amp;lt;/h6&amp;gt;&lt;/span&gt;

    {{content}}

    Read on &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{link}}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Medium&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  No JS framework/library and tooling
&lt;/h2&gt;

&lt;p&gt;Decided to avoid adding a framework or library in this project since the use case didn’t need one. All JS interactions on this page are quite simple (show/hide menus, open a modal screen, handle permalinks).&lt;/p&gt;

&lt;p&gt;Since I was not using a framework/library, I would avoid adding webpack and babel. No need to dig into presets and loaders.&lt;/p&gt;

&lt;p&gt;PS. You can argue that I’d have chosen create-react-app or Next.js and get all these problems solved, but no.&lt;/p&gt;

&lt;h2&gt;
  
  
  No CSS work
&lt;/h2&gt;

&lt;p&gt;I love writing CSS, especially when I can use SASS but I decided to do not write CSS here. I had a few good reasons to avoid doing it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had no designs and despite I could do something decent-looking, I didn’t want to put time and energy on it;&lt;/li&gt;
&lt;li&gt;I wanted to use &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you never heard about Tailwind CSS, please don’t try to think “it is a Bootstrap option”. Here is a good short explanation, from their website:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Most CSS frameworks do too much.&lt;br&gt;&lt;br&gt;
…&lt;br&gt;&lt;br&gt;
Instead of opinionated predesigned components, Tailwind provides low-level utility classes that let you build completely custom designs without ever leaving your HTML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is pretty much true. A quick search gives you many web apps “rebuilt” with Tailwind CSS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/whatsapp-web-clone"&gt;Whatsapp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/telegram-desktop-using-tailwindcss"&gt;Telegram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/facebook-clone"&gt;Facebook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/reddit-clone"&gt;Reddit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/youtube-clone"&gt;Youtube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/slack-clone-1"&gt;Slack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/coinbase-clone"&gt;Coinbase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/github-profile-clone"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcomponents.com/component/trello-panel-clone"&gt;Trello&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codepen.io/drehimself/full/vpeVMx/"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=_JhTaENzfZQ"&gt;Netlify&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a website with good accessibility
&lt;/h2&gt;

&lt;p&gt;Last month I started taking accessibility courses at &lt;a href="https://dequeuniversity.com/curriculum/packages/full"&gt;Deque University&lt;/a&gt;. Their content is great and it reminders me that &lt;strong&gt;HTML is accessible by default&lt;/strong&gt;. By using a semantic HTML structure and testing basic things like keyboard navigation and colour contrast you eliminate several barries that move people with disabilities from your content. I am not an accessibility expert but here are a few accessibility-related things I’ve worked on this website:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disable stylesheets: by disabling stylesheets you can ensure that your content follows a logical/structural way.&lt;/li&gt;
&lt;li&gt;VoiceOver: VoiceOver is included in macOS and iOS. It is &lt;a href="https://webaim.org/articles/voiceover/"&gt;very simple to use it&lt;/a&gt; and by using it you can have a better understanding of how people use this feature. &lt;/li&gt;
&lt;li&gt;Modals: modals can be problematic. Decided to follow &lt;a href="https://bitsofco.de/accessible-modal-dialog/"&gt;Ire Aderinokun’s&lt;/a&gt; approach.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd"&gt;axe&lt;/a&gt;: the extension is an accessibility checker for WCAG 2 and Section 508 accessibility rules. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is not perfect, there are a few things that I didn’t work it, like adding a skip link to the main content. If you are curious, &lt;a href="https://github.com/leonardofaria/collection/pull/1"&gt;here is the Pull Request with all the changes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a website that works in old browsers
&lt;/h2&gt;

&lt;p&gt;I couldn’t achieve this objective since I had no control over scripts and styles. However, it doesn’t seem to be impossible. A few things I noticed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/SteinHQ/Expedite"&gt;Expedite&lt;/a&gt; (Stein client) uses &lt;a href="https://github.com/SteinHQ/Expedite/blob/master/index.js#L51-L54"&gt;fetch&lt;/a&gt;, which was only &lt;a href="https://caniuse.com/#feat=fetch"&gt;added in Safari 10&lt;/a&gt;. The request to their server could be probably replaced for an XMLHttpRequest.&lt;/li&gt;
&lt;li&gt;Tailwind uses flexbox in many elements. Safari only started supporting Flexbox in iOS 7. Maybe I could write a few properties to their existing elements to achieve a decent look.&lt;/li&gt;
&lt;li&gt;SSL Certificates may be an issue to old browsers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Making this website was super fun. The fact I added “constraints” to it made think outside the box. Out of curiosity, I tracked my time using &lt;a href="https://clockify.me"&gt;Clockify&lt;/a&gt; and I’ve worked 13h on this, between coding, creating the data, testing and writing this post.&lt;/p&gt;

</description>
      <category>netlify</category>
      <category>tailwindcss</category>
      <category>steinjs</category>
      <category>vanillajs</category>
    </item>
    <item>
      <title>Forcing the usage of yarn (and at a specific version)</title>
      <dc:creator>Leonardo Faria</dc:creator>
      <pubDate>Fri, 11 Oct 2019 03:40:46 +0000</pubDate>
      <link>https://dev.to/leonardofaria/forcing-the-usage-of-yarn-and-at-a-specific-version-pne</link>
      <guid>https://dev.to/leonardofaria/forcing-the-usage-of-yarn-and-at-a-specific-version-pne</guid>
      <description>&lt;p&gt;&lt;em&gt;Also published in &lt;a href="http://bit.ly/2VH2g1P"&gt;my blog&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;People and organizations often have preferences for a specific package manager. At work, we decided to use Yarn due to emoji support (jk) but how to keep everybody using Yarn? &lt;/p&gt;

&lt;p&gt;We can use the &lt;a href="https://docs.npmjs.com/misc/scripts"&gt;preinstall hook&lt;/a&gt; to check if the user run &lt;code&gt;npm install&lt;/code&gt;or &lt;code&gt;yarn install&lt;/code&gt;. Here is one example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preinstall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node -e &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;if(process.env.npm&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;_execpath.indexOf('yarn') === -1) throw new Error('You must use Yarn to install, not NPM')&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you run &lt;code&gt;npm install&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Uor-MhRY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2019/10/npm-install.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Uor-MhRY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2019/10/npm-install.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to ignore the checking (CI environment for instance), use the &lt;code&gt;--ignore-scripts&lt;/code&gt; option: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install --ignore-scripts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Moreover, you can use the &lt;a href="https://docs.npmjs.com/files/package.json#engines"&gt;engines option&lt;/a&gt; of NPM to force a specific version of Node, and/or Yarn. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;engines&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yarn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;1.19.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;12&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;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LZT20m_b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2019/10/npm-engines.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LZT20m_b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://leonardofaria.net/wp-content/uploads/2019/10/npm-engines.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>npm</category>
      <category>yarn</category>
    </item>
  </channel>
</rss>
