Create a Build pipeline for React app

Kristaps Vītoliņš
9 min readDec 30, 2018

It is mid-summer of 18th of July 2016, Christopher Chedeau made first commit into GitHub repository create-react-app. At the same time, Dan Abramov makes one more commit with the commit message “Explain what it is”. Commit contains README and Goal in it.

Make it easy to get started with React.

And that overall explains the situation with React a few years ago. Cool and shiny but to start hacking — it will take some time.

What is command create-react-app?

Create React App doesn’t handle backend logic or databases; it just creates a frontend build pipeline, so you can use it with any backend you want. Under the hood, it uses Babel and webpack, but you don’t need to know anything about them.

And here we are, almost three years later, where React is one of the most popular frontend JavaScript libraries. Can we blame partly command create-react-app for that? Most certainly yes, it takes one command to bootstrap React app. Versus spending the time to build a pipeline of your own, just to test your idea.

As cool it is, it hides a lot. What does it hide?

Ladies and Gentlemen, I bring to you my own experience of building your own build pipeline.

# The beginning

Create an empty directory, have you noticed that every new amazing project starts with an empty directory?

mkdir react-tutorial; cd react-tutorial

Initialize the npm project

npm init -y

React

npm i -S react react-dom

Webpack

npm i -D webpack webpack-cli

Create folder src which will contain app source

mkdir src

Add ./src/index.html

And ./src/index.js

Now the question is. How to combine these two things together? Usually, in the build process.

Add build command to thepackage.json

Navigate to project root directory and run the command build

npm run build

WebPack will create a directory distand put all compiled result inside. Look insidedist and you will find minified main.js but no index.html. The desired result would be: main.js and index.html insidedist directory and index.html should contain a reference to main.js.

For that, we have to create webpack.config.js in the project root and give instruction to WebPack to do so. Before that add package html-webpack-plugin

npm i -D html-webpack-plugin

This will allow manipulating with index.html. Now create webpack.config.js

Run build once more

npm run build

Dist directory should contain index.html and main.js. Open index.html with Google Chrome and press F12

Google Chrome console

It works!

Of course, this isn’t React app. This is plain javascript with console output. But take a few seconds and think about it. What possibilities are there for you as a software engineer to manipulate with HTML files in build time. Basically, html-webpack-plugin is a lightweight templating engine.

# The Babel and Typescript

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

npm i -D @babel/cli @babel/core @babel/preset-env babel-loader @babel/plugin-proposal-class-properties @babel/plugin-transform-arrow-functions

TypeScript and Babel TypeScript preset

npm i -D typescript @babel/preset-typescript

Create a Babel configuration .babelrc in the project root directory

Rename index.js to index.ts

Modify webpack.config.jsrename index.js to index.ts. Give WebPack instructions to use babel-loader when encountered files with extension ts or tsx.

Let us adjust index.ts file, just to test. Does our TypeScript Babel configuration work

Now run the build command again

npm run build

And if you open index.html in the browser, the console will print “Hello Mom!” Isn’t it marvelous!? This proves that our build pipeline works.

# The React app

It time to add some reaction to our app! Let us create a Dashboard React component.

For that, we will need to teach Babel to understand reactish :)

npm i -D @babel/preset-react

Add preset to .babelrc so that Babel can talk reactish

Create tsconfig.jsonin the project root, it is a configuration file for TypeScript.

Changes in webpack.config.js. For debugging purposed add source maps. As other npm packages import javascript files add to resolve extension “.js”.

Before we start hacking, add React typings. It will enhance your experience with modern IDEs, hints, strict type checks, etc.

npm i -D @types/react @types/react-dom

Now we are ready to build out component. Create a new directory in the source directory.

mkdir src/components

Create component Dashboard.tsx

Replace the content of index.tsx

And run once again build command

npm run build

Open index.html with browser and

Hello Mom from React

# The WebPack dev server

As you noticed, manually checking the changes in index.html is annoying. To ease our pain, let us install WebPack dev server.

Use webpack with a development server that provides live reloading. This should be used for development only.

npm i -D webpack-dev-server

Add start script entry in the package.json to start WebPack dev server

Add dev server configuration in webpack.config.js

Start will start the web server and open default browser. Now whenever you make changes in your code, the page will be refreshed automatically. Cool, isn’t it? Try it out yourself!

npm run start

# The Polyfill

Not all browsers understand the new and fancy javascript constructs. For that, Babel can be used to fill the gaps with polyfills. For example, Array.prototype.map().

Install babels polyfill package

npm i -D @babel/polyfill

Configure Babel in .babelrc what gaps to fill. This is my preference.

# The Unit tests

Testing your code is crucial. Let us write a unit test for the Dashboard component.

Install all the required packages

npm i -D jest @types/jest @types/enzyme @types/enzyme-adapter-react-16 enzyme enzyme-adapter-react-16 enzyme-to-json ts-jest

Create jest.config.js in the project root directory

This gives instructions to jest to look for test files in directory __test__ and file should end with .test.jsx or test.tsx.

Additionally, we need Enzyme to be friends with Jest.

Enzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components’ output.

Create setupTests.ts file in the root directory

Add new script in the package.json named “test”

