{"id":812,"date":"2022-01-31T21:24:51","date_gmt":"2022-01-31T20:24:51","guid":{"rendered":"https:\/\/nubisoft.io\/blog\/?p=812"},"modified":"2024-01-08T10:13:21","modified_gmt":"2024-01-08T09:13:21","slug":"using-modern-tooling-for-faster-frontend-development","status":"publish","type":"post","link":"https:\/\/nubisoft.io\/blog\/using-modern-tooling-for-faster-frontend-development\/","title":{"rendered":"Using modern tooling for faster frontend development"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Why bother?<\/h2>\n\n\n\n<p>Life is too short to waste it on tedious tasks such as installing dependencies or waiting for the Webpack to finish bundling your whole app even though you&#8217;ve made a small label change.<\/p>\n\n\n\n<p>That&#8217;s when modern frontend technologies come in. I&#8217;ll be focusing on the migration of an typical old setup (~2019) with <strong>npm<\/strong>, <strong>Webpack <\/strong>and <strong>Create React App<\/strong> to something much more modern (and quicker!) utilizing <a href=\"https:\/\/pnpm.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">pnpm <\/a>and <a href=\"https:\/\/vitejs.dev\/\" data-type=\"URL\" data-id=\"https:\/\/vitejs.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">Vite<\/a>.  <\/p>\n\n\n\n<p>Let&#8217;s quickly introduce our main heros of this article.<\/p>\n\n\n\n<p><strong>Vite <\/strong>is a no-bundle ESM dev server that works a bit differently &#8211; it doesn&#8217;t bundle anything unlike e.g. <strong>Webpack<\/strong>. Because of this <strong>Vite<\/strong>&#8216;s sever starts almost instantly and offers pretty much instant page hot reloading.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"641\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/esm.3070012d-1024x641.png\" alt=\"\" class=\"wp-image-818\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/esm.3070012d-1024x641.png 1024w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/esm.3070012d-300x188.png 300w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/esm.3070012d-768x481.png 768w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/esm.3070012d-1536x961.png 1536w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/esm.3070012d-1200x751.png 1200w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/esm.3070012d.png 1646w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><a href=\"https:\/\/vitejs.dev\/guide\/why.html#slow-server-start\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/vitejs.dev\/guide\/why.html#slow-server-start<\/a><\/figcaption><\/figure>\n<\/div>\n\n\n<p><strong>Pnpm <\/strong>is an alternative to npm and yarn. Usually our project use simillar packages so why install them separately each time? <strong>Pnpm <\/strong>takes advantage of that and keeps only one copy of a package. It allows us to install dependencies much faster.<\/p>\n\n\n\n<p>Obviously there&#8217;s much more depth in both of those technologies, but that&#8217;s not our focus here. So now it&#8217;s time for some refactoring, let&#8217;s get to it!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">It was never meant to be simple<\/h2>\n\n\n\n<p>Making a big change to the project like switching out tooling will always break something but our goal is to make the changes bare minimum.<\/p>\n\n\n\n<p>Because we&#8217;ll no longer be using <strong>Create React App<\/strong> we need to eject by running <strong>eject <\/strong>script. It will stop hiding what it&#8217;s got installed under the hood and instead eject those things into your project&#8217;s <strong>package.json<\/strong> for everyone to see.<\/p>\n\n\n\n<p>But first let&#8217;s do what&#8217;s obvious, we need to remove <strong>node_modules <\/strong>and <strong>package-lock.json<\/strong>, because <strong>pnpm <\/strong>creates it&#8217;s own lock file and we cannot reuse previous dependencies.<\/p>\n\n\n\n<p>Then our next best bet is to add two plugins with <strong>pnpm <\/strong>(or in <strong>package.json<\/strong>) by running below command.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">pnpm i vite @vitejs\/plugin-react<\/pre>\n\n\n\n<p>We also need to update scripts in our package.json with following commands:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>start &#8211; starts the dev sever<\/li>\n\n\n\n<li>preview &#8211; starts server and serves files from our build directory<\/li>\n\n\n\n<li>build &#8211; it bundles and minifies our project<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\"start\": \"vite\",  \n\"preview\": \"vite preview\", \n\"build\": \"tsc &amp;&amp; vite build\"<\/pre>\n\n\n\n<p>Now we need to do small changes to our code structure &#8211; we need to keep index.html at the root of our project, not in the public directory.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"301\" height=\"661\" src=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/image.png\" alt=\"\" class=\"wp-image-824\" srcset=\"https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/image.png 301w, https:\/\/nubisoft.io\/blog\/wp-content\/uploads\/2022\/01\/image-137x300.png 137w\" sizes=\"auto, (max-width: 301px) 100vw, 301px\" \/><\/figure>\n<\/div>\n\n\n<p>Last thing that we need to take care of is vite.config.ts which is home to all settings regarding <strong>Vite<\/strong>. Bare minimum for a <strong>React <\/strong>application is to add <strong>reactRefresh <\/strong>plugin, but of course we can configure a plethora of elements here such as proxies, dev server settings and path resolving (which will be important later).<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">export default defineConfig({\n  plugins: [\n    reactRefresh(),\n  ],\n});<\/pre>\n\n\n\n<p>So it&#8217;s not that difficult, right? Only <code>pnpm start<\/code> and we can get back to development, right? Not really let&#8217;s list everything that is wrong with our project now:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>jsx in .js files<\/li>\n\n\n\n<li>folders from <strong>Create React App<\/strong> eject<\/li>\n\n\n\n<li>svgs doesn&#8217;t work<\/li>\n\n\n\n<li>every import path is broken<\/li>\n<\/ul>\n\n\n\n<p>So basically our server won&#8217;t start unless we fix those issues.<\/p>\n\n\n\n<p>But let&#8217;s tackle the simplest issues for now. First of all let&#8217;s remove <strong>scripts <\/strong>and <strong>config <\/strong>folders, we won&#8217;t need them anyway. Next, we can&#8217;t have jsx in <strong>.js<\/strong> or <strong>.ts<\/strong> file end of story. <strong>Vite <\/strong>won&#8217;t allow it so we have no choice but to change them. Depending on your case it may be a few files or every single file in your codebase.<\/p>\n\n\n\n<p>Because we aren&#8217;t using <strong>Webpack <\/strong>eco system anymore we need to use different loader for svgs. I&#8217;ll be using <a rel=\"noreferrer noopener\" href=\"https:\/\/www.npmjs.com\/package\/vite-plugin-svgr\" data-type=\"URL\" data-id=\"https:\/\/www.npmjs.com\/package\/vite-plugin-svgr\" target=\"_blank\">vite-plugin-svgr<\/a>. It&#8217;s <strong>npm <\/strong>page provides all necessary info on how to configure it for your project, so I&#8217;ll skip that part.<\/p>\n\n\n\n<p>However biggest problem that we have are our import paths. <strong>Webpack <\/strong>was fine with paths like <code>import Component from 'components\/Component'<\/code>, but <strong>Vite <\/strong>is not. Unfortunately path resolver in <strong>Vite <\/strong>will not accept non-prefixed paths so that means we need to update those paths. First we need to add a resolver to <strong>vite.config.ts<\/strong>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  resolve: {\n    alias: {\n      '@': path.resolve(__dirname, 'src'),\n    },\n  },<\/pre>\n\n\n\n<p>And then our path should change to  <code>import Component from '@\/components\/Component'<\/code>. I&#8217;ve spend quite a lot of time on this problem, but we won&#8217;t get away with that change.<\/p>\n\n\n\n<p>There also might be different other things broken depending on what your project actually uses so it&#8217;s not a full guide to every issue.<\/p>\n\n\n\n<p>But what about pnpm you might ask and that&#8217;s correct, we need to change one thing in our <strong>CI <\/strong>config (in our case <strong>Gitlab<\/strong>) to make package cache work. Basically we need to update 2 entries. We need to add a path <strong>.pnpm-store<\/strong> to cache and install <strong>pnpm<\/strong> globally on our image because that&#8217;s not something that is provided (read more about <a href=\"https:\/\/nubisoft.io\/blog\/robust-ci-cd-pipelines-for-node-based-projects\/\" data-type=\"post\" data-id=\"289\">robust CI\/CD pipelines for Node projects<\/a>).<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">cache:\n  key: node_modules\n  paths:\n    - .pnpm-store\n\nbuild UI:\n  stage: build\n  image: node:14.16.0-buster\n  before_script:\n    - curl -f https:\/\/get.pnpm.io\/v6.16.js | node - add --global pnpm@6\n    - pnpm config set store-dir .pnpm-store\n  script:\n    - pnpm install\n    - pnpm lint\n    - pnpm build\n  artifacts:\n    expire_in: 1d\n    paths:\n      - .\/build<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Was it worth it?<\/h2>\n\n\n\n<p>In short &#8211; yes, very much so. Our <strong>CI <\/strong>pipeline got reduced by about 2 minutes and because of the hot reload feature of <strong>Vite <\/strong>development got much smoother as a result. There were a few hiccups along the way but it wasn&#8217;t anything that couldn&#8217;t be handled with few Google searches \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Why bother? Life is too short to waste it on tedious tasks such as installing dependencies or waiting for the Webpack to finish bundling your whole app even though you&#8217;ve made a small label change. That&#8217;s when modern frontend technologies come in. I&#8217;ll be focusing on the migration of an typical old setup (~2019) with [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":837,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_case_study_excerpt":"","footnotes":""},"categories":[68,3],"tags":[47,69,412,414,416,418],"class_list":["post-812","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-javascript","category-software-development","tag-ci","tag-javascript","tag-pnpm-en","tag-react-en","tag-vite-en","tag-webpack-en"],"_links":{"self":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts\/812","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/comments?post=812"}],"version-history":[{"count":21,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts\/812\/revisions"}],"predecessor-version":[{"id":1266,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/posts\/812\/revisions\/1266"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/media\/837"}],"wp:attachment":[{"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/media?parent=812"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/categories?post=812"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nubisoft.io\/blog\/wp-json\/wp\/v2\/tags?post=812"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}