<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Takuya Matsuyama</title>
    <description>The latest articles on Forem by Takuya Matsuyama (@craftzdog).</description>
    <link>https://forem.com/craftzdog</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%2F51532%2F5678e586-bad9-4271-b915-72336be509e8.jpg</url>
      <title>Forem: Takuya Matsuyama</title>
      <link>https://forem.com/craftzdog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/craftzdog"/>
    <language>en</language>
    <item>
      <title>How to specify pre-processor flags to the JSI-based React Native libraries</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Thu, 29 Sep 2022 05:07:07 +0000</pubDate>
      <link>https://forem.com/craftzdog/how-to-specify-pre-processor-flags-to-the-jsi-based-react-native-libraries-51m5</link>
      <guid>https://forem.com/craftzdog/how-to-specify-pre-processor-flags-to-the-jsi-based-react-native-libraries-51m5</guid>
      <description>&lt;p&gt;Hi, it's Takuya.&lt;br&gt;
I'm building a note-taking app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop&lt;/a&gt; for mobile using React Native.&lt;/p&gt;

&lt;p&gt;The app uses react-native-quick-sqlite, a fast wrapper of SQLite:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ospfranco" rel="noopener noreferrer"&gt;
        ospfranco
      &lt;/a&gt; / &lt;a href="https://github.com/ospfranco/react-native-quick-sqlite" rel="noopener noreferrer"&gt;
        react-native-quick-sqlite
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Fast SQLite for react-native.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;New package &lt;a href="https://github.com/OP-Engineering/op-sqlite" rel="noopener noreferrer"&gt;op-sqlite&lt;/a&gt;. It is a lot faster and consumes much less memory.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ospfranco/react-native-quick-sqlite" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;It embeds the latest version of SQLite in C/C++.&lt;br&gt;
That means that you build SQLite from source in your RN project on Xcode and Android Studio (CMake).&lt;/p&gt;

&lt;p&gt;It doesn't enable any compile-time options by default, and I needed to enable FTS5 for full-text search in my app.&lt;br&gt;
It can be enabled by &lt;a href="https://www.sqlite.org/fts5.html" rel="noopener noreferrer"&gt;specifying a &lt;code&gt;SQLITE_ENABLE_FTS5&lt;/code&gt; pre-processor flag when compiling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've managed to enable FTS5 and I'd like to share how to do that in your project.&lt;br&gt;
For Android, the library needs to make some changes. So, it'd be helpful for library maintainers who would support accepting custom build flags.&lt;/p&gt;
&lt;h2&gt;
  
  
  iOS
&lt;/h2&gt;

&lt;p&gt;Fortunately, CocoaPods allows you to customize the pods project settings via &lt;code&gt;Podfile&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://guides.cocoapods.org/syntax/podfile.html" rel="noopener noreferrer"&gt;https://guides.cocoapods.org/syntax/podfile.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add a &lt;code&gt;post_install&lt;/code&gt; block to your &lt;code&gt;&amp;lt;PROJECT_ROOT&amp;gt;/ios/Podfile&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;post_install&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;installer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pods_project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"react-native-quick-sqlite"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_configurations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'GCC_PREPROCESSOR_DEFINITIONS'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'$(inherited)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;SQLITE_FLAGS&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Replace the &lt;code&gt;&amp;lt;SQLITE_FLAGS&amp;gt;&lt;/code&gt; part with flags you want to add.&lt;br&gt;
For example, you could add &lt;code&gt;SQLITE_ENABLE_FTS5=1&lt;/code&gt; to &lt;code&gt;GCC_PREPROCESSOR_DEFINITIONS&lt;/code&gt; to enable FTS5 in the iOS project.&lt;/p&gt;
&lt;h2&gt;
  
  
  Android
&lt;/h2&gt;

&lt;p&gt;The native modules are compiled with CMake that reads &lt;code&gt;CMakeLists.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;CMake supports adding flags with &lt;a href="https://cmake.org/cmake/help/v3.0/command/add_definitions.html" rel="noopener noreferrer"&gt;&lt;code&gt;add_definitions&lt;/code&gt; command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add the following lines in the library's CMakeLists.txt:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add_definitions(
  ${SQLITE_FLAGS}
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It specifies flags via the variable &lt;code&gt;SQLITE_FLAGS&lt;/code&gt;, which needs to be specified in &lt;code&gt;react-native-quick-sqlite/android/build.gradle&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;SQLITE_FLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'quickSqliteFlags'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;externalNativeBuild&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cmake&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="s1"&gt;'-DANDROID_STL=c++_shared'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
              &lt;span class="s2"&gt;"-DREACT_NATIVE_VERSION=${REACT_NATIVE_VERSION}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
              &lt;span class="s2"&gt;"-DNODE_MODULES_DIR=${nodeModules}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
              &lt;span class="s2"&gt;"-DSQLITE_FLAGS='${SQLITE_FLAGS ? SQLITE_FLAGS : ''}'"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, you can define the flags in your &lt;code&gt;&amp;lt;PROJECT_ROOT&amp;gt;/android/gradle.properties&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quickSqliteFlags="&amp;lt;SQLITE_FLAGS&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's it!&lt;br&gt;
I've sent a pull request to the quick sqlite repository:&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/ospfranco/react-native-quick-sqlite/pull/86" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        feat(ios&amp;amp;android): Support specifying pre-processor flags to sqlite
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#86&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/craftzdog" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F1332805%3Fv%3D4" alt="craftzdog avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/craftzdog" rel="noopener noreferrer"&gt;craftzdog&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/ospfranco/react-native-quick-sqlite/pull/86" rel="noopener noreferrer"&gt;&lt;time&gt;Sep 29, 2022&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;I've managed to enable FTS5 on both iOS and Android by specifying pre-processor flags.&lt;/p&gt;
&lt;p&gt;On Android, we need to accept a setting from the parent project.
This PR makes it possible to specify SQLite flags via &lt;code&gt;android/gradle.properties&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Also, I've added instructions on how to do that for iOS and Android.
Maybe the grammar could be improved tho.&lt;/p&gt;
&lt;p&gt;fix #37&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ospfranco/react-native-quick-sqlite/pull/86" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Hope that helps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Follow me online
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Check out my app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop — A Markdown note-taking app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;https://twitter.com/inkdrop_app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog &lt;a href="https://blog.inkdrop.app/" rel="noopener noreferrer"&gt;https://blog.inkdrop.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;YouTube &lt;a href="https://www.youtube.com/devaslife" rel="noopener noreferrer"&gt;https://www.youtube.com/devaslife&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;https://instagram.com/craftzdog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>reactnative</category>
      <category>ios</category>
      <category>android</category>
      <category>sqlite</category>
    </item>
    <item>
      <title>My Neovim setup for React, TypeScript, Tailwind CSS, etc</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Tue, 09 Aug 2022 10:35:56 +0000</pubDate>
      <link>https://forem.com/craftzdog/my-neovim-setup-for-react-typescript-tailwind-css-etc-58fb</link>
      <guid>https://forem.com/craftzdog/my-neovim-setup-for-react-typescript-tailwind-css-etc-58fb</guid>
      <description>&lt;p&gt;Hi, it's &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Takuya&lt;/a&gt; here.&lt;br&gt;
As you may know, I mainly use Neovim to code my app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop&lt;/a&gt;, a cross-platform Markdown note-taking app.&lt;br&gt;
It's built with Electron for desktop and React Native for mobile platforms.&lt;br&gt;
It's been 1 year since I last posted &lt;a href="https://blog.inkdrop.app/how-to-set-up-neovim-0-5-modern-plugins-lsp-treesitter-etc-542c3d9c9887" rel="noopener noreferrer"&gt;my Neovim setup&lt;/a&gt;.&lt;br&gt;
Neovim and its plugins have been evolved so well.&lt;br&gt;
So, I'd like to share my latest setup for coding React and TypeScript based apps.&lt;br&gt;
The main difference is that the config files are now written in Lua.&lt;br&gt;
I switched from vim-plug to Packer.&lt;br&gt;
I also made a tutorial video on how to set up Neovim from scratch on a new M2 MacBook Air.&lt;br&gt;
If you have your own dotfiles already, you can cherry-pick my config.&lt;br&gt;
I hope you enjoy it!&lt;/p&gt;
&lt;h2&gt;
  
  
  Ingredients
&lt;/h2&gt;