Make changes in the Dashboard.tsx, so that we can actually test it. Add an id to div, we will use the id to query by it.

Create a directory for test cases

mkdir src/components/__tests__

Create Dashboard.test.tsx and place it inside __test__ directory

Execute the test script

npm run test

Perfect! 100% code coverage a lot of development teams can envy us now.

# The Styling

So far we dealt only with JavaScrip. But we shouldn't forget about styling. For styling, I prefer to use Sass.

Sass is the most mature, stable, and powerful professional grade CSS extension language in the world.

Install packages for styling

npm i -D sass-loader node-sass mini-css-extract-plugin css-loader style-loader

Modify webpack.config.js

This is a lot to process. But what I’m doing here is:

  • Importing new plugin mini-css-extrat-plugin. This allows Web Pack to extract all CSS in one file.
  • Determinate the mode, development or production
  • More rules, what to do when encountered files with extensions sass, scss and css. Loaders will be used in order sass-loader, css-loader and then it depends on mode either style-loader or extract plugin.
  • Add an extension to resolve “.scss”, I would add “.css” if needed.
  • And register new plugin for WebPack to use

Cool, we are ready to make out “Hello Mom!” message bigger and stronger again!

Create Dashboard.scssfile right beside Dashboard.tsx

Import Dashboard.scssinto Dashboard.tsxand reference style class on a div element.

npm run start

Now that is glorious I say!

But wait — what is this when I run

npm run test

It explodes in my face… :(

That's OK. Jest doesn’t understand styling imports and honestly, unit testing should care about styling. So let's mock them out.

Adjust jest.config.js

This basically says, when you see files with extensions like xyz use mock files.

Create a directory __mocks__ in the project root

mkdir __mocks__

Create fileMock.js

Create styleMock.js

Run tests again

npm run test

And it works again!

#The PostCSS

It turns out that not only JavaScript requires polyfills but CSS as well. To fill these gaps and enhance the output CSS with browser prefixes, PostCSS is the tool we will use.

Let us install required packages

npm i -D postcss-loader postcss-flexbugs-fixes postcss-preset-env postcss-safe-parser

Adjust webconfig.config.js by adding postcss-loader

For testing purposes, let's put inside Dashboard.scssplaceholder definition.

::placeholder {
color: blue;
}

And run the build script

npm run build

Now look inside generated a CSS file

Now, now, now! There is more than we placed in SCSS file. PostCSS loader is here to blame. It will make sure that placeholder will look the same all across the different browsers.

# The Linting and Formatting

We all write perfect code, but just sometimes we forget that semicolumn or comma. And then at the build time, code explodes or even worse production. Linting your code before committing it to the repository is a smart move. We will you TSLint for that.

TSLint is an extensible static analysis tool that checks TypeScript code for readability, maintainability, and functionality errors. It is widely supported across modern editors & build systems and can be customized with your own lint rules, configurations, and formatters.

Counting the spaces and tabs, just to make your code prettier is mind-numbing. We can use a tool for that, called prettier.

Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.

Install tslint and prettier packages

npm i -D prettier tslint tslint-config-prettier

Create TSLint configuration tslint.json and place it in the project root

Create a prettier configuration .prettierrc and place it in the project root

Add two new scripts in package.json

Run both commands

npm run format && npm run lint

Your code is ready for committing to the repository a good job!

#The E2E testing

As I understand E2E means. Does the flow happen when I press the button? Because one button click may trigger hundreds of component interactions. That kind of test should be done by a unit test.

e2e or end-to-end or UI testing is a methodology used to test whether the flow of an application is performing as designed from start to finish. In simple words, it is testing of your application from the user endpoint where the whole system is a blackbox with only the UI exposed to the user.

Now how we can put this in a pipeline of ours? Puppeteer will be a tool of ours!

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

Add packages

npm i -D puppeteer @types/puppeteer

Create Dashboard.e2e.test.ts right beside Dashboard.test.tsx

Now IDE will light up red on async keyword, to fix it, let’s modify tsconfig.json. Add lib configuration.

Run test script

npm run test

Congratulation! Your first end to end test is working! NB: Don’t forget to npm run startbefore running the test.

# The End

End result of package.json

Worth notice that build command executes linting, formation, and testing and only when that is done, WebPack builds the project.

And the end result of WebPack configuration webpack.config.js

This is the time when to remind our self what was the Goal of create-react-app

Make it easy to get started with React.

There is a lot of things which are hidden behind one command. And there is a good reason to do so. Imagen every time to do all those things we just did, just to get started. Madness — I know.

I personally encourage to use create-react-app. But I’m pretty sure that if the project is big or complex, you will end up with a pipeline of your own. Which most likely will look like one I created. To have full control over what happens when and why.

Overall by having this experience. I did see this amazing synergy between Titans, like Facebook (React), Puppeteer (Google), Enzyme (Airbnb), TypeScript (Microsoft), Webpack (OpenSource) and other technologies and tools.

Ladies and Gentlemen this was my experience of hacking a build pipeline for React app.

GitHub repo https://github.com/alzuma/react-tutorial

Next up. “Why web components matter”

Kristaps

--

--

Kristaps Vītoliņš

I’m a Software engineer, dad, and husband. Innovation as a service.