&lt;p&gt;Here is a quick summary of my set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;Neovim&lt;/a&gt; &amp;gt;= 0.7&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer"&gt;wbthomason/packer.nvim&lt;/a&gt; - A plugin manager for Neovim&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/svrana/neosolarized.nvim" rel="noopener noreferrer"&gt;svrana/neosolarized.nvim&lt;/a&gt; - A truecolor, solarized dark colorscheme&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer"&gt;nvim-lualine/lualine.nvim&lt;/a&gt; - A blazing fast and easy to configure Neovim statusline written in Lua&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/onsails/lspkind-nvim" rel="noopener noreferrer"&gt;onsails/lspkind-nvim&lt;/a&gt; - VSCode-like pictograms&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer"&gt;L3MON4D3/LuaSnip&lt;/a&gt; - Snippet Engine for Neovim written in Lua&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/cmp-nvim-lsp" rel="noopener noreferrer"&gt;hrsh7th/cmp-nvim-lsp&lt;/a&gt; - nvim-cmp source for neovim's built-in LSP&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/cmp-buffer" rel="noopener noreferrer"&gt;hrsh7th/cmp-buffer&lt;/a&gt; - nvim-cmp source for buffer words&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer"&gt;hrsh7th/nvim-cmp&lt;/a&gt; - A completion engine plugin for neovim written in Lua&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer"&gt;neovim/nvim-lspconfig&lt;/a&gt; - A collection of configurations for Neovim's built-in LSP&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer"&gt;jose-elias-alvarez/null-ls.nvim&lt;/a&gt; - Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/MunifTanjim/prettier.nvim" rel="noopener noreferrer"&gt;MunifTanjim/prettier.nvim&lt;/a&gt; - Prettier plugin for Neovim's built-in LSP client&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/williamboman/mason.nvim" rel="noopener noreferrer"&gt;williamboman/mason.nvim&lt;/a&gt; - Portable package manager for Neovim that runs everywhere Neovim runs. Easily install and manage LSP servers, DAP servers, linters, and formatters&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/williamboman/mason-lspconfig.nvim" rel="noopener noreferrer"&gt;williamboman/mason-lspconfig.nvim&lt;/a&gt; - Extension to mason.nvim that makes it easier to use lspconfig with mason.nvim&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/glepnir/lspsaga.nvim" rel="noopener noreferrer"&gt;glepnir/lspsaga.nvim&lt;/a&gt; - A light-weight lsp plugin based on neovim's built-in lsp with a highly performant UI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer"&gt;nvim-treesitter/nvim-treesitter&lt;/a&gt; - &lt;a href="https://github.com/tree-sitter/tree-sitter" rel="noopener noreferrer"&gt;Treesitter&lt;/a&gt; configurations and abstraction layer for Neovim&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kyazdani42/nvim-web-devicons" rel="noopener noreferrer"&gt;kyazdani42/nvim-web-devicons&lt;/a&gt; - Lua &lt;code&gt;fork&lt;/code&gt; of vim-web-devicons for neovim&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer"&gt;nvim-telescope/telescope.nvim&lt;/a&gt; - A highly extendable fuzzy finder over lists&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nvim-telescope/telescope-file-browser.nvim" rel="noopener noreferrer"&gt;nvim-telescope/telescope-file-browser.nvim&lt;/a&gt; - File Browser extension for telescope.nvim&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/windwp/nvim-autopairs" rel="noopener noreferrer"&gt;windwp/nvim-autopairs&lt;/a&gt; - Autopairs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/windwp/nvim-ts-autotag" rel="noopener noreferrer"&gt;windwp/nvim-ts-autotag&lt;/a&gt; - Use treesitter to auto close and auto rename html tag&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/norcalli/nvim-colorizer.lua" rel="noopener noreferrer"&gt;norcalli/nvim-colorizer.lua&lt;/a&gt; - A high-performance color highlighter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/akinsho/nvim-bufferline.lua" rel="noopener noreferrer"&gt;akinsho/nvim-bufferline.lua&lt;/a&gt; - A snazzy bufferline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lewis6991/gitsigns.nvim" rel="noopener noreferrer"&gt;lewis6991/gitsigns.nvim&lt;/a&gt; - Git integration for buffers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/dinhhuy258/git.nvim" rel="noopener noreferrer"&gt;dinhhuy258/git.nvim&lt;/a&gt; - A simple clone of the plugin vim-fugitive&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/folke/zen-mode.nvim" rel="noopener noreferrer"&gt;folke/zen-mode.nvim&lt;/a&gt; - Distraction-free mode&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/iamcco/markdown-preview.nvim" rel="noopener noreferrer"&gt;iamcco/markdown-preview.nvim&lt;/a&gt; - Markdown live preview&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here is my dotfiles repository:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/craftzdog" rel="noopener noreferrer"&gt;
        craftzdog
      &lt;/a&gt; / &lt;a href="https://github.com/craftzdog/dotfiles-public" rel="noopener noreferrer"&gt;
        dotfiles-public
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      My personal dotfiles
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/craftzdog/dotfiles-public./images/screenshot-1.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fcraftzdog%2Fdotfiles-public.%2Fimages%2Fscreenshot-1.png" alt="fish screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/craftzdog/dotfiles-public./images/screenshot-neovim.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fcraftzdog%2Fdotfiles-public.%2Fimages%2Fscreenshot-neovim.png" alt="nvim screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Takuya's dotfiles&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: Don’t blindly use my settings unless you know what that entails. Use at your own risk!&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Looking for a Markdown note-taking app?&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Check out my app called &lt;a href="https://www.inkdrop.app/" rel="nofollow noopener noreferrer"&gt;Inkdrop&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.inkdrop.app/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fcraftzdog%2Fdotfiles-public.%2Fimages%2Fscreenshot-inkdrop.png" alt="Inkdrop"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;vim (Neovim) config&lt;/li&gt;
&lt;li&gt;tmux config&lt;/li&gt;
&lt;li&gt;git config&lt;/li&gt;
&lt;li&gt;fish config&lt;/li&gt;
&lt;li&gt;PowerShell config&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Neovim setup&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Requirements&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Neovim &amp;gt;= &lt;strong&gt;0.9.0&lt;/strong&gt; (needs to be built with &lt;strong&gt;LuaJIT&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Git &amp;gt;= &lt;strong&gt;2.19.0&lt;/strong&gt; (for partial clones support)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.lazyvim.org/" rel="nofollow noopener noreferrer"&gt;LazyVim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;a &lt;a href="https://www.nerdfonts.com/" rel="nofollow noopener noreferrer"&gt;Nerd Font&lt;/a&gt;(v3.0 or greater) &lt;strong&gt;&lt;em&gt;(optional, but needed to display some icons)&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jesseduffield/lazygit" rel="noopener noreferrer"&gt;lazygit&lt;/a&gt; &lt;strong&gt;&lt;em&gt;(optional)&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;C&lt;/strong&gt; compiler for &lt;code&gt;nvim-treesitter&lt;/code&gt;. See &lt;a href="https://github.com/nvim-treesitter/nvim-treesitter#requirements" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;for &lt;a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer"&gt;telescope.nvim&lt;/a&gt; &lt;strong&gt;&lt;em&gt;(optional)&lt;/em&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;live grep&lt;/strong&gt;: &lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;ripgrep&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;find files&lt;/strong&gt;: &lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;fd&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;a terminal that support true color and &lt;em&gt;undercurl&lt;/em&gt;:
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kovidgoyal/kitty" rel="noopener noreferrer"&gt;kitty&lt;/a&gt; &lt;strong&gt;&lt;em&gt;(Linux &amp;amp; Macos)&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wez/wezterm" rel="noopener noreferrer"&gt;wezterm&lt;/a&gt; &lt;strong&gt;&lt;em&gt;(Linux, Macos &amp;amp; Windows)&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/alacritty/alacritty" rel="noopener noreferrer"&gt;alacritty&lt;/a&gt; &lt;strong&gt;&lt;em&gt;(Linux, Macos &amp;amp; Windows)&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://iterm2.com/" rel="nofollow noopener noreferrer"&gt;iterm2&lt;/a&gt; &lt;strong&gt;&lt;em&gt;(Macos)&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/craftzdog/solarized-osaka.nvim" rel="noopener noreferrer"&gt;Solarized Osaka&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Shell setup (macOS &amp;amp; Linux)&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fishshell.com/" rel="nofollow noopener noreferrer"&gt;Fish shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jorgebucaran/fisher" rel="noopener noreferrer"&gt;Fisher&lt;/a&gt; - Plugin manager&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/IlanCosman/tide" rel="noopener noreferrer"&gt;Tide&lt;/a&gt; - Shell theme&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ryanoasis/nerd-fonts" rel="noopener noreferrer"&gt;Nerd fonts&lt;/a&gt; - Patched fonts for development-use. I use…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/craftzdog/dotfiles-public" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;A tutorial video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ajmK0ZNcM4Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites — iTerm2 and Patched Nerd Font
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://iterm2.com/" rel="noopener noreferrer"&gt;iTerm2&lt;/a&gt; is a fast terminal emulator for macOS.&lt;br&gt;
Install one of &lt;a href="https://github.com/ryanoasis/nerd-fonts" rel="noopener noreferrer"&gt;Nerd Fonts&lt;/a&gt; for displaying fancy glyphs on your terminal.&lt;br&gt;
My current choice is &lt;a href="https://github.com/ryanoasis/nerd-fonts/tree/master/patched-fonts/Hack" rel="noopener noreferrer"&gt;Hack&lt;/a&gt;.&lt;br&gt;
And use it on your terminal app. For example, on iTerm2:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fir1944oqhwu8kz9zakqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fir1944oqhwu8kz9zakqt.png" alt="iTerm Preferences"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Neovim via Homebrew
&lt;/h2&gt;

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

brew &lt;span class="nb"&gt;install &lt;/span&gt;neovim


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Directory structure
&lt;/h2&gt;

&lt;p&gt;Neovim conforms &lt;a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html" rel="noopener noreferrer"&gt;XDG Base Directory&lt;/a&gt; structure. Here is my config file structure:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

📂 ~/.config/nvim
├── 📁 after
│  └── 📁 plugin
├── 📂 lua
│  └── 🌑 base.lua
├── 📁 plugin
└── 🇻 init.lua


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

&lt;/div&gt;

&lt;p&gt;Neovim loads &lt;code&gt;$HOME/.config/nvim/init.vim&lt;/code&gt; or &lt;code&gt;init.lua&lt;/code&gt; first instead of &lt;code&gt;$HOME/.vimrc&lt;/code&gt;.&lt;br&gt;
Check out the quickstart guide for more detail:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nanotee/nvim-lua-guide" rel="noopener noreferrer"&gt;https://github.com/nanotee/nvim-lua-guide&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install plugin manager: Packer
&lt;/h2&gt;

&lt;p&gt;Install &lt;a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer"&gt;Packer&lt;/a&gt; by running the below command:&lt;/p&gt;

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

git clone --depth 1 https://github.com/wbthomason/packer.nvim \
 ~/.local/share/nvim/site/pack/packer/start/packer.nvim


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

&lt;/div&gt;

&lt;p&gt;Then, make &lt;code&gt;./.config/nvim/lua/plugins.lua&lt;/code&gt; like so:&lt;/p&gt;

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

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;packer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"packer"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Packer is not installed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="s"&gt;[[packadd packer.nvim]]&lt;/span&gt;

&lt;span class="n"&gt;packer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;use&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="s1"&gt;'wbthomason/packer.nvim'&lt;/span&gt;
  &lt;span class="c1"&gt;-- Your plugins go here&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then, require it from &lt;code&gt;init.lua&lt;/code&gt; like so:&lt;/p&gt;

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

require('plugins')


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Color scheme: Neosolarized
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9wnie5eat6gc7ig2w8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9wnie5eat6gc7ig2w8i.png" alt="Neosolarized"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://github.com/svrana/neosolarized.nvim" rel="noopener noreferrer"&gt;svrana/neosolarized.nvim&lt;/a&gt; with some customizations.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"neosolarized"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;comment_italics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'colorbuddy.init'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;styles&lt;/span&gt;

&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'black'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'#000000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CursorLine'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CursorLineNr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Visual'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;cError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fg&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;cInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Information&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fg&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;cWarn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fg&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;cHint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fg&lt;/span&gt;

&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticVirtualTextError"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticVirtualTextInfo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticVirtualTextWarn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cWarn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cWarn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticVirtualTextHint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cHint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cHint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticUnderlineError"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;undercurl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticUnderlineWarn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;undercurl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cWarn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticUnderlineInfo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;undercurl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DiagnosticUnderlineHint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;undercurl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cHint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Status line: Lualine
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flaex613uo60w14jx2nqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flaex613uo60w14jx2nqi.png" alt="lualine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nvim-lualine/lualine.nvim" rel="noopener noreferrer"&gt;nvim-lualine/lualine.nvim&lt;/a&gt; provides a flexible way to configure statusline.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lualine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"lualine"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;lualine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;icons_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'solarized_dark'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;section_separators&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;component_separators&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;disabled_filetypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;sections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lualine_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'mode'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'branch'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'filename'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;file_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- displays file status (readonly status, modified status)&lt;/span&gt;
      &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;-- 0 = just filename, 1 = relative path, 2 = absolute path&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'diagnostics'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"nvim_diagnostic"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;symbols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;hint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="s1"&gt;'encoding'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'filetype'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'progress'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'location'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;inactive_sections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lualine_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s1"&gt;'filename'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;file_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- displays file status (readonly status, modified status)&lt;/span&gt;
      &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;-- 0 = just filename, 1 = relative path, 2 = absolute path&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'location'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="n"&gt;lualine_z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;tabline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="n"&gt;extensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'fugitive'&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;h2&gt;
  
  
  Lspconfig
&lt;/h2&gt;

&lt;p&gt;Neovim has a built-in LSP support.&lt;br&gt;
You can easily configure it by using &lt;a href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer"&gt;neovim/nvim-lspconfig&lt;/a&gt;.&lt;br&gt;
For example, to enable typescript language server on Neovim:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nvim_lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"lspconfig"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'vim.lsp.protocol'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;on_attach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;-- format on save&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server_capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;documentFormattingProvider&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_autocmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"BufWritePre"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_augroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Format"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;clear&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lsp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formatting_seq_sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;-- TypeScript&lt;/span&gt;
&lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tsserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;on_attach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on_attach&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;filetypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"typescriptreact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"typescript.tsx"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"typescript-language-server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--stdio"&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;Don't forget to install typescript language server itself:&lt;/p&gt;

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

npm i &lt;span class="nt"&gt;-g&lt;/span&gt; typescript-language-server


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Auto-completion: Lspkind and cmp
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqhzfpustqvwjmub7ps6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqhzfpustqvwjmub7ps6.png" alt="lspkind and cmp"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get LSP-aware auto-completion feature with fancy pictograms, I use the following plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/onsails/lspkind-nvim" rel="noopener noreferrer"&gt;onsails/lspkind-nvim&lt;/a&gt; - VSCode-like pictograms&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer"&gt;L3MON4D3/LuaSnip&lt;/a&gt; - Snippet engine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/cmp-nvim-lsp" rel="noopener noreferrer"&gt;hrsh7th/cmp-nvim-lsp&lt;/a&gt; - nvim-cmp source for neovim's built-in LSP&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/cmp-buffer" rel="noopener noreferrer"&gt;hrsh7th/cmp-buffer&lt;/a&gt; - nvim-cmp source for buffer words&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer"&gt;hrsh7th/nvim-cmp&lt;/a&gt; - A completion engine plugin for neovim&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Configure it like so:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"cmp"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;lspkind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'lspkind'&lt;/span&gt;

&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;expand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'luasnip'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;lsp_expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-d&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scroll_docs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-f&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scroll_docs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-Space&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;C-e&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="n"&gt;behavior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfirmBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nb"&gt;select&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nvim_lsp'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'buffer'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="n"&gt;formatting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lspkind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmp_format&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;with_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxwidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&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="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="s"&gt;[[
  set completeopt=menuone,noinsert,noselect
  highlight! default link CmpItemKind CmpItemMenuDefault
]]&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Syntax highlightings: Treesitter
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82fwb1gjj0vn0itywfl3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82fwb1gjj0vn0itywfl3.png" alt="treesitter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Treesitter is a popular language parser for syntax highlightings.&lt;br&gt;
First, install it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

brew &lt;span class="nb"&gt;install &lt;/span&gt;tree-sitter


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

&lt;/div&gt;

&lt;p&gt;Install &lt;a href="https://github.com/nvim-treesitter/nvim-treesitter" rel="noopener noreferrer"&gt;nvim-treesitter/nvim-treesitter&lt;/a&gt; with Packer and configure it like so:&lt;/p&gt;

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

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"nvim-treesitter.configs"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;highlight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;disable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;indent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;disable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;ensure_installed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"tsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"toml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"fish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"php"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"yaml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"swift"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"lua"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;autotag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;parser_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"nvim-treesitter.parsers"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_parser_configs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;parser_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tsx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filetype_to_parsername&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"javascript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"typescript.tsx"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Autotag and Autopair
&lt;/h2&gt;

&lt;p&gt;For React apps, you often want to close tags quickly.&lt;br&gt;
&lt;a href="https://github.com/windwp/nvim-ts-autotag" rel="noopener noreferrer"&gt;windwp/nvim-ts-autotag&lt;/a&gt; is exactly what you want.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;autotag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"nvim-ts-autotag"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;autotag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&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://github.com/windwp/nvim-autopairs" rel="noopener noreferrer"&gt;windwp/nvim-autopairs&lt;/a&gt; is for closing brackets.&lt;/p&gt;

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

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;autopairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"nvim-autopairs"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;autopairs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;disable_filetype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"TelescopePrompt"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"vim"&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;h2&gt;
  
  
  Fuzz finder: Telescope
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm94jub71c9q9juwywhag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm94jub71c9q9juwywhag.png" alt="telescope"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer"&gt;telescope.nvim&lt;/a&gt; provides an interactive fuzzy finder over lists, built on top of the latest Neovim features.&lt;br&gt;
I also use &lt;a href="https://github.com/nvim-telescope/telescope-file-browser.nvim" rel="noopener noreferrer"&gt;telescope-file-browser.nvim&lt;/a&gt; as a filer.&lt;/p&gt;

&lt;p&gt;It’s so useful because you can search files while viewing the content of the files without actually opening them. It supports various sources like &lt;a href="https://github.com/nvim-telescope/telescope.nvim#vim-pickers" rel="noopener noreferrer"&gt;Vim&lt;/a&gt;, &lt;a href="https://github.com/nvim-telescope/telescope.nvim#file-pickers" rel="noopener noreferrer"&gt;files&lt;/a&gt;, &lt;a href="https://github.com/nvim-telescope/telescope.nvim#git-pickers" rel="noopener noreferrer"&gt;Git&lt;/a&gt;, &lt;a href="https://github.com/nvim-telescope/telescope.nvim#lsp-pickers" rel="noopener noreferrer"&gt;LSP&lt;/a&gt;, and &lt;a href="https://github.com/nvim-telescope/telescope.nvim#treesitter-picker" rel="noopener noreferrer"&gt;Treesitter&lt;/a&gt;. Check out &lt;a href="https://github.com/nvim-telescope/telescope.nvim/wiki/Showcase" rel="noopener noreferrer"&gt;the showcase&lt;/a&gt; of Telescope.&lt;/p&gt;

&lt;p&gt;Install &lt;a href="https://github.com/kyazdani42/nvim-web-devicons" rel="noopener noreferrer"&gt;kyazdani42/nvim-web-devicons&lt;/a&gt; to get file icons on Telescope, statusline, and other supported plugins.&lt;/p&gt;

&lt;p&gt;The configuration would look like so:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;telescope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"telescope"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'telescope.actions'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;builtin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"telescope.builtin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;telescope_buffer_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%:p:h'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;fb_actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"telescope"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;

&lt;span class="n"&gt;telescope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- keymaps&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;';f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_files&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="n"&gt;no_ignore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;hidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;';r'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;live_grep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\\\&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buffers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;';t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;help_tags&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;';;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;';e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;builtin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diagnostics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6cremxfu2jwspf794e2g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6cremxfu2jwspf794e2g.png" alt="file browser"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the telescope browser extension:&lt;/p&gt;

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

&lt;span class="n"&gt;telescope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&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="n"&gt;extensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;file_browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dropdown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;-- disables netrw and use telescope-file-browser in its place&lt;/span&gt;
      &lt;span class="n"&gt;hijack_netrw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;mappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;-- your custom insert mode mappings&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"i"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;C-w&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'normal vbd'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&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="s2"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;-- your custom normal mode mappings&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fb_actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fb_actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;goto_parent_dir&lt;/span&gt;&lt;span class="p"&gt;,&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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'startinsert'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;telescope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"file_browser"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"n"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;telescope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_browser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"%:p:h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;telescope_buffer_dir&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;respect_gitignore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;hidden&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;grouped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;previewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;initial_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"normal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;layout_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Tabs: Bufferline
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlii0bob3ijnqpyxsf7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftlii0bob3ijnqpyxsf7w.png" alt="bufferline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://github.com/akinsho/nvim-bufferline.lua" rel="noopener noreferrer"&gt;akinsho/nvim-bufferline.lua&lt;/a&gt; to get better looking of tabs.&lt;br&gt;
Make some customizations to make it look better with Solarized theme:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufferline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"bufferline"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;bufferline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tabs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;separator_style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'slant'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;always_show_bufferline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;show_buffer_close_icons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;show_close_icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;color_icons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;highlights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;separator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;guifg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'#073642'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;guibg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'#002b36'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;separator_selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;guifg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'#073642'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;background&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;guifg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'#657b83'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;guibg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'#002b36'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;buffer_selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;guifg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'#fdf6e3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;gui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"bold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;fill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;guibg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'#073642'&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="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Tab&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;BufferLineCycleNext&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;S-Tab&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;BufferLineCyclePrev&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  LSP Uls: Lspsaga
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmfw7q5oejyhntnze56o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmfw7q5oejyhntnze56o.png" alt="lsp_finder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ro4cf4ci5zmzt6zz7im.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ro4cf4ci5zmzt6zz7im.png" alt="rename action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y29lppj2l23uakahnp6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3y29lppj2l23uakahnp6.png" alt="definition preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/glepnir/lspsaga.nvim" rel="noopener noreferrer"&gt;glepnir/lspsaga.nvim&lt;/a&gt; is one of my favorite LSP plugins.&lt;br&gt;
It provides beautiful UIs for various LSP-related features like hover doc, definition preview, and rename actions.&lt;br&gt;
My configuration is simple:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saga&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"lspsaga"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;saga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init_lsp_saga&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;server_filetype_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;typescript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'typescript'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;noremap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;silent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;C-j&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;Lspsaga diagnostic_jump_next&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'K'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;Lspsaga hover_doc&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;Lspsaga lsp_finder&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;C-k&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;Lspsaga signature_help&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;Lspsaga preview_definition&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'gr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;Cmd&amp;gt;Lspsaga rename&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Code formatter: Prettier and null-ls
&lt;/h2&gt;

&lt;p&gt;I heavily rely on Prettier to format TypeScript/JavaScript/CSS files.&lt;br&gt;
Use &lt;a href="https://github.com/jose-elias-alvarez/null-ls.nvim" rel="noopener noreferrer"&gt;jose-elias-alvarez/null-ls.nvim&lt;/a&gt; and &lt;a href="https://github.com/MunifTanjim/prettier.nvim" rel="noopener noreferrer"&gt;MunifTanjim/prettier.nvim&lt;/a&gt; to accomplish that.&lt;/p&gt;

&lt;p&gt;First, you need &lt;a href="https://github.com/fsouza/prettierd/releases" rel="noopener noreferrer"&gt;prettierd&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

brew &lt;span class="nb"&gt;install &lt;/span&gt;prettierd


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

&lt;/div&gt;

&lt;p&gt;Then, configure null-ls as following:&lt;/p&gt;

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

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null_ls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"null-ls"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;null_ls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;null_ls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builtins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diagnostics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eslint_d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="n"&gt;diagnostics_format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'[eslint] #{m}\n(#{c})'&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="n"&gt;null_ls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builtins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diagnostics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fish&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;Prettier:&lt;/p&gt;

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

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prettier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"prettier"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;prettier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;bin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'prettierd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;filetypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"javascript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"javascriptreact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"typescriptreact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"scss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"less"&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;h2&gt;
  
  
  Git markers: gitsigns
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29x6r0m1uyn4h9wyp1pz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29x6r0m1uyn4h9wyp1pz.png" alt="gitsigns"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lewis6991/gitsigns.nvim" rel="noopener noreferrer"&gt;lewis6991/gitsigns.nvim&lt;/a&gt; provides git decorations for current buffers.&lt;br&gt;
It helps you know which lines are currently changed.&lt;br&gt;
It works out of the box.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gitsigns'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  git
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw67gq6ml6aduhuszig9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw67gq6ml6aduhuszig9a.png" alt="git"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I often view the code on GitHub.&lt;br&gt;
&lt;a href="https://github.com/dinhhuy258/git.nvim" rel="noopener noreferrer"&gt;dinhhuy258/git.nvim&lt;/a&gt; helps open GitHub right from Neovim, and provides &lt;code&gt;git blame&lt;/code&gt; view in split view, which are super handy.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"git"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;keymaps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;-- Open blame window&lt;/span&gt;
    &lt;span class="n"&gt;blame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;Leader&amp;gt;gb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Open file/folder in git repository&lt;/span&gt;
    &lt;span class="n"&gt;browse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;Leader&amp;gt;go"&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;h2&gt;
  
  
  LSP tool: mason
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7cmmoju1oh7abawwwo0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7cmmoju1oh7abawwwo0.png" alt="mason"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you need additional LSP support for specific libraries, you may need &lt;a href="https://github.com/williamboman/mason.nvim" rel="noopener noreferrer"&gt;williamboman/mason.nvim&lt;/a&gt; and &lt;a href="https://github.com/williamboman/mason-lspconfig.nvim" rel="noopener noreferrer"&gt;williamboman/mason-lspconfig.nvim&lt;/a&gt;.&lt;br&gt;
I use them for getting Tailwind CSS language server to work on Neovim.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mason"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;status2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lspconfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pcall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mason-lspconfig"&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;status2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;mason&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;

&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;lspconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ensure_installed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"sumneko_lua"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"tailwindcss"&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;Add lspconfig:&lt;/p&gt;

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

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;nvim_lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"lspconfig"&lt;/span&gt;
&lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tailwindcss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


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

&lt;/div&gt;




&lt;p&gt;That’s pretty much it!&lt;br&gt;
I hope it’s helpful for improving your Neovim environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me online
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Check out my app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop — A Markdown note-taking app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;https://twitter.com/inkdrop_app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog &lt;a href="https://blog.inkdrop.app/" rel="noopener noreferrer"&gt;https://blog.inkdrop.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;YouTube &lt;a href="https://www.youtube.com/devaslife" rel="noopener noreferrer"&gt;https://www.youtube.com/devaslife&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;https://instagram.com/craftzdog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>neovim</category>
      <category>lsp</category>
      <category>treesitter</category>
      <category>typescript</category>
    </item>
    <item>
      <title>A performant way to use PouchDB@7 on React Native in 2022</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Sun, 01 May 2022 01:13:49 +0000</pubDate>
      <link>https://forem.com/craftzdog/a-performant-way-to-use-pouchdb7-on-react-native-in-2022-24ej</link>
      <guid>https://forem.com/craftzdog/a-performant-way-to-use-pouchdb7-on-react-native-in-2022-24ej</guid>
      <description>&lt;p&gt;Hi, it's &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Takuya&lt;/a&gt; here.&lt;br&gt;
I've been developing a React Native app which syncs user data with CouchDB. The app is called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've published several posts on how to use PouchDB on React Native in the past:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/craftzdog/hacking-pouchdb-to-use-on-react-native-1gjh"&gt;Hacking PouchDB to Use It on React Native&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/craftzdog/created-pouchdb-7-for-react-native-23g6"&gt;Created PouchDB@7 for React Native&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article is an update for using PouchDB on RN.&lt;/p&gt;

&lt;p&gt;Fortunately, &lt;a href="https://github.com/pouchdb/pouchdb" rel="noopener noreferrer"&gt;PouchDB&lt;/a&gt; is still maintained, and recently they've shipped v7.3.0 🎉. Much appreciated the community effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;2~5x speed improvement!&lt;/li&gt;
&lt;li&gt;Less library tweaks &amp;amp; simpler setup&lt;/li&gt;
&lt;li&gt;Attachments are no longer supported&lt;/li&gt;
&lt;li&gt;How to use&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use a JSI-based SQLite driver
&lt;/h2&gt;

&lt;p&gt;Oscar Franco created a great SQLite driver &lt;a href="https://github.com/ospfranco/react-native-quick-sqlite" rel="noopener noreferrer"&gt;react-native-quick-sqlite&lt;/a&gt;, which provides a low-level API to execute SQL queries, fast bindings via &lt;a href="https://formidable.com/blog/2019/jsi-jsc-part-2/" rel="noopener noreferrer"&gt;JSI&lt;/a&gt;. JSI has a smaller overhead than the traditional RN bridge's.&lt;br&gt;
So, this is much faster than &lt;a href="https://github.com/craftzdog/react-native-sqlite-2" rel="noopener noreferrer"&gt;react-native-sqlite-2&lt;/a&gt;, which I made to get PouchDB to work on RN before.&lt;/p&gt;

&lt;p&gt;I wanted to use it in my project, so I made &lt;a href="https://github.com/craftzdog/react-native-quick-websql/" rel="noopener noreferrer"&gt;react-native-quick-websql&lt;/a&gt;, which is a thin wrapper to make quick-sqlite compatible with WebSQL. It works flawlessly!&lt;/p&gt;

&lt;p&gt;I'll discuss how it improves the performance later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tackling the NULL character issue (again)
&lt;/h2&gt;

&lt;p&gt;There is still an issue in React Native - It can't store string data with &lt;code&gt;\u0000&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/facebook/react-native/issues/12731" rel="noopener noreferrer"&gt;JavaScript strings with NULL character are not handled properly when passed to Native Modules · Issue #12731 · facebook/react-native&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To avoid the issue, the NULL characters are escaped/unescaped in &lt;a href="https://github.com/craftzdog/react-native-sqlite-2/blob/master/src/SQLiteDatabase.ts#L50" rel="noopener noreferrer"&gt;react-native-sqlite-2&lt;/a&gt; like so:&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;escapeBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="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;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;0002/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;u0002&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;u0002&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;0001/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;u0001&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;u0002&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="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;0000/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;u0001&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;u0001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&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;But it's not ideal because it would affect the performance.&lt;/p&gt;

&lt;p&gt;PouchDB internally uses &lt;code&gt;\u0000&lt;/code&gt; for indexing documents with MapReduce in &lt;a href="https://github.com/pouchdb/pouchdb/blob/fafcd701a26171abc42b9c64ff4abc3c418c0d81/packages/node_modules/pouchdb-collate/src/index.js#L116-L119" rel="noopener noreferrer"&gt;pouchdb-collate&lt;/a&gt;:&lt;/p&gt;

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

&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;convert&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;given&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nx"&gt;that&lt;/span&gt; &lt;span class="nx"&gt;would&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;appropriate&lt;/span&gt;
&lt;span class="c1"&gt;// for lexical sorting, e.g. within a database, where the&lt;/span&gt;
&lt;span class="c1"&gt;// sorting is the same given by the collate() function.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toIndexableString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;zero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;u0000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;collationIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;SEP&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;indexify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;I didn't see why it should be &lt;code&gt;\u0000&lt;/code&gt;, so I've changed the zero marker to &lt;code&gt;\u0003&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/craftzdog/pouchdb-react-native/commit/228f68220fe31236f6630b71c030eef29ae6e7a8" rel="noopener noreferrer"&gt;https://github.com/craftzdog/pouchdb-react-native/commit/228f68220fe31236f6630b71c030eef29ae6e7a8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it turns out that it works!&lt;br&gt;
Now the overhead of escaping NULL characters has been removed.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's 2~5x faster 💨
&lt;/h2&gt;

&lt;p&gt;I've tested it on my Google Pixel 5 and it turns out it's blazing fast. Here is a quick benchmark:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;sqlite-2&lt;/th&gt;
&lt;th&gt;quick-sqlite + quick-websql&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Non-cached query for 38 docs&lt;/td&gt;
&lt;td&gt;4,510 ms&lt;/td&gt;
&lt;td&gt;984 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cached query for 38 docs&lt;/td&gt;
&lt;td&gt;275 ms&lt;/td&gt;
&lt;td&gt;75 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cached query for 1422 docs&lt;/td&gt;
&lt;td&gt;636 ms&lt;/td&gt;
&lt;td&gt;206 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PouchDB performs MapReduce to index the documents based on &lt;a href="https://pouchdb.com/guides/queries.html" rel="noopener noreferrer"&gt;design documents&lt;/a&gt;.&lt;br&gt;
That's why the first query takes a long time.&lt;br&gt;
But thanks to the small overheads, it's now about 4~5x faster.&lt;/p&gt;

&lt;p&gt;I can't wait to release the new version of my app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attachments are no longer supported
&lt;/h2&gt;

&lt;p&gt;In the past work, the main focus was to get PouchDB able to store attachments. But &lt;a href="https://blog.inkdrop.app/how-i-improved-my-react-native-app-50x-faster-13d566061e84" rel="noopener noreferrer"&gt;I no longer store them in PouchDB&lt;/a&gt;, so that requirement has been dropped.&lt;br&gt;
But that allows you to simply use the official PouchDB packages except for &lt;code&gt;pouchdb-collate&lt;/code&gt; as I mentioned above.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;Install dev package:&lt;/p&gt;

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

yarn add -D babel-plugin-module-resolver


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

&lt;/div&gt;

&lt;p&gt;Install polyfill packages:&lt;/p&gt;

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

yarn add events react-native-get-random-values react-native-quick-base64


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

&lt;/div&gt;

&lt;p&gt;Install pouchdb packages:&lt;/p&gt;

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

yarn add pouchdb-core pouchdb-replication pouchdb-mapreduce pouchdb-adapter-http


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

&lt;/div&gt;

&lt;p&gt;Install the patched package:&lt;/p&gt;

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

yarn add @craftzdog/pouchdb-collate-react-native


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

&lt;/div&gt;

&lt;p&gt;Install storage adapter packages:&lt;/p&gt;

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

yarn add pouchdb-adapter-react-native-sqlite react-native-quick-sqlite react-native-quick-websql


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ospfranco/react-native-quick-sqlite" rel="noopener noreferrer"&gt;react-native-quick-sqlite&lt;/a&gt; - A fast bindings via JSI for SQLite&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/craftzdog/react-native-quick-websql/" rel="noopener noreferrer"&gt;react-native-quick-websql&lt;/a&gt; - WebSQL wrapper for quick-sqlite&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/craftzdog/pouchdb-adapter-react-native-sqlite" rel="noopener noreferrer"&gt;pouchdb-adapter-react-native-sqlite&lt;/a&gt; - PouchDB adapter for SQLite with those two modules&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Install CocoaPods:&lt;/li&gt;
&lt;/ol&gt;

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

npx pod-install


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Configure
&lt;/h3&gt;

&lt;p&gt;Make a &lt;code&gt;shim.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shim&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;react-native-quick-base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;shim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Avoid using node dependent modules&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;then, require it at the beginning of your &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Edit your &lt;code&gt;babel.config.js&lt;/code&gt; like so:&lt;/p&gt;

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

&lt;span class="nx"&gt;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;presets&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;module:metro-react-native-babel-preset&lt;/span&gt;&lt;span class="dl"&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="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module-resolver&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;alias&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;pouchdb-collate&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;@craftzdog/pouchdb-collate-react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="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;h3&gt;
  
  
  Initialize
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;pouchdb.ts&lt;/code&gt; like so:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-get-random-values&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PouchDB&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;pouchdb-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;HttpPouch&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;pouchdb-adapter-http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;replication&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;pouchdb-replication&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mapreduce&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;pouchdb-mapreduce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SQLiteAdapterFactory&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;pouchdb-adapter-react-native-sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;WebSQLite&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;react-native-quick-websql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SQLiteAdapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SQLiteAdapterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WebSQLite&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;PouchDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpPouch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;replication&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapreduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SQLiteAdapter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then, import and use it as usual:&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PouchDB&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;./pouchdb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PouchDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mydb.db&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;adapter&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-native-sqlite&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;That's it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations &amp;amp; Debugging
&lt;/h3&gt;

&lt;p&gt;As the libraries use JSI for synchronous native methods access, remote debugging (e.g. with Chrome) is no longer possible.&lt;br&gt;
Instead, you should use &lt;a href="https://github.com/facebook/flipper" rel="noopener noreferrer"&gt;Flipper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hope you find it useful and helpful.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Follow me on &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop — Markdown note-taking app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/dNgJo6" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fproxy%2F1%2A-J5tp1_lulY9QknoLEv6zA.png" alt="Subscribe Newsletter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/c/devaslife" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fproxy%2F1%2Ar-daItizHpAU-fQZk3ISag.png" alt="My YouTube channel"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pouchdb</category>
      <category>reactnative</category>
      <category>couchdb</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Getting side-by-side preview in a terminal app Hyper</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Mon, 04 Oct 2021 11:16:06 +0000</pubDate>
      <link>https://forem.com/craftzdog/getting-side-by-side-preview-in-a-terminal-app-hyper-20ii</link>
      <guid>https://forem.com/craftzdog/getting-side-by-side-preview-in-a-terminal-app-hyper-20ii</guid>
      <description>&lt;p&gt;Hi, it's &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Takuya&lt;/a&gt;.&lt;br&gt;
I'm publishing &lt;a href="https://youtube.com/devaslife" rel="noopener noreferrer"&gt;YouTube content&lt;/a&gt;, sharing my dev workflows.&lt;/p&gt;
&lt;h2&gt;
  
  
  Want a side-by-side preview in a terminal app
&lt;/h2&gt;

&lt;p&gt;For web coding tutorials, it'd be great to show your coding and output side-by-side.&lt;br&gt;
Typically, you would do that by simply arranging two windows: An editor and a browser.&lt;br&gt;
But that's annoying because every time you make a tutorial, you have to align the windows side-by-side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hyper.is/" rel="noopener noreferrer"&gt;Hyper&lt;/a&gt;, a terminal app built with web standards, once had a built-in webview feature:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F529pd3dsbyab86o7chvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F529pd3dsbyab86o7chvf.png" alt="Hyper's built-in webview feature"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see above, it displays a preview on the right side of the terminal window.&lt;br&gt;
It looks pretty neat.&lt;br&gt;
So, you can make tutorials without having a separate window for preview.&lt;/p&gt;

&lt;p&gt;But it looks like the feature has been removed for security reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel/hyper/issues/2748" rel="noopener noreferrer"&gt;built-in hyperlinks alternative not working properly · Issue #2748 · vercel/hyper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was 3 years ago, a quite old issue.&lt;br&gt;
I understand the risk of loading web pages in Electron apps.&lt;br&gt;
However, &lt;code&gt;webview&lt;/code&gt; of the recent &lt;a href="https://www.electronjs.org/docs/api/webview-tag#nodeintegration" rel="noopener noreferrer"&gt;Electron prohibits NodeJS integration by default&lt;/a&gt;. So, I think it's safe to embed in Hyper, especially when you understand what you do.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hacking Hyper to get back the built-in webview feature
&lt;/h2&gt;

&lt;p&gt;So, I decided to get that feature back to Hyper, and successfully did it.&lt;br&gt;
Here is how it looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi92cqgf20zmc8rlx1d5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi92cqgf20zmc8rlx1d5c.png" alt="Demo"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Demo video:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1444952485177683969-303" src="https://platform.twitter.com/embed/Tweet.html?id=1444952485177683969"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1444952485177683969-303');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1444952485177683969&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  How to build
&lt;/h3&gt;

&lt;p&gt;I've made &lt;code&gt;built-in-webview&lt;/code&gt; branch for the hack:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/craftzdog/hyper/tree/built-in-webview" rel="noopener noreferrer"&gt;https://github.com/craftzdog/hyper/tree/built-in-webview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clone this repo and built it yourself as following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i
npm run dev
&lt;span class="c"&gt;# On another terminal session&lt;/span&gt;
npm run app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to use
&lt;/h3&gt;

&lt;p&gt;Split the window right by pressing &lt;code&gt;Cmd-D&lt;/code&gt;.&lt;br&gt;
Then, type &lt;code&gt;echo &amp;lt;URL-to-open&amp;gt;&lt;/code&gt; and hit &lt;code&gt;Return&lt;/code&gt;.&lt;br&gt;
Click the URL in the terminal.&lt;br&gt;
Then, the pane becomes a webview that loads the URL.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I hacked Hyper
&lt;/h2&gt;

&lt;p&gt;Check out &lt;a href="https://github.com/craftzdog/hyper/compare/canary...craftzdog:built-in-webview?expand=1" rel="noopener noreferrer"&gt;the diffs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, you have to allow &lt;code&gt;webview&lt;/code&gt; tags in &lt;code&gt;app/ui/window.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   const winOpts: BrowserWindowConstructorOptions = {
     minWidth: 370,
     minHeight: 190,
     backgroundColor: toElectronBackgroundColor(cfg.backgroundColor || '#000'),
     titleBarStyle: 'hiddenInset',
     title: 'Hyper.app',
     // we want to go frameless on Windows and Linux
     frame: process.platform === 'darwin',
     transparent: process.platform === 'darwin',
     icon,
     show: Boolean(process.env.HYPER_DEBUG || process.env.HYPERTERM_DEBUG || isDev),
     acceptFirstMouse: true,
     webPreferences: {
       nodeIntegration: true,
       navigateOnDragDrop: true,
       enableRemoteModule: true,
&lt;span class="gd"&gt;-      contextIsolation: false
&lt;/span&gt;&lt;span class="gi"&gt;+      contextIsolation: false,
+      webviewTag: true
&lt;/span&gt;     },
     ...options_
   };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hyper partially remains the old implementations. You can reuse them.&lt;br&gt;
The terminal component already has &lt;code&gt;url&lt;/code&gt; prop.&lt;br&gt;
You can display &lt;code&gt;webview&lt;/code&gt; when the component has &lt;code&gt;url&lt;/code&gt; prop.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;lib/components/term.tsx&lt;/code&gt;, change the terminal component class like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -430,18 +436,35 @@&lt;/span&gt; export default class Term extends React.PureComponent&amp;lt;TermProps&amp;gt; {
         style={{padding: this.props.padding}}
         onMouseUp={this.onMouseUp}
       &amp;gt;
&lt;span class="gd"&gt;-        {this.props.customChildrenBefore}
-        &amp;lt;div ref={this.onTermWrapperRef} className="term_fit term_wrapper" /&amp;gt;
-        {this.props.customChildren}
-        {this.props.search ? (
-          &amp;lt;SearchBox
-            search={this.search}
-            next={this.searchNext}
-            prev={this.searchPrevious}
-            close={this.closeSearchBox}
&lt;/span&gt;&lt;span class="gi"&gt;+        {this.props.url ? (
+          &amp;lt;webview
+            src={this.props.url}
+            style={{
+              background: '#fff',
+              position: 'absolute',
+              top: 0,
+              left: 0,
+              display: 'inline-flex',
+              width: '100%',
+              height: '100%'
+            }}
&lt;/span&gt;           /&amp;gt;
         ) : (
&lt;span class="gd"&gt;-          ''
&lt;/span&gt;&lt;span class="gi"&gt;+          &amp;lt;&amp;gt;
+            {this.props.customChildrenBefore}
+            &amp;lt;div ref={this.onTermWrapperRef} className="term_fit term_wrapper" /&amp;gt;
+            {this.props.customChildren}
+            {this.props.search ? (
+              &amp;lt;SearchBox
+                search={this.search}
+                next={this.searchNext}
+                prev={this.searchPrevious}
+                close={this.closeSearchBox}
+              /&amp;gt;
+            ) : (
+              ''
+            )}
+          &amp;lt;/&amp;gt;
&lt;/span&gt;         )}
&lt;span class="err"&gt;
&lt;/span&gt;         &amp;lt;style jsx global&amp;gt;{`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, change the URL click handler to dispatch an action instead of opening up the page in an external browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -160,7 +160,13 @@&lt;/span&gt; export default class Term extends React.PureComponent&amp;lt;TermProps&amp;gt; {
       this.term.loadAddon(
         new WebLinksAddon(
           (event: MouseEvent | undefined, uri: string) =&amp;gt; {
&lt;span class="gd"&gt;-            if (shallActivateWebLink(event)) void shell.openExternal(uri);
&lt;/span&gt;&lt;span class="gi"&gt;+            // if (shallActivateWebLink(event)) void shell.openExternal(uri);
+            // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+            store.dispatch({
+              type: 'SESSION_URL_SET',
+              uid: props.uid,
+              url: uri
+            });
&lt;/span&gt;           },
           {
             // prevent default electron link handling to allow selection, e.g. via double-click
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;lib/reducers/sessions.ts&lt;/code&gt;, add the reducer for &lt;code&gt;SESSION_URL_SET&lt;/code&gt; like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -11,7 +11,8 @@&lt;/span&gt; import {
   SESSION_SET_XTERM_TITLE,
   SESSION_SET_CWD,
   SESSION_SEARCH,
&lt;span class="gd"&gt;-  SESSION_SEARCH_CLOSE
&lt;/span&gt;&lt;span class="gi"&gt;+  SESSION_SEARCH_CLOSE,
+  SESSION_URL_SET
&lt;/span&gt; } from '../constants/sessions';
 import {sessionState, session, Mutable, ISessionReducer} from '../hyper';
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;@@ -129,6 +130,9 @@&lt;/span&gt; const reducer: ISessionReducer = (state = initialState, action) =&amp;gt; {
       }
       return state;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    case SESSION_URL_SET:
+      return state.setIn(['sessions', action.uid, 'url'], action.url);
+
&lt;/span&gt;     default:
       return state;
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works like a charm!&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow me online
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check out my app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop - A Markdown note-taking app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe Newsletter &lt;a href="http://eepurl.com/dNgJo6" rel="noopener noreferrer"&gt;http://eepurl.com/dNgJo6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;https://twitter.com/inkdrop_app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;YouTube channel &lt;a href="https://youtube.com/c/devaslife" rel="noopener noreferrer"&gt;https://youtube.com/c/devaslife&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog &lt;a href="https://blog.inkdrop.app/" rel="noopener noreferrer"&gt;https://blog.inkdrop.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discord community &lt;a href="https://discord.gg/QfsG5Kj" rel="noopener noreferrer"&gt;https://discord.gg/QfsG5Kj&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;https://instagram.com/craftzdog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hyper</category>
      <category>electron</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to use Neovim 0.5 on iSH on iPadOS</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Thu, 05 Aug 2021 02:19:01 +0000</pubDate>
      <link>https://forem.com/craftzdog/how-to-use-neovim-0-5-on-ish-on-ipados-2ie5</link>
      <guid>https://forem.com/craftzdog/how-to-use-neovim-0-5-on-ish-on-ipados-2ie5</guid>
      <description>&lt;p&gt;I have an iPad. I love Neovim. Can I use it on my iPad? -- Let's try it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use iSH as a terminal emulator
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://camo.githubusercontent.com/9f65eca28081cc51e41ca56630cb9272c8ac8cdfe2216c3291febf6a87842319/68747470733a2f2f6973682e6170702f6173736574732f6769746875622d726561646d652e706e67" class="article-body-image-wrapper"&gt;&lt;img src="https://camo.githubusercontent.com/9f65eca28081cc51e41ca56630cb9272c8ac8cdfe2216c3291febf6a87842319/68747470733a2f2f6973682e6170702f6173736574732f6769746875622d726561646d652e706e67" alt="iSH"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ish.app/" rel="noopener noreferrer"&gt;https://ish.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;iSH is a Linux shell built for iOS &amp;amp; iPadOS, using usermode x86 emulation and syscall translation. It's not a SSH client. You can actually have a terminal locally on your device.&lt;br&gt;
It runs Alpine Linux, so you can install packages with &lt;code&gt;apk&lt;/code&gt; command. Pretty neat!&lt;/p&gt;
&lt;h2&gt;
  
  
  Couldn't build Neovim from source
&lt;/h2&gt;

&lt;p&gt;Apparently, the official iSH package repository currently provides only Neovim 0.4.&lt;br&gt;
Looks like you need to build the latest Neovim from its source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/neovim/neovim/wiki/Building-Neovim" rel="noopener noreferrer"&gt;https://github.com/neovim/neovim/wiki/Building-Neovim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

apk update
apk add git build-base cmake automake autoconf libtool pkgconf coreutils curl unzip gettext-tiny-dev


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

&lt;/div&gt;
&lt;p&gt;Clone the reopsitory:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

git clone https://github.com/neovim/neovim.git
&lt;span class="nb"&gt;cd &lt;/span&gt;neovim


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

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

make &lt;span class="nt"&gt;-j4&lt;/span&gt; &lt;span class="nv"&gt;CMAKE_BUILD_TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Release


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

&lt;/div&gt;
&lt;p&gt;But got an error:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Install the project...
-- Install configuration: "Release"
-- Installing: /root/neovim/.deps/usr/lib/libluv_a.a
-- Installing: /root/neovim/.deps/usr/include/luv/luv.h
-- Installing: /root/neovim/.deps/usr/include/luv/util.h
-- Installing: /root/neovim/.deps/usr/include/luv/lhandle.h
-- Installing: /root/neovim/.deps/usr/include/luv/lreq.h
make[4]: Leaving directory '/root/neovim/.deps/build/src/luv-static-build'
[ 94%] Completed 'luv-static'
make[3]: Leaving directory '/root/neovim/.deps'
[ 94%] Built target luv-static
make[2]: Leaving directory '/root/neovim/.deps'
make[1]: *** [Makefile:104: all] Error 2
make[1]: Leaving directory '/root/neovim/.deps'
make: *** [Makefile:105: deps] Error 2


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

&lt;/div&gt;
&lt;p&gt;I don't understand what's going wrong from this error log.&lt;br&gt;
iSH is quite unstable on doing those things.&lt;/p&gt;

&lt;p&gt;I decided to go another way.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try Alpine's edge repository
&lt;/h2&gt;

&lt;p&gt;The iSH's default package repository is kind of outdated.&lt;br&gt;
So, I tried to use the Alpine's official edge repository.&lt;br&gt;
Because neovim-0.5.0 is available there:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkgs.alpinelinux.org/package/edge/community/x86/neovim" rel="noopener noreferrer"&gt;https://pkgs.alpinelinux.org/package/edge/community/x86/neovim&lt;/a&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;echo &lt;/span&gt;https://dl-cdn.alpinelinux.org/alpine/edge/main &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/apk/repositories
&lt;span class="nb"&gt;echo &lt;/span&gt;https://dl-cdn.alpinelinux.org/alpine/edge/community &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/apk/repositories
apk update
apk upgrade
apk add neovim


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

&lt;/div&gt;
&lt;p&gt;Boom. It worked!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspkak4298c0cbz58dccl.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspkak4298c0cbz58dccl.PNG" alt="IMG_0341"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  You can't use all the edge packages
&lt;/h2&gt;

&lt;p&gt;I found that git or nodejs won't work if I installed it from the edge repos due to "Bad system call".&lt;br&gt;
So, you basically should be on the default package repos.&lt;/p&gt;

&lt;p&gt;I hope iSH gets more stable. Kudos to &lt;a href="https://github.com/tbodt" rel="noopener noreferrer"&gt;tbodt&lt;/a&gt; for the awesome work!&lt;/p&gt;
&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;p&gt;I've made a tutorial video on how to set up Neovim on macOS:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/FW2X1CXrU1w"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>ipad</category>
      <category>ios</category>
      <category>ish</category>
    </item>
    <item>
      <title>How I integrated 3D secure for recurring payments with Stripe</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Mon, 31 May 2021 04:00:40 +0000</pubDate>
      <link>https://forem.com/craftzdog/how-i-integrated-3d-secure-for-recurring-payments-with-stripe-dmh</link>
      <guid>https://forem.com/craftzdog/how-i-integrated-3d-secure-for-recurring-payments-with-stripe-dmh</guid>
      <description>&lt;p&gt;Hi, it's &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Takuya&lt;/a&gt; from Japan.&lt;/p&gt;

&lt;p&gt;I'm running a SaaS app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop&lt;/a&gt; which is a subscription-based service.&lt;br&gt;
I use &lt;a href="http://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; to accept payments with credit cards around the world.&lt;br&gt;
Recently, I've got an email from Stripe that users can't renew their subscriptions under RBI regulations in India if your website doesn't support 3D secure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://support.stripe.com/questions/important-updates-to-rbi-regulations-on-recurring-card-payments-in-india" rel="noopener noreferrer"&gt;https://support.stripe.com/questions/important-updates-to-rbi-regulations-on-recurring-card-payments-in-india&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's going to affect my service since I have customers from India.&lt;br&gt;
So, I decided to support 3D secure authentication on my website.&lt;/p&gt;

&lt;p&gt;In terms of implementation, there are several ways to implement a card form for recurring payments.&lt;br&gt;
If you are already using &lt;a href="https://stripe.com/docs/billing/subscriptions/checkout" rel="noopener noreferrer"&gt;Stripe Checkout&lt;/a&gt;, it's easy. All you have to do is &lt;a href="https://support.stripe.com/questions/important-updates-to-rbi-regulations-on-recurring-card-payments-in-india" rel="noopener noreferrer"&gt;enable 3D Secure&lt;/a&gt; in your Billing settings. Then, Stripe basically does all nicely for you.&lt;br&gt;
However, I was using &lt;a href="https://stripe.com/en-jp/payments/elements" rel="noopener noreferrer"&gt;Stripe Elements&lt;/a&gt; and &lt;a href="https://stripe.com/docs/sources" rel="noopener noreferrer"&gt;Sources API&lt;/a&gt; to provide a credit card form. While it provides &lt;a href="https://stripe.dev/elements-examples/" rel="noopener noreferrer"&gt;highly customizable form components&lt;/a&gt;, &lt;a href="https://stripe.com/docs/payments/3d-secure#confirm-payment-intent" rel="noopener noreferrer"&gt;it requires an additional complicated implementation&lt;/a&gt; for 3D secure authentication. Besides, the Sources API is &lt;a href="https://stripe.com/docs/payments/payment-methods/transitioning" rel="noopener noreferrer"&gt;no longer recommended&lt;/a&gt;.&lt;br&gt;
Looks like my code is old since I've implemented it several years ago.&lt;br&gt;
I thought it's time to switch my payment logic from Stripe Elements to Stripe Checkout.&lt;/p&gt;

&lt;p&gt;In this article, I'll share how I migrated from Stripe Elements to Stripe Checkout. It'd be also helpful for those who are planning to adopt Stripe Checkout for your website. Let's get started.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understand a new way to set up future payments
&lt;/h2&gt;

&lt;p&gt;I was so confused when reading Stripe's documentation because my knowledge was outdated.&lt;br&gt;
There are several new APIs you have to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stripe.com/docs/payments/payment-methods" rel="noopener noreferrer"&gt;Payment Methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stripe.com/docs/payments/setup-intents" rel="noopener noreferrer"&gt;Setup Intents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stripe.com/docs/api/checkout/sessions" rel="noopener noreferrer"&gt;Checkout Sessions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I try to explain them as simple as possible:&lt;br&gt;
You've been able to use card tokens retrieved via the Sources API for recurring payments.&lt;br&gt;
But the Sources are now replaced with Payment methods and Setup intents.&lt;br&gt;
You can think like the Sources API has been subdivided into the Payment Methods API and Setup Intents API.&lt;br&gt;
Valid payment methods are attached to customers.&lt;br&gt;
You can use a payment method to charge a customer for recurring payments.&lt;br&gt;
The Setup Intents API allows you to set up a payment method for future payments.&lt;br&gt;
Stripe Checkout creates a Checkout Session for the customer. A setup intent is issued and managed by the checkout session. It attaches a payment method to the customer once successfully finished the session.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enable 3D Secure
&lt;/h2&gt;

&lt;p&gt;As the latest Stripe API supports 3D Secure out of the box, you can enable it from &lt;em&gt;Settings -&amp;gt; Subscriptions and emails -&amp;gt; Manage payments that require 3D Secure&lt;/em&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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2A_mfaDMipb3ToCcUso4Sa7Q.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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2A_mfaDMipb3ToCcUso4Sa7Q.png" alt="Settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, check your Radar rules from &lt;em&gt;Settings -&amp;gt; Radar rules&lt;/em&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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2AEmZBEt7Lj8wuYvJyss4qjw.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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2AEmZBEt7Lj8wuYvJyss4qjw.png" alt="Radar rules"&gt;&lt;/a&gt;&lt;br&gt;
With this configuration, 3D secure will be requested when it is required for card. I don't know which is the best practice, so I try this rule for now.&lt;/p&gt;

&lt;p&gt;Now, you are ready to integrate it!&lt;/p&gt;
&lt;h2&gt;
  
  
  4 pathways users input their card information
&lt;/h2&gt;

&lt;p&gt;In Stripe, each user has a &lt;a href="https://stripe.com/docs/api/customers" rel="noopener noreferrer"&gt;Customer&lt;/a&gt; object, and a &lt;a href="https://stripe.com/docs/api/subscriptions" rel="noopener noreferrer"&gt;Subscription&lt;/a&gt; object is associated with each customer, which allows you to manage his/her subscription status.&lt;br&gt;
Inkdrop doesn't require card information when signing up because it provides free trials. Customers have the following 3 account statuses:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;trial&lt;/code&gt; - In free-trial&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;active&lt;/code&gt; - Has an active subscription&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deactivated&lt;/code&gt; - The subscription has been cancelled 15 days after a payment failure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That completely depends on your business design but I guess it'd be one of the common design patterns. Note that they are my application-specific statuses stored in my server.&lt;br&gt;
With those statuses, the Inkdrop users may input their card information when:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user adds/changes/updates the card detail&lt;/li&gt;
&lt;li&gt;The user starts paying it before the trial expires&lt;/li&gt;
&lt;li&gt;The trial has been expired&lt;/li&gt;
&lt;li&gt;The account has been deactivated&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll explain how to deal with those cases with Stripe Checkout.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. User adds/changes/updates card detail
&lt;/h2&gt;

&lt;p&gt;This is the simplest case.&lt;br&gt;
Users can do it anytime from the website.&lt;br&gt;
Here is the billing page of Inkdrop:&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%2Fcdn-images-1.medium.com%2Fmax%2F2600%2F1%2A8lnOjUBEVGx6Vmo2Uk--mA.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%2Fcdn-images-1.medium.com%2Fmax%2F2600%2F1%2A8lnOjUBEVGx6Vmo2Uk--mA.png" alt="Billing page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can update the billing details on this page. Nothing special.&lt;br&gt;
And when a user clicked &lt;em&gt;'Change / update card'&lt;/em&gt; button, it shows:&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%2Fcdn-images-1.medium.com%2Fmax%2F2600%2F1%2AQS7xgo_fPZa5xsgs94QkDw.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%2Fcdn-images-1.medium.com%2Fmax%2F2600%2F1%2AQS7xgo_fPZa5xsgs94QkDw.png"&gt;&lt;/a&gt;&lt;br&gt;
In this page, the website initiates a new Checkout Session by calling &lt;a href="https://stripe.com/docs/api/checkout/sessions/create" rel="noopener noreferrer"&gt;&lt;code&gt;stripe.checkout.sessions.create&lt;/code&gt;&lt;/a&gt; on the server-side like so:&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;payment_method_types&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;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;success_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redirectSuccessUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;billing_address_collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;needsBillingAddress&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&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;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;payment_method_types&lt;/code&gt; - Inkdrop only accepts credit cards, so it should be always &lt;code&gt;['card']&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mode&lt;/code&gt; - It specifies &lt;code&gt;mode&lt;/code&gt; as &lt;code&gt;'setup'&lt;/code&gt; so that you can use the payment method for future payments.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;success_url&lt;/code&gt; &amp;amp; &lt;code&gt;cancel_url&lt;/code&gt; - You can specify redirection URLs where Stripe will navigate the user after the session.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;billing_address_collection&lt;/code&gt; - If you need to collect the customer's billing address, you can do that on the Checkout page by specifying it as &lt;code&gt;'required'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the website, it retrieves the Session data from the server when opened the above page. When the user pressed 'Input Card' button, it redirects to the Checkout page like so:&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;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirectToCheckout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, the user should see a page something like:&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AEJhCDDQ-UwSeJIWLj7fn2g.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AEJhCDDQ-UwSeJIWLj7fn2g.png" alt="Checkout page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 3D Secure
&lt;/h3&gt;

&lt;p&gt;Use the test cards listed in &lt;a href="https://stripe.com/docs/testing#regulatory-cards" rel="noopener noreferrer"&gt;this page&lt;/a&gt; to test 3D secure.&lt;br&gt;
You should get a popup iframe during a Checkout session as following:&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AJzHPcrjMVeBsdHeaMiSIvg.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AJzHPcrjMVeBsdHeaMiSIvg.png" alt="3D secure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty neat.&lt;/p&gt;
&lt;h3&gt;
  
  
  Process new payment method
&lt;/h3&gt;

&lt;p&gt;After the user input the card information, the Checkout redirects to &lt;code&gt;success_url&lt;/code&gt;. While Stripe automatically attaches the new card to the Customer object, it doesn't anything else for you.&lt;/p&gt;

&lt;p&gt;So, on the &lt;code&gt;success_url&lt;/code&gt;, the Inkdrop server does the following processes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the card brand is supported&lt;/li&gt;
&lt;li&gt;Use the new card as the default payment method&lt;/li&gt;
&lt;li&gt;Retry payment if necessary&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While Stripe accepts JCB cards through the Checkout but Inkdrop doesn't support them, it needs to verify the card brand manually like so:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkValidPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;paymentMethod&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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;jcb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jcb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's necessary to set the new card as the default payment method manually on your server since Stripe only adds it to the customer:&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;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;invoice_settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;default_payment_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's optional if your website provides a UI to select a default card for users.&lt;/p&gt;

&lt;p&gt;If the user has a past-due invoice, Inkdrop retries to charge it:&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;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;expand&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;subscriptions&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latest_invoice&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;latestInvoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latest_invoice&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latestInvoice&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;latestInvoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latestInvoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. User starts paying before the trial expires
&lt;/h2&gt;

&lt;p&gt;Some users may want to finish their free trials and start subscribing to Inkdrop. Users under the free trial would see this:&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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2A_XcLh1MtinfS4wg-QF4g8w.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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2A_XcLh1MtinfS4wg-QF4g8w.png" alt="Free trial"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To provide a way to manually finish their free trials, you have to &lt;strong&gt;create another subscription&lt;/strong&gt; instead of updating the existing subscription.&lt;br&gt;
Actually you can do so during the redirection hook but you shouldn't because there is a UX issue where the price won't be displayed in the Checkout session if you don't specify any &lt;code&gt;line_items&lt;/code&gt; just as you saw in &lt;em&gt;pattern 1&lt;/em&gt;.&lt;br&gt;
For example, you will see it tries to charge &lt;strong&gt;$0 (¥0)&lt;/strong&gt; for the subscription when you use Apple Pay, which is kind of weird:&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AO2G55FcfxWK1kw0UNrh19A.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AO2G55FcfxWK1kw0UNrh19A.png" alt="Apple Pay"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope Stripe will support updating the existing subscriptions with Checkout, but it isn't supported at the moment.&lt;br&gt;
So, you have to create another subscription &lt;em&gt;without a free trial&lt;/em&gt; and remove the old subscription to accomplish that.&lt;/p&gt;

&lt;p&gt;In this case, create a Checkout session like so:&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;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;payment_method_types&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;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subscription&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;success_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;redirectSuccessUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;billing_address_collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;needsBillingAddress&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&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;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tax_rates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;japan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;taxRateJpn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taxRateZero&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;ul&gt;
&lt;li&gt;
&lt;code&gt;mode&lt;/code&gt; - It must be &lt;code&gt;subscription&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;line_items&lt;/code&gt; - A product to newly subscribe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As Stripe doesn't support &lt;a href="https://stripe.com/docs/payments/checkout/taxes#dynamic-tax-rates" rel="noopener noreferrer"&gt;dynamic tax rates&lt;/a&gt; in Japan, I had to implement it myself (Please support it!). People from outside Japan are exempt from payment of a consumption tax if your business is based in Japan.&lt;/p&gt;

&lt;p&gt;By doing so, users can see the price like this:&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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2A2jEzJAhtJIAAfUC32FhaYg.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%2Fcdn-images-1.medium.com%2Fmax%2F2400%2F1%2A2jEzJAhtJIAAfUC32FhaYg.png" alt="Stripe checkout with a subscription item"&gt;&lt;/a&gt;&lt;br&gt;
After a successful checkout, you can cancel the old subscription during the redirection hook:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;removeOldSubscriptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;customerId&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;newSubscription&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subscriptions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&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;activeStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trialing&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;active&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;past_due&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;newSubscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Trial has been expired
&lt;/h2&gt;

&lt;p&gt;This is similar to the pattern 2. Again, Checkout doesn't allow to update the existing subscription directly, you have to re-create a subscription for better UX. For that reason, you can't charge immediately from the trial expiration date. The subscription starts just on a day when the user input the card information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notify users of the trial expiration with webhook
&lt;/h3&gt;

&lt;p&gt;It'd be nice to kindly notify users that their trial has been expired.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not send payment failure notifications&lt;/strong&gt; because they will be surprised and get angry! In the early days, I got some complaints, screaming like "It's a scam! 😡" because they haven't intended to buy or inputted card information (yet). You need to kindly notify their trial expired instead.&lt;br&gt;
I couldn't find that Stripe supports it, so I implemented it myself.&lt;/p&gt;

&lt;p&gt;To accomplish that: When the trial expired and the user hasn't inputted a card, the first payment fails and an event &lt;a href="https://stripe.com/docs/api/events/types#event_types-invoice.payment_failed" rel="noopener noreferrer"&gt;&lt;code&gt;invoice.payment_failed&lt;/code&gt;&lt;/a&gt; fires.&lt;br&gt;
You can know the event through webhook.&lt;br&gt;
In your webhook, check if the user has any cards attached like so:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkCustomerHasPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;customerId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paymentMethods&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;paymentMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user doesn't have a card, then check the number of charge attempts. If it was the first attempt, lock the account like so:&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="c1"&gt;// invoice.payment_failed&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// first attempt&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempt_count&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do things you need&lt;/span&gt;
  &lt;span class="nf"&gt;notifyTrialExpired&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also display the notification about the expiration on the website like this:&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AdPWvjDMfv8bNuOEEHUIsOQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AdPWvjDMfv8bNuOEEHUIsOQ.png" alt="trial expired notification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Account has been deactivated
&lt;/h2&gt;

&lt;p&gt;A customer's subscription is canceled when all charge retries for a payment failed as I configured Stripe like this from &lt;em&gt;Settings -&amp;gt; Subscriptions and emails -&amp;gt; Manage failed payments for subscriptions&lt;/em&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AnGG4dLrd4H8A_7vqwjespQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AnGG4dLrd4H8A_7vqwjespQ.png" alt="subscription status"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the website, it displays the account has been deactivated:&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ACVwYMynuAH4YtZDziTRvUQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ACVwYMynuAH4YtZDziTRvUQ.png" alt="deactivated"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To reactivate the account, you can simply create a new subscription via the Checkout. Then, process the account to reactivate in your server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing the plan (Monthly ⇄ Yearly)
&lt;/h2&gt;

&lt;p&gt;Inkdrop provides monthly and annual plans.&lt;br&gt;
Users can change it anytime.&lt;br&gt;
To change the existing subscription:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ignoreNoSubscriptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cancel_at_period_end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// avoid double-charge&lt;/span&gt;
  &lt;span class="na"&gt;proration_behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create_prorations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// do not forget!&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// If the free trial remains, specify the same `trial_end` value&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trial_end&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trial_end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trial_end&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;newSubscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;params&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When required 3D secure for renewing the subscription
&lt;/h2&gt;

&lt;p&gt;Stripe supports an option "&lt;a href="https://stripe.com/docs/invoicing/paying#send-3dsecure-emails" rel="noopener noreferrer"&gt;Send a Stripe-hosted link for cardholders to authenticate when required&lt;/a&gt;".&lt;br&gt;
So, Stripe will automatically send a notification email to your users when required an additional action to complete the payment.&lt;br&gt;
But, it'd be also nice to display the notification on the website like so:&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AGwZm1eJWiEi5xUhWzs-9Ow.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AGwZm1eJWiEi5xUhWzs-9Ow.png" alt="3D secure auth required"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can determine if the payment needs 3D secure authentication like so:&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;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;past_due&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;latest_invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;latestInvoice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;payment_intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;latestInvoice&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;paymentIntent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentIntent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;requires_source_action&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="nx"&gt;paymentIntent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;requires_action&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="nx"&gt;paymentIntent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next_action&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="nx"&gt;paymentIntent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_secret&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Action required&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;Then, proceed to 3D secure authentication by calling &lt;a href="https://stripe.com/docs/js/payment_intents/confirm_card_payment" rel="noopener noreferrer"&gt;&lt;code&gt;confirmCardPayment&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmCardPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentIntent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upgrade the API version
&lt;/h2&gt;

&lt;p&gt;When everything is ready to roll out, it's time to upgrade the API version. &lt;br&gt;
If you are using the old API version, you have to upgrade it to the latest version from &lt;em&gt;Developers -&amp;gt; API version&lt;/em&gt;. You should see the upgrade button if you are on the old one.&lt;br&gt;
Be careful to do this because it immediately affects your production environment!&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2A1Jk_YvpdVXydUrq-Wde6jQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2A1Jk_YvpdVXydUrq-Wde6jQ.png" alt="API Version"&gt;&lt;/a&gt;&lt;br&gt;
I hope Stripe will allow testing the new API before upgrading it because I had many unexpected errors when switching it, which I left a sour taste in my mouth:&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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ApWqlx1KSQIs04Safiwbuzw.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%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ApWqlx1KSQIs04Safiwbuzw.png" alt="API failures"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It's never been such simple without Stripe
&lt;/h2&gt;

&lt;p&gt;I've implemented credit card payments with PayPal in the past but it was so complicated and hard. The documentation was not clear to understand.&lt;br&gt;
Stripe is so easy to integrate compared to that.&lt;br&gt;
I still have some small issues as I mentioned in the article, but I'm basically happy with Stripe.&lt;br&gt;
Besides, Stripe's website, dashboard, and mobile app are so beautiful and I've got a lot of inspiration from them.&lt;br&gt;
You will learn their good UX practices while building your product with Stripe.&lt;/p&gt;

&lt;p&gt;That's it! I hope it's helpful for building your SaaS business.&lt;/p&gt;

&lt;h4&gt;
  
  
  Follow me online
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Check out my app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop - A Markdown note-taking app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe Newsletter &lt;a href="http://eepurl.com/dNgJo6" rel="noopener noreferrer"&gt;http://eepurl.com/dNgJo6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;https://twitter.com/inkdrop_app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog &lt;a href="https://blog.inkdrop.info/" rel="noopener noreferrer"&gt;https://blog.inkdrop.info/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discord community &lt;a href="https://discord.gg/QfsG5Kj" rel="noopener noreferrer"&gt;https://discord.gg/QfsG5Kj&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;https://instagram.com/craftzdog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>stripe</category>
      <category>javascript</category>
      <category>saas</category>
    </item>
    <item>
      <title>Running a React Native app on Android Emulator in M1 Mac</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Fri, 14 May 2021 09:49:02 +0000</pubDate>
      <link>https://forem.com/craftzdog/running-a-react-native-app-on-android-emulator-in-m1-mac-28c7</link>
      <guid>https://forem.com/craftzdog/running-a-react-native-app-on-android-emulator-in-m1-mac-28c7</guid>
      <description>&lt;p&gt;Hi. I have an M1 MacBook Air and have been waiting for Android Studio to support M1 macs so that I can use it for developing my React Native project.&lt;br&gt;
It's been about a half year since an Android Emulator developer tweeted that he managed to run it:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1332474914398322689-848" src="https://platform.twitter.com/embed/Tweet.html?id=1332474914398322689"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1332474914398322689-848');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1332474914398322689&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;It had a performance issue though, I heard that the recent Android Studio Preview has supported M1 chips pretty well. So I tried it.&lt;br&gt;
I've got several issues to make it work, but thanks to &lt;a href="https://twitter.com/yrezgui" rel="noopener noreferrer"&gt;Yacine&lt;/a&gt;, I managed to solve them.&lt;br&gt;
I'm gonna share how to get your RN project to work on Android Emulator on your M1 mac.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9z3qr2f3w3254lvr7xhf.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9z3qr2f3w3254lvr7xhf.JPG" alt="_DSF0494"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Install Android Studio Preview (Canary build)
&lt;/h2&gt;

&lt;p&gt;Android Emulator for M1 is not available on stable releases yet. You have to use Android Studio Preview.&lt;br&gt;
I'm using Arctic Fox (2020.3.1) Canary 15.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/studio/preview" rel="noopener noreferrer"&gt;Android Studio Preview  |  Android Developers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setup M1 Android Emulator
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to Tools → AVD Manager, then click &lt;em&gt;+Create Virtual Device...&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Pick a device definition you like, e.g., Pixel 5. Click Next.&lt;/li&gt;
&lt;li&gt;Pick a system image whose ABI is &lt;code&gt;arm64-v8a&lt;/code&gt;. If you can't find any of them, check out the 'Other Images' tab.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9qg9s2mo262z5xv2xfp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9qg9s2mo262z5xv2xfp.png" alt="Screen Shot 2021-05-14 at 17.59.32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Download the 'S' or 'R (30)' API level.&lt;br&gt;
Now you can run the Android Emulator natively. Congrats!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3t1f65fecbski0y5ep5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3t1f65fecbski0y5ep5.png" alt="Screen_Shot_2021-05-14_at_18_05_43"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Build errors you may get
&lt;/h2&gt;

&lt;p&gt;If you are lucky enough, your project will be built just fine without any errors. Enjoy a cup of coffee.&lt;br&gt;
However, you may get some build errors like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The Jdk installation is invalid.
Selected Jdk location is /Applications/Android Studio Preview.app/Contents/jre/jdk/Contents/Home.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gradle sync failed: Could not install Gradle distribution from 'https://services.gradle.org/distributions/gradle-6.0.1-all.zip'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Android Studio Preview is not a stable version, they can happen for unknown reasons, even if the configurations looked correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clear caches
&lt;/h2&gt;

&lt;p&gt;There are several ways to clear project caches.&lt;/p&gt;

&lt;p&gt;First, try &lt;em&gt;File → Invalidate Caches / Restart...&lt;/em&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7io7kjhi41dlrisb5u7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7io7kjhi41dlrisb5u7a.png" alt="Screen_Shot_2021-05-14_at_18_16_37"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, remove the following file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Library/Application\ Support/Google/AndroidStudioPreview2020.3/options/jdk.table.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the path can be different based on the preview version (In this case, it's &lt;code&gt;2020.3&lt;/code&gt;).&lt;br&gt;
Then, launch Android Studio.&lt;/p&gt;
&lt;h2&gt;
  
  
  Completely uninstall Android Studio Preview and try again
&lt;/h2&gt;

&lt;p&gt;If you still have the compile error, try reinstalling Android Studio Preview.&lt;br&gt;
First, uninstall it completely. Be careful to do them, or you may lose the important files:&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;rm&lt;/span&gt; &lt;span class="nt"&gt;-Rf&lt;/span&gt; /Applications/Android&lt;span class="se"&gt;\ &lt;/span&gt;Studio&lt;span class="se"&gt;\ &lt;/span&gt;Preview.app
&lt;span class="nb"&gt;rm&lt;/span&gt; ~/Library/Preferences/com.google.android.studio-EAP.plist  ~/Library/Preferences/com.google.android.studio.plist
&lt;span class="nb"&gt;rm&lt;/span&gt; /Users/nora/Library/Preferences/com.android.Emulator.plist
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s1"&gt;'~/Library/Application Support/Google/AndroidStudioPreview2020.3'&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/Library/Logs/Google/AndroidStudioPreview2020.3
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; .gradle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, install it again.&lt;br&gt;
That worked for me.&lt;/p&gt;



&lt;p&gt;Now, my RN project works smoothly on my M1 MacBook Air!&lt;br&gt;
I'm pretty happy.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1393120068184121350-256" src="https://platform.twitter.com/embed/Tweet.html?id=1393120068184121350"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1393120068184121350-256');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1393120068184121350&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h4&gt;
  
  
  Follow me online
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Check out my app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop - A Markdown note-taking app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe Newsletter &lt;a href="http://eepurl.com/dNgJo6" rel="noopener noreferrer"&gt;http://eepurl.com/dNgJo6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;https://twitter.com/inkdrop_app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;YouTube channel &lt;a href="https://youtube.com/c/devaslife" rel="noopener noreferrer"&gt;https://youtube.com/c/devaslife&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog &lt;a href="https://blog.inkdrop.info/" rel="noopener noreferrer"&gt;https://blog.inkdrop.info/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discord community &lt;a href="https://discord.gg/QfsG5Kj" rel="noopener noreferrer"&gt;https://discord.gg/QfsG5Kj&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;https://instagram.com/craftzdog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>reactnative</category>
      <category>android</category>
      <category>m1mac</category>
    </item>
    <item>
      <title>Fixing gradle errors of C++ JSI native modules</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Fri, 14 May 2021 02:53:14 +0000</pubDate>
      <link>https://forem.com/craftzdog/fixing-gradle-errors-of-c-jsi-native-modules-4336</link>
      <guid>https://forem.com/craftzdog/fixing-gradle-errors-of-c-jsi-native-modules-4336</guid>
      <description>&lt;p&gt;I have a React Native project with some JSI native modules implemented in C++ like &lt;a href="https://github.com/craftzdog/react-native-quick-base64"&gt;react-native-quick-base64&lt;/a&gt;.&lt;br&gt;
Today I upgraded Android Studio to 4.2.1, and it failed to build my project with the following CMake error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/***/Library/Android/sdk/cmake/3.6.4111459/bin/cmake -H/Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android -B/Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android/.cxx/cmake/release/armeabi-v7a
CMake Error at .cxx/cmake/release/armeabi-v7a/CMakeFiles/3.6.0-rc2/CMakeSystem.cmake:6 (include):
  include could not find load file:

    /Users/***/Library/Android/sdk/ndk/23.0.7123448/build/cmake/android.toolchain.cmake
Call Stack (most recent call first):
  CMakeLists.txt


-- The C compiler identification is AppleClang 12.0.5.12050022
-- The CXX compiler identification is AppleClang 12.0.5.12050022
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
CMake Error at /Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android/.cxx/cmake/release/armeabi-v7a/CMakeFiles/3.6.0-rc2/CMakeSystem.cmake:6 (include):
  include could not find load file:

    /Users/***/Library/Android/sdk/ndk/23.0.7123448/build/cmake/android.toolchain.cmake
Call Stack (most recent call first):
  /Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android/.cxx/cmake/release/armeabi-v7a/CMakeFiles/CMakeTmp/CMakeLists.txt:2 (project)


CMake Error: Internal CMake error, TryCompile configure of cmake failed
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- broken
CMake Error at /Users/***/Library/Android/sdk/cmake/3.6.4111459/share/cmake-3.6/Modules/CMakeTestCCompiler.cmake:61 (message):
  The C compiler
  "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc"
  is not able to compile a simple test program.

  It fails with the following output:





  CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
  CMakeLists.txt


-- Configuring incomplete, errors occurred!
See also "/Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android/.cxx/cmake/release/armeabi-v7a/CMakeFiles/CMakeOutput.log".
See also "/Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android/.cxx/cmake/release/armeabi-v7a/CMakeFiles/CMakeError.log".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like that's because the NDK path has been changed, so &lt;code&gt;/Users/***/Library/Android/sdk/ndk/23.0.7123448/build/cmake/android.toolchain.cmake&lt;/code&gt; does not exist anymore.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;./gradlew clean&lt;/code&gt; in &lt;code&gt;android&lt;/code&gt; folder didn't clean them up.&lt;br&gt;
So, I manually removed &lt;code&gt;build/&lt;/code&gt; and &lt;code&gt;.cxx/&lt;/code&gt; directories from the module directory:&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;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ./node_modules/react-native-quick-md5/android/.cxx ./node_modules/react-native-quick-base64/android/.cxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I got another error like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./gradlew :react-native-quick-base64:externalNativeBuildRelease

&amp;gt; Configure project :app

The Kotlin Gradle plugin was loaded multiple times in different subprojects, which is not supported and may break the build.
This might happen in subprojects that apply the Kotlin plugins with the Gradle 'plugins { ... }' DSL if they specify explicit versions, even if the versions are equal.
Please add the Kotlin plugin to the common parent project or the root project, then remove the versions in the subprojects.
If the parent project does not need the plugin, add 'apply false' to the plugin line.
See: https://docs.gradle.org/current/userguide/plugins.html#sec:subprojects_plugins_dsl
The Kotlin plugin was loaded in the following projects: ':react-native-aes-gcm-crypto', ':react-native-webview'

&amp;gt; Task :react-native-quick-base64:externalNativeBuildRelease FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':react-native-quick-base64:externalNativeBuildRelease'.
&amp;gt; java.io.FileNotFoundException: /Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android/.cxx/cmake/release/armeabi-v7a/android_gradle_build.json (No such file or directory)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD FAILED in 4s
2 actionable tasks: 1 executed, 1 up-to-date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, the following file does not exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/***/Developments/inkdrop/inkdrop-mobile/node_modules/react-native-quick-base64/android/.cxx/cmake/release/armeabi-v7a/android_gradle_build.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means that gradle doesn't recompile the C++ sources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run gradle with &lt;code&gt;--rerun-tasks&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Just running the gradle task does not recompile the C code.&lt;br&gt;
You have to add &lt;code&gt;--rerun-tasks&lt;/code&gt; option to do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew :react-native-quick-base64:externalNativeBuildRelease --rerun-tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It solved the issue.&lt;/p&gt;

&lt;h4&gt;
  
  
  Follow me online
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Check out my app called &lt;a href="https://www.inkdrop.app/"&gt;Inkdrop - A Markdown note-taking app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe Newsletter &lt;a href="http://eepurl.com/dNgJo6"&gt;http://eepurl.com/dNgJo6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter &lt;a href="https://twitter.com/inkdrop_app"&gt;https://twitter.com/inkdrop_app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog &lt;a href="https://blog.inkdrop.info/"&gt;https://blog.inkdrop.info/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discord community &lt;a href="https://discord.gg/QfsG5Kj"&gt;https://discord.gg/QfsG5Kj&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram &lt;a href="https://instagram.com/craftzdog"&gt;https://instagram.com/craftzdog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>reactnative</category>
      <category>jsi</category>
    </item>
    <item>
      <title>How to build an air quality monitor using Raspberry Pi Zero W</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Thu, 13 May 2021 02:32:15 +0000</pubDate>
      <link>https://forem.com/craftzdog/how-to-build-an-air-quality-monitor-using-raspberry-pi-zero-w-54eh</link>
      <guid>https://forem.com/craftzdog/how-to-build-an-air-quality-monitor-using-raspberry-pi-zero-w-54eh</guid>
      <description>&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F1vldwz10efzfph122q6ziy3vuf22" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F1vldwz10efzfph122q6ziy3vuf22"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hi, it's &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Takuya&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Knowing the air quality is useful to keep yourself productive because the bad air quality would affect your brain performance more than you'd think.&lt;br&gt;
So, I built a room air quality monitor that displays the temperature, humidity, CO2 density, and barometric pressure of my home office.&lt;br&gt;
It notifies with sound when the CO2 level gets more than 1,000 ppm - So, I can know when to refresh the air.&lt;br&gt;
I'm gonna show you a walkthrough of how to build it.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0gNnUWtbmeI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fmmsu7g5gkyqemoz0s4ydvt8uf63e" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fmmsu7g5gkyqemoz0s4ydvt8uf63e"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the architecture overview.&lt;br&gt;
It is made with a Raspberry Pi Zero W, which runs Nginx and has an ANAVI Infrared pHAT with some sensors attached.&lt;br&gt;
And you can view it from a browser on any device.&lt;/p&gt;
&lt;h3&gt;
  
  
  Unbox a Raspberry Pi Zero W
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fo9d6elj2n3z0qibkmf0ccti3pol3" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fo9d6elj2n3z0qibkmf0ccti3pol3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3eyF0xJ" rel="noopener noreferrer"&gt;https://amzn.to/3eyF0xJ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  SanDisk 32GB MicroSD Card
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F82y3l7b2eszw3qlm8vblfk0k3s0p" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F82y3l7b2eszw3qlm8vblfk0k3s0p"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3hlJfi3" rel="noopener noreferrer"&gt;https://amzn.to/3hlJfi3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Anker 2-in-1 USB C Memory Card Reader
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fcse1crawk1g1h8mx0993grgxm76e" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fcse1crawk1g1h8mx0993grgxm76e"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/33vdf31" rel="noopener noreferrer"&gt;https://amzn.to/33vdf31&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Download Raspberry Pi Imager
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fk57nvajbis11ejokmxa0p3k3tjng" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fk57nvajbis11ejokmxa0p3k3tjng"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.org/software/" rel="noopener noreferrer"&gt;https://www.raspberrypi.org/software/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Write a Raspberry Pi OS to the SD Card
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fkzolwnggylkz0kr9l3tjbfb27l3t" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fkzolwnggylkz0kr9l3tjbfb27l3t"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure your Wi-Fi network
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fpkmogohtphggcfcwhp759lkw4u51" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fpkmogohtphggcfcwhp759lkw4u51"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get your Rasberry Pi to connect your Wi-Fi network, create &lt;code&gt;wpa_supplicant.conf&lt;/code&gt; file in the root directory of the SD card memory.&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="nv"&gt;ctrl_interface&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/run/wpa_supplicant &lt;span class="nv"&gt;GROUP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;netdev
&lt;span class="nv"&gt;update_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Insert 2 letter ISO 3166-1 country code here&amp;gt;

&lt;span class="nv"&gt;network&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;
 &lt;span class="nv"&gt;ssid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Name of your wireless LAN&amp;gt;"&lt;/span&gt;
 &lt;span class="nv"&gt;psk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Password for your wireless LAN&amp;gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raspberrypi.org/documentation/configuration/wireless/headless.md" rel="noopener noreferrer"&gt;https://www.raspberrypi.org/documentation/configuration/wireless/headless.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enable SSH server
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Ffhbdbbfatgyqquyswrydwc137bxz" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Ffhbdbbfatgyqquyswrydwc137bxz"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have to enable SSH server to connect through the network.&lt;br&gt;
Create an empty file named "ssh" in the root directory by &lt;code&gt;touch&lt;/code&gt; command.&lt;/p&gt;
&lt;h3&gt;
  
  
  Boot the Raspberry Pi
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fklcv93uo6xu5i43gltmgeb92ydun" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fklcv93uo6xu5i43gltmgeb92ydun"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Check if connected to the Wi-Fi network
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fjozfjfgkilzxdkomqosx6le4ej2p" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fjozfjfgkilzxdkomqosx6le4ej2p"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping raspberrypi.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Log in to the Raspberry Pi via SSH
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F9q2ndhic6u6luyamxol8u1ixj9bm" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F9q2ndhic6u6luyamxol8u1ixj9bm"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh pi@raspberrypi.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;user: &lt;code&gt;pi&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;pass: &lt;code&gt;raspberry&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unbox an ANAVI Infrared pHAT
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fl33og8431a8fedltoxwxwft4b8hg" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fl33og8431a8fedltoxwxwft4b8hg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.crowdsupply.com/anavi-technology/infrared-phat" rel="noopener noreferrer"&gt;https://www.crowdsupply.com/anavi-technology/infrared-phat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ANAVI Infrared pHAT is an add-on for the Raspberry Pi family, which adds an ability to control your old consumer electronic devices like air conditioning with infrared signals.&lt;br&gt;
On top of that, it supports sensor modules.&lt;br&gt;
With the advanced kit, you can get these sensor modules for the air quality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTU21D - temperature &amp;amp; humidity&lt;/li&gt;
&lt;li&gt;BMP180 - temperature &amp;amp; barometric&lt;/li&gt;
&lt;li&gt;BH1750 - light&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Install the ANAVI Infrared pHAT to the Raspberry Pi Zero
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F5zff7y4v6rgysv9dyrb2w8ayvsg7" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F5zff7y4v6rgysv9dyrb2w8ayvsg7"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Connect the sensors to the I2C slots
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fri5phyxwlyvavfmrqevpmqjd51r6" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fri5phyxwlyvavfmrqevpmqjd51r6"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  MH-Z19 - CO2 density sensor
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Foodqhamjtxttawwt1f0jxzr8t0cb" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Foodqhamjtxttawwt1f0jxzr8t0cb"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3uCcIbo" rel="noopener noreferrer"&gt;https://amzn.to/3uCcIbo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MH-Z19 is a low-cost CO2 density sensor.&lt;/p&gt;
&lt;h3&gt;
  
  
  Connect MH-Z19 to the UART slot
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fpp3qkqlqyc6ucgbozfr8bqos6ecg" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fpp3qkqlqyc6ucgbozfr8bqos6ecg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Check if MH-Z19 is working
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fpyk39a83qerhud22t1al7d5rsknh" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fpyk39a83qerhud22t1al7d5rsknh"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Update Raspberry Pi packages
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fnn4c6xtcsrkdfa66vd7l915olqve" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fnn4c6xtcsrkdfa66vd7l915olqve"&gt;&lt;/a&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;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install git &amp;amp; build tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git build-essential
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install python-smbus and i2c-tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python-smbus i2c-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable I2C interface
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fo2torgjyxba5ms458mpnssfvo7xy" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fo2torgjyxba5ms458mpnssfvo7xy"&gt;&lt;/a&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;sudo &lt;/span&gt;raspi-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose 'Interface Options' -&amp;gt; 'I2C' -&amp;gt; 'Yes'.&lt;br&gt;
Then, reboot.&lt;/p&gt;
&lt;h3&gt;
  
  
  Check the I2C interface working
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F5btjfbuoypb64lygevj9f71vo0z8" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F5btjfbuoypb64lygevj9f71vo0z8"&gt;&lt;/a&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;sudo &lt;/span&gt;i2cdetect &lt;span class="nt"&gt;-y&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get the example code for testing the sensors
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fjoajwanq9nfzsjcj4br12dyhjhk2" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fjoajwanq9nfzsjcj4br12dyhjhk2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/AnaviTechnology/anavi-examples" rel="noopener noreferrer"&gt;https://github.com/AnaviTechnology/anavi-examples&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/AnaviTechnology/anavi-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;anavi-examples
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install wiringpi
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;wiringpi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build the example code
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fgsn3dh16v1yqamp9iinfh5bqbvue" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fgsn3dh16v1yqamp9iinfh5bqbvue"&gt;&lt;/a&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;cd&lt;/span&gt; ./sensors/HTU21D/c
make
./HTU21D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Serial interface
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fowoktt24g76q9ckr5ymlrrn04uto" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fowoktt24g76q9ckr5ymlrrn04uto"&gt;&lt;/a&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;sudo &lt;/span&gt;raspi-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose 'Interface Options' -&amp;gt; 'Serial Port' -&amp;gt; 'No' -&amp;gt; 'Yes'.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install python-pip
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python-pip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install a Python module for mh-z19
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mh-z19
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; mh_z19
&lt;span class="c"&gt;# -&amp;gt; {"co2": 5000}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Put MH-Z19 outside for a while for calibration
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fm75lbw64142cz3zrbvcsgxikppzu" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fm75lbw64142cz3zrbvcsgxikppzu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run ZERO point calibration
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fjvaug4tjftovuuqk0hqpnnkip0d2" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fjvaug4tjftovuuqk0hqpnnkip0d2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the device is put outside, run ZERO point calibration to calibrate the CO2 sensor:&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;sudo &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; mh_z19 &lt;span class="nt"&gt;--zero_point_calibration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a while:&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;sudo &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; mh_z19
&lt;span class="c"&gt;# -&amp;gt; {"co2": 400}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install nginx web server
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fc94cmjfl0cwqt8vifswhcqm2vf0g" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fc94cmjfl0cwqt8vifswhcqm2vf0g"&gt;&lt;/a&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clone a web UI
&lt;/h3&gt;

&lt;p&gt;I published my web UI for displaying the sensor data on GitHub here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/craftzdog/anavi-phat-sensors-ui" rel="noopener noreferrer"&gt;https://github.com/craftzdog/anavi-phat-sensors-ui&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can reuse it by cloning the repository.&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;cd&lt;/span&gt; ~
git clone https://github.com/craftzdog/anavi-phat-sensors-ui
&lt;span class="nb"&gt;cd &lt;/span&gt;anavi-phat-sensors-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build C programs for the sensors
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F5vj4af7qmvaf70hgorq518cnijai" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F5vj4af7qmvaf70hgorq518cnijai"&gt;&lt;/a&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;cd &lt;/span&gt;sensors/BH1750
make
&lt;span class="nb"&gt;cd&lt;/span&gt; ../HTU21D
make
&lt;span class="nb"&gt;cd&lt;/span&gt; ../BMP180
make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prepare test data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/anavi-phat-sensors-ui
&lt;span class="nb"&gt;mkdir &lt;/span&gt;data
./sensors/HTU21D/HTU21D &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./data/HTU21D.json
./sensors/BMP180/BMP180 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./data/BMP180.json
./sensors/BH1750/BH1750 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./data/BH1750.json
&lt;span class="nb"&gt;sudo &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; mh_z19 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./data/MH_Z19.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure nginx
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Ftn1ird21grmhut0t9qc4c186ojbq" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Ftn1ird21grmhut0t9qc4c186ojbq"&gt;&lt;/a&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;sudo &lt;/span&gt;vi /etc/nginx/sites-available/default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/home/pi/anavi-phat-sensors-ui&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, restart nginx.&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;sudo&lt;/span&gt; /etc/init.d/nginx restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Open the web interface from browser
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fshjqj8hra87mwjgkorotu01ug2ep" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fshjqj8hra87mwjgkorotu01ug2ep"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fz8sztlmjoo62yuz0si1uiqel8t31" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fz8sztlmjoo62yuz0si1uiqel8t31"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Boom!&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure root's crontab to update data every 5 minutes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/5 * * * * /usr/bin/python -m mh_z19                &amp;gt; /home/pi/anavi-phat-sensors-ui/data/MH_Z19.json
*/5 * * * * /home/pi/anavi-phat-sensors-ui/sensors/HTU21D/HTU21D &amp;gt; /home/pi/anavi-phat-sensors-ui/data/HTU21D.json
*/5 * * * * /home/pi/anavi-phat-sensors-ui/sensors/BMP180/BMP180 &amp;gt; /home/pi/anavi-phat-sensors-ui/data/BMP180.json
*/5 * * * * /home/pi/anavi-phat-sensors-ui/sensors/BH1750/BH1750 &amp;gt; /home/pi/anavi-phat-sensors-ui/data/BH1750.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Put the Raspberry Pi in a cable box
&lt;/h3&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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F8dpzrvjliug4maw4fx8idnnlc7q3" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2F8dpzrvjliug4maw4fx8idnnlc7q3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fmpdydsbpzzvuah3zmj6p2j7vv6st" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Fmpdydsbpzzvuah3zmj6p2j7vv6st"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fzenn-user-upload%2Ffdoc38l8qkii3kcp5qviz9fnpqc1" 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%2Fstorage.googleapis.com%2Fzenn-user-upload%2Ffdoc38l8qkii3kcp5qviz9fnpqc1"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That's it.&lt;br&gt;
I hope you found it helpful.&lt;br&gt;
Enjoy hacking.&lt;/p&gt;

&lt;h4&gt;
  
  
  Follow me online
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Check out my app called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop - A Markdown note-taking app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe Newsletter &lt;a href="http://eepurl.com/dNgJo6" rel="noopener noreferrer"&gt;http://eepurl.com/dNgJo6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;https://twitter.com/inkdrop_app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog &lt;a href="https://blog.inkdrop.info/" rel="noopener noreferrer"&gt;https://blog.inkdrop.info/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discord community &lt;a href="https://discord.gg/QfsG5Kj" rel="noopener noreferrer"&gt;https://discord.gg/QfsG5Kj&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Instagram &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;https://instagram.com/craftzdog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>raspberrypi</category>
      <category>diy</category>
      <category>iot</category>
    </item>
    <item>
      <title>A batch-JS for downloading invoice PDFs from Stripe</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Wed, 07 Apr 2021 11:33:31 +0000</pubDate>
      <link>https://forem.com/craftzdog/a-batch-js-for-downloading-invoice-pdfs-from-stripe-4m0n</link>
      <guid>https://forem.com/craftzdog/a-batch-js-for-downloading-invoice-pdfs-from-stripe-4m0n</guid>
      <description>&lt;p&gt;I'm running &lt;a href="https://www.inkdrop.app/"&gt;a small business&lt;/a&gt; that uses Stripe for accepting payments from users.&lt;br&gt;
While the Stripe dashboard is quite useful, it doesn't provide a quick way to download invoice PDF files for accounting purposes.&lt;br&gt;
So, I wrote a batch script to do so:&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="nx"&gt;initStripe&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;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&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;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exec&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;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;moment&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;moment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initStripe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk_live_****************&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&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;datetime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YYYY-MM-DDTHH:mm:ss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&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;starting_after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;

  &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2020-01-01T00:00:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2020-12-31T23:59:59&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;starting_after&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;got&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;starting_after&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount_paid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invoice is not paid:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Processing invoice:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paidAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status_transitions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paid_at&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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;dest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoices&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;2020&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paidAt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YYYYMMDD-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s2"&gt;`/usr/local/bin/wget '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoice_pdf&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' -O '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dest&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="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Downloaded:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to process invoice:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;starting_after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;has_more&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DONE&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="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ERROR:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="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;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;It retrieves a list of the invoices of the given term by specifying &lt;code&gt;created.gte&lt;/code&gt; and &lt;code&gt;created.lt&lt;/code&gt; parameters:&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2020-01-01T00:00:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2020-12-31T23:59:59&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;starting_after&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, it retrieves all the invoices issued in 2020.&lt;br&gt;
It only processes the invoices with paid status by checking &lt;code&gt;invoice.status&lt;/code&gt; and &lt;code&gt;invoice.amount_paid&lt;/code&gt;.&lt;br&gt;
An invoice data has &lt;code&gt;invoice_pdf&lt;/code&gt; field, which is a URL to the invoice PDF.&lt;br&gt;
Since downloading a PDF takes time because Stripe generates it on-demand, the script processes up to 10 items at once.&lt;br&gt;
Then, it downloads to &lt;code&gt;./invoices/2020/&lt;/code&gt;. You can change it.&lt;/p&gt;

&lt;p&gt;Hope that helps!&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>javascript</category>
      <category>accounting</category>
    </item>
    <item>
      <title>How to get next.js and vercel to work on M1 mac</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Mon, 22 Mar 2021 02:12:32 +0000</pubDate>
      <link>https://forem.com/craftzdog/how-to-get-next-js-to-work-on-m1-mac-3222</link>
      <guid>https://forem.com/craftzdog/how-to-get-next-js-to-work-on-m1-mac-3222</guid>
      <description>&lt;h2&gt;
  
  
  Use &lt;code&gt;next@canary&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I'm building my blog website using next.js and vercel.&lt;br&gt;
Today, I was working on an M1 MacBook Air and I got the following error when installing the dependency packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;warning Error running install script for optional dependency: "/***/blog/node_modules/sharp: Command failed.
Exit code: 1
Command: (node install/libvips &amp;amp;&amp;amp; node install/dll-copy &amp;amp;&amp;amp; prebuild-install) || (node-gyp rebuild &amp;amp;&amp;amp; node install/dll-copy)
Arguments:
Directory: /***/blog/node_modules/sharp
Output:
info sharp Downloading https://github.com/lovell/sharp-libvips/releases/download/v8.10.0/libvips-8.10.0-darwin-arm64v8.tar.br
ERR! sharp Prebuilt libvips 8.10.0 binaries are not yet available for darwin-arm64v8
info sharp Attempting to build from source via node-gyp but this may fail due to the above error
info sharp Please see https://sharp.pixelplumbing.com/install for required dependencies
gyp info it worked if it ends with ok
gyp info using node-gyp@7.1.2
gyp info using node@15.5.0 | darwin | arm64
gyp info find Python using Python version 3.9.1 found at \"/opt/homebrew/opt/python@3.9/bin/python3.9\"
gyp info spawn /opt/homebrew/opt/python@3.9/bin/python3.9
gyp info spawn args [
gyp info spawn args   '/opt/homebrew/Cellar/node/15.5.0/libexec/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'make',
gyp info spawn args   '-I',
gyp info spawn args   '/***/blog/node_modules/sharp/build/config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/opt/homebrew/Cellar/node/15.5.0/libexec/lib/node_modules/npm/node_modules/node-gyp/addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/Users/***/Library/Caches/node-gyp/15.5.0/include/node/common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=/Users/***/Library/Caches/node-gyp/15.5.0',
gyp info spawn args   '-Dnode_gyp_dir=/opt/homebrew/Cellar/node/15.5.0/libexec/lib/node_modules/npm/node_modules/node-gyp',
gyp info spawn args   '-Dnode_lib_file=/Users/***/Library/Caches/node-gyp/15.5.0/&amp;lt;(target_arch)/node.lib',
gyp info spawn args   '-Dmodule_root_dir=/***/blog/node_modules/sharp',
gyp info spawn args   '-Dnode_engine=v8',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'build',
gyp info spawn args   '-Goutput_dir=.'
gyp info spawn args ]
gyp info spawn make
gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ]
  CC(target) Release/obj.target/nothing/../node-addon-api/nothing.o
  LIBTOOL-STATIC Release/nothing.a
warning: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool: archive library: Release/nothing.a the table of contents is empty (no object file members in the library define global symbols)
  TOUCH Release/obj.target/libvips-cpp.stamp
  CXX(target) Release/obj.target/sharp/src/common.o
../src/common.cc:24:10: fatal error: 'vips/vips8' file not found
#include &amp;lt;vips/vips8&amp;gt;
         ^~~~~~~~~~~~
1 error generated.
make: *** [Release/obj.target/sharp/src/common.o] Error 1
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/opt/homebrew/Cellar/node/15.5.0/libexec/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:194:23)
gyp ERR! stack     at ChildProcess.emit (node:events:376:20)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:284:12)
gyp ERR! System Darwin 20.3.0
gyp ERR! command \"/opt/homebrew/Cellar/node/15.5.0/bin/node\" \"/opt/homebrew/Cellar/node/15.5.0/libexec/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js\" \"rebuild\"
gyp ERR! cwd /***/blog/node_modules/sharp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, it looks like the error came from &lt;code&gt;sharp&lt;/code&gt;.&lt;br&gt;
I found the related issue on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/lovell/sharp/issues/2460"&gt;Can't compile under Apple Silicon M1 arm64 · Issue #2460 · lovell/sharp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And I checked nextjs and found this commit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel/next.js/commit/99a4ea6e9a3a4a591d2a57c7b8f906d5ff30fd63#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519"&gt;feat(next/image): remove &lt;code&gt;sharp&lt;/code&gt; for wasm variant (#22253) · vercel/next.js@99a4ea6&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This pull request removes the native &lt;code&gt;sharp&lt;/code&gt; dependency (which doesn't work on some Linux variants, nor &lt;strong&gt;M1 Mac&lt;/strong&gt;) and replaces it with a wasm equivalent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, nextjs dropped the dependency on &lt;code&gt;sharp&lt;/code&gt; as of &lt;code&gt;v10.0.10-canary.6&lt;/code&gt;.&lt;br&gt;
Installing &lt;code&gt;next@canary&lt;/code&gt; solved it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use &lt;code&gt;node@15.8.0&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Next, I ran &lt;code&gt;vercel dev&lt;/code&gt; and got this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#
# Fatal error in , line 0
# Check failed: allocator-&amp;gt;SetPermissions(reinterpret_cast&amp;lt;void*&amp;gt;(region.begin()), region.size(), PageAllocator::kNoAccess).
#
#
#
#FailureMessage Object: 0x173e61c98
 1: 0x100eaf8a8 node::NodePlatform::GetStackTracePrinter()::$_3::__invoke() [/opt/homebrew/bin/node]
 2: 0x1018317d4 V8_Fatal(char const*, ...) [/opt/homebrew/bin/node]
 3: 0x10141c6bc v8::internal::wasm::WasmCodeAllocator::GetNumCodeSpaces() const [/opt/homebrew/bin/node]
 4: 0x10141c55c v8::internal::wasm::WasmCodeAllocator::FreeCode(v8::internal::Vector&amp;lt;v8::internal::wasm::WasmCode* const&amp;gt;) [/opt/homebrew/bin/node]
 5: 0x10141f3ac v8::internal::wasm::NativeModule::FreeCode(v8::internal::Vector&amp;lt;v8::internal::wasm::WasmCode* const&amp;gt;) [/opt/homebrew/bin/node]
 6: 0x10142cf30 v8::internal::wasm::WasmEngine::FreeDeadCodeLocked(std::__1::unordered_map&amp;lt;v8::internal::wasm::NativeModule*, std::__1::vector&amp;lt;v8::internal::wasm::WasmCode*, std::__1::allocator&amp;lt;v8::internal::wasm::WasmCode*&amp;gt; &amp;gt;, std::__1::hash&amp;lt;v8::internal::wasm::NativeModule*&amp;gt;, std::__1::equal_to&amp;lt;v8::internal::wasm::NativeModule*&amp;gt;, std::__1::allocator&amp;lt;std::__1::pair&amp;lt;v8::internal::wasm::NativeModule* const, std::__1::vector&amp;lt;v8::internal::wasm::WasmCode*, std::__1::allocator&amp;lt;v8::internal::wasm::WasmCode*&amp;gt; &amp;gt; &amp;gt; &amp;gt; &amp;gt; const&amp;amp;) [/opt/homebrew/bin/node]
 7: 0x10142af50 v8::internal::wasm::WasmEngine::PotentiallyFinishCurrentGC() [/opt/homebrew/bin/node]
 8: 0x10142c18c v8::internal::wasm::WasmEngine::ReportLiveCodeForGC(v8::internal::Isolate*, v8::internal::Vector&amp;lt;v8::internal::wasm::WasmCode*&amp;gt;) [/opt/homebrew/bin/node]
 9: 0x10142e8ac v8::internal::wasm::(anonymous namespace)::WasmGCForegroundTask::RunInternal() [/opt/homebrew/bin/node]
10: 0x100eae364 node::PerIsolatePlatformData::RunForegroundTask(std::__1::unique_ptr&amp;lt;v8::Task, std::__1::default_delete&amp;lt;v8::Task&amp;gt; &amp;gt;) [/opt/homebrew/bin/node]
11: 0x100ead458 node::PerIsolatePlatformData::FlushForegroundTasksInternal() [/opt/homebrew/bin/node]
12: 0x101521508 uv__async_io [/opt/homebrew/bin/node]
13: 0x10153107c uv__io_poll [/opt/homebrew/bin/node]
14: 0x101521928 uv_run [/opt/homebrew/bin/node]
15: 0x100dbea88 node::SpinEventLoop(node::Environment*) [/opt/homebrew/bin/node]
16: 0x100edafd4 node::worker::Worker::Run() [/opt/homebrew/bin/node]
17: 0x100edda18 node::worker::Worker::StartThread(v8::FunctionCallbackInfo&amp;lt;v8::Value&amp;gt; const&amp;amp;)::$_3::__invoke(void*) [/opt/homebrew/bin/node]
18: 0x18864606c _pthread_start [/usr/lib/system/libsystem_pthread.dylib]
19: 0x188640da0 thread_start [/usr/lib/system/libsystem_pthread.dylib]
Error! Failed to complete request to /_next/image?url=%2Fimages%2Ffootprint.png&amp;amp;w=48&amp;amp;q=75: Error: socket hang up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, there is something wrong with v8.&lt;br&gt;
Found the issue related to the error:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nodejs/node/issues/37061"&gt;macOS 11.2: Fatal error &lt;code&gt;Check failed: allocator-&amp;gt;SetPermissions&lt;/code&gt; · Issue #37061 · nodejs/node&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was on &lt;code&gt;v15.5.0&lt;/code&gt; and it looks like &lt;code&gt;v15.8.0&lt;/code&gt; solved it.&lt;br&gt;
Installing the latest nodejs solved it.&lt;/p&gt;

&lt;p&gt;Hope that helps!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>node</category>
      <category>m1</category>
    </item>
    <item>
      <title>How I thrived as an introverted developer</title>
      <dc:creator>Takuya Matsuyama</dc:creator>
      <pubDate>Wed, 10 Mar 2021 01:54:54 +0000</pubDate>
      <link>https://forem.com/craftzdog/how-i-thrived-as-an-introverted-developer-273h</link>
      <guid>https://forem.com/craftzdog/how-i-thrived-as-an-introverted-developer-273h</guid>
      <description>&lt;p&gt;Hi, it's &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Takuya&lt;/a&gt; here.&lt;br&gt;
I'm a software developer, an introvert, and happy.&lt;br&gt;
You don't have to become an extrovert person in order to become a successful developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I’m fine with being introverted&lt;/li&gt;
&lt;li&gt;Introverts’ trait is their reflecting thoughts&lt;/li&gt;
&lt;li&gt;How I joined Yahoo! Japan — Showed what I built&lt;/li&gt;
&lt;li&gt;Keep publishing your work to make and retain connections with people&lt;/li&gt;
&lt;li&gt;How to work remotely with few meetings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I'm fine with being introverted
&lt;/h2&gt;

&lt;p&gt;I hate telephones. I have never made a sales call to get a freelance job. I have a fragile temperament. I don't have so many friends. I can't be always friendly to everyone.&lt;br&gt;
And I don't like parties.&lt;br&gt;
Every time, I need to consider for more than 24 hours if I should join a party. I'm extremely nervous when I talk with unfamiliar people. I can hardly make eye contact when talking to people.&lt;/p&gt;

&lt;p&gt;On the other hand, being extroverted looks great.&lt;br&gt;
They look enjoying every day among a lot of their friends.&lt;br&gt;
They can get great opportunities because they can express themselves very well.&lt;br&gt;
So, you might think extroverts are always better than introverts.&lt;/p&gt;

&lt;p&gt;But, no, that's a bad stereotype and the black and whitening of psychology.&lt;br&gt;
I'm not anti-social. I love people. But I just can't behave like extroverts.&lt;br&gt;
What I want to say is that you can grow in a developer career despite being introverted.&lt;br&gt;
Being introverted is a trait rather than a disadvantage.&lt;br&gt;
In this video, I'm gonna talk about how I thrived as an introverted developer.&lt;br&gt;
It's totally fine to be an introvert if you want to become a successful developer.&lt;/p&gt;

&lt;p&gt;In my daily life, I rarely talk with people except for my wife.&lt;br&gt;
And, I am about to forget Japanese vocabularies.&lt;br&gt;
But, as you can see, I'm doing good.&lt;br&gt;
I'm spending a comfortable life, doing what I love, and building apps that I want to use.&lt;br&gt;
In the previous video, I've shared my experience on how I earned $17,000 in a month as a freelancer.&lt;br&gt;
So, it's possible.&lt;br&gt;
And currently, I'm running my own software-as-a-service called &lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;Inkdrop&lt;/a&gt;, which is a simple Markdown note-taking app.&lt;br&gt;
And it makes enough profit, so I don't need to make sales calls or send cold emails to get freelance jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introverts' trait is their reflecting thoughts
&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuby8y00zdcnqnsz47y8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnuby8y00zdcnqnsz47y8.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I was young, I was so sad that I can't make "good friendships".&lt;br&gt;
I tried hard to go along with people but soon I got exhausted.&lt;br&gt;
My energy to hang out with friends is extremely low.&lt;br&gt;
My interests usually didn't match with other people's.&lt;br&gt;
I really really envied those who are active with close friends.&lt;/p&gt;

&lt;p&gt;But I found myself happy when reading books, drawing pictures, shooting photos, playing rock music with guitar and drums, and coding software.&lt;br&gt;
Creating things was so much fun for me.&lt;br&gt;
I could sit at a PC for a long extended time, through the night.&lt;br&gt;
My mom couldn't understand that and asked me like "Why do you need to do such a thing for a long time?"&lt;br&gt;
That's just because it was fun to do.&lt;br&gt;
It was fun to play music, it was fun to draw illustrations, and it was so fun to build things with programming.&lt;br&gt;
I couldn't stop doing them because I didn't get exhausted unlike being with friends.&lt;br&gt;
I think this is the ability of introverts that extroverts don't have.&lt;/p&gt;

&lt;p&gt;Extroverts often find joy in socializing and interacting with the outside world.&lt;br&gt;
They can thrive best when there are action and movement.&lt;br&gt;
But they don't like sitting at a PC for a long time or focusing on a lot of tasks alone.&lt;/p&gt;

&lt;p&gt;Introvert dominant people thrive in spaces where they can reflect, act and then reflect again.&lt;br&gt;
Something like, how do I feel, what is this feeling, what did happen exactly, why doesn't it work, why is it so beautiful, how to improve this, how can I express this idea....&lt;br&gt;
They do those reflectings even while being with their friends.&lt;br&gt;
So, that's why they tend to be soon exhausted.&lt;br&gt;
They need the mental space in order to pause and re-energize themselves.&lt;br&gt;
But people usually don't care that or wait for you.&lt;/p&gt;

&lt;p&gt;But this habit gives you great creativity later, even if it's currently painful for you.&lt;br&gt;
Trust me, you are ok.&lt;/p&gt;

&lt;p&gt;Olympic gold medalist David Hemery reports that almost nine out of ten top athletes identify as introverts:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A remarkably distinguishing feature is that a large proportion, 89 percent of these sports achievers, classed themselves as introverts - David Hemery&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And author Eric Barker says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The superpower of introverts is that they are far more likely to become experts in their field.&lt;/p&gt;

&lt;p&gt;-- Barking Up the Wrong Tree: The Surprising Science Behind Why Everything You Know About Success Is (Mostly) Wrong - Eric Barker&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And Paul Graham says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most important thing is to be able to think what you want, not to say what you want.&lt;/p&gt;

&lt;p&gt;-- Hackers &amp;amp; Painters&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, you don't have to be fluent to express yourself.&lt;br&gt;
To become a great hacker, just think and build what you want.&lt;br&gt;
Inside your head, anything is allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips to thrive
&lt;/h2&gt;

&lt;p&gt;Well, let's talk about the strategy to thrive as an introverted developer.&lt;br&gt;
The key is just to keep building things and publishing them.&lt;br&gt;
You don't need to make a ground-shaking thing from the beginning.&lt;br&gt;
You can start out by making something tiny and simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I joined Yahoo! Japan
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3r7eh572bmlq4loc09w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3r7eh572bmlq4loc09w.jpg" alt="Screen Shot 2021-03-09 at 17.33.40"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I joined Yahoo! Japan about 10 years ago as a new college graduate.&lt;br&gt;
Companies want to know you are competent to solve real-world problems by coding.&lt;br&gt;
And showing what you built is the best way to prove that through a job interview.&lt;br&gt;
Because many people don't have such the public works and they just tell what they learned in the college instead of what they built.&lt;br&gt;
So, showing your works would be impressive for the interviewers.&lt;/p&gt;

&lt;p&gt;I explained what I built during the interview. I talked about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem I solved&lt;/li&gt;
&lt;li&gt;What was the challenge&lt;/li&gt;
&lt;li&gt;How I tackled it&lt;/li&gt;
&lt;li&gt;What I learned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;from my projects.&lt;br&gt;
For example, I made &lt;a href="https://www.craftz.dog/works/pichu2" rel="noopener noreferrer"&gt;a Twitter client for iPhone Safari in 2010&lt;/a&gt;.&lt;br&gt;
I made it because there wasn't a solid browser client for Twitter yet on the internet.&lt;br&gt;
It was challenging for me to build an AJAX-based web app.&lt;br&gt;
Since HTML5 APIs or ReactJS isn't available yet at that time, manipulating DOMs based on the server response with jQuery was a cool way to build a product.&lt;br&gt;
I used LAMP stack, which is a server-side stack with Linux, Apache, MySQL, and PHP.&lt;br&gt;
And it successfully got traction and attracted 1,000 people in Japan.&lt;br&gt;
I've learned how to build a web app for small touch screens.&lt;br&gt;
With this work, I proved my competence to build an attractive web app from scratch.&lt;/p&gt;

&lt;p&gt;So, just following some tutorials is not enough.&lt;br&gt;
You should build your own things yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep publishing your work
&lt;/h3&gt;

&lt;p&gt;When I'm freelancing, I kept publishing my personal works on the internet.&lt;br&gt;
It's important to appeal to people that you make interesting things.&lt;br&gt;
Because your friends are actually checking them.&lt;/p&gt;

&lt;p&gt;I've got all the past freelance jobs through referrals via my friends.&lt;br&gt;
I said my friends, but they are not my close friends.&lt;br&gt;
Actually, I rarely contacted them, but we were aware of each other indirectly on social media.&lt;br&gt;
Then, they remember you, and once they've got a job to ask you, they will send a message to you. Or, they will introduce another person who is interested in working with you.&lt;/p&gt;

&lt;p&gt;So, no matter how small your achievement is, publish it!&lt;/p&gt;

&lt;p&gt;You don't have to make a lot of connections like thousands.&lt;br&gt;
I have only 300 friends on facebook.&lt;br&gt;
You should connect with people who tell you they love your works.&lt;br&gt;
I very occasionally join a meetup and make some friends.&lt;br&gt;
Joining a hackathon would be a great way to make friends as well.&lt;br&gt;
And also, giving a talk at a conference attracts some people.&lt;/p&gt;

&lt;p&gt;Then, your friend will introduce someone who is looking for a software developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jow to work remotely with few meetings
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22oyqq42l3xgyy06abqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22oyqq42l3xgyy06abqe.png" alt="undraw_Remote_team_re_ck1y"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you may know, remote work is a great work-style for introverts.&lt;br&gt;
You don't have to meet people every day, which is great.&lt;br&gt;
I have some tips for doing remote work.&lt;/p&gt;

&lt;p&gt;Most of my clients were startups.&lt;br&gt;
So, my job was basically to build a service from scratch.&lt;br&gt;
For example, building a new mobile app, including UI design and server-side program.&lt;br&gt;
Or, building a web service using Ruby on Rails with Slim and AngularJS.&lt;/p&gt;

&lt;p&gt;As I talked in the previous video, I only had contract jobs with project-based fees instead of hourly fees.&lt;br&gt;
First, I hold a meeting and hear what the client wants to make.&lt;br&gt;
After that, I have meetings with the client only once or twice a month.&lt;br&gt;
I use a voice chat or a video chat if I thought it doesn't need to talk face-to-face with them.&lt;/p&gt;

&lt;p&gt;The point is that I frequently reported my work progress in order not to let the client be worried about me abandoning the work.&lt;br&gt;
When I'm making a prototype, I often send the screenshots via Slack or Email.&lt;br&gt;
Then, I deploy it somewhere so that the client can try it anytime.&lt;br&gt;
If it were a mobile app, I use TestFlight to show the progress.&lt;br&gt;
Once they trusted me, those frequent reports are no longer necessary.&lt;/p&gt;

&lt;p&gt;Writing documentation significantly helps you reduce the communication cost because it answers questions on your behalf.&lt;br&gt;
So, I write documentation in as much detail as possible so it'll be still ok even if I quit the project.&lt;br&gt;
The amount of the documentation would be like 20~30 pages of Keynote.&lt;br&gt;
And I send it to the client a week before the meeting and let them read it.&lt;br&gt;
So, I only need to clarify some points in the meeting and I can avoid explain them over and over again.&lt;/p&gt;




&lt;p&gt;So, that's how I thrived as an introverted developer.&lt;br&gt;
Hope that's helpful!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow me on &lt;a href="https://twitter.com/inkdrop_app" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://instagram.com/craftzdog" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/S7hDmvh" rel="noopener noreferrer"&gt;Solo developer's Discord community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.inkdrop.app/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjak3hkzf8kwzeg6e48fp.jpg" alt="Inkdrop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://eepurl.com/dNgJo6" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2Am_OQH1uPXG_JcuW9BTe82A.png" alt="Subscribe Newsletter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/c/devaslife" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fproxy%2F1%2Ar-daItizHpAU-fQZk3ISag.png" alt="My YouTube channel"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>lifelessons</category>
      <category>career</category>
      <category>freelancing</category>
      <category>psychology</category>
    </item>
  </channel>
</rss>
