For my test projects I’ve been using create-react-app, which is very nice to kick start a React project in Webpack build flow. Unfortunately making it work with TypeScript is somewhat a pain (doable though) as for example new versions of TS loaders will not work with Webpack 3 anymore which is used by the current version of create-react-app. Instead of using old libraries, let’s see what it takes to bootstrap a React web project/build workflow including:

  • build workflow setup using Webpack 4
  • TypeScript support
  • TS linting for code style checking
  • CSS module support to avoid clashing with other components
  • support unit testing with Jest and Enzyme

These are mostly fairly well documented things, you will find more details for each tools on their respective website. Here I summarize the steps and create a working configuration. At the end of the blog post you will also find a link to the complete repository.

Initialize the project

During this tutorial I will use yarn to manage npm packages. If these terms are not yet familiar to you, it will be probably useful learning about them. I’m not describing here how to install node, npm or yarn, I expect you to have a working node environment already. I’m using Linux command line in my examples, for other OSs you might need to transcribe my commands based on the description.

When asked to enter project details when initializing the package feel free to use your best judgement. You can change all details later by editing the package.json file generated by the yarn init.

Now that we have a place to live in, install the packages we will need to get started:

Configure TypeScript

Create a tsconfig.json file with the following content:

Configure Webpack

Webpack 4 doesn’t require a configuration, so we can jump to the next step… Just kidding. I assume you are somewhat familiar with Webpack, as I don’t have enough storage to describe the concepts here. I will use a basic configuration file and only describe what needs to be added to achieve a functionality which is in scope of this post.

I took the configuration example from the TypeScript React&Webpack tutorial, it already includes the TS loader (awesome-typescript-loader) which we installed before and added some configuration, for example to use an HTML template and some configuration for the Webpack DevServer, but mostly using defaults. Note, that for example just changing the mode in the configuration from development to production it will create different results, will automatically include different default plugins. I’m creating both a development and a production configuration, the differences will be subtle though. In production the code is minified/uglified and will not generate source maps. The files I named webpack.dev.conf.js and webpack.prod.conf.js.

Webpack development configuration

Webpack production configuration

Build commands

Let’s add a scripts section to the package.json file to either build code or start the development server. This should do it for now:

Create a test application

Of course feel free to create your own test application, we just need something that we can build and test later. For this purpose I created the following files in the src folder:

index.html – the HTML template

index.tsx – Entry point of the application

App.tsx – The React application to inject

components/NameCard/NameCard.tsx – A component to demonstrate types

For now we have no loaders to support CSS or images. If you followed the instructions though, this should already create a working application which will list the two names with their age, so it’s a good start. Try to run the development server by calling:

TypeScript linting

I know I wrote semantically correct code, I have seen it running with my own eyes. But maybe I forgot a semi-colon somewhere or didn’t match the corporate style guide. Let’s check it now. First add a few more development dependencies.

Configure linting in tslint.json

Configure Webpack to run tslint

To run tslint each time when the code is compiled, I’ve added the following configuration before the typescript loader to the Webpack configuration files:

When the code is compiled (either because you build it or the DevServer is watching it and the code changed, etc), you will see the tslint output on the screen. It may run out of screen though, so scroll up. The linter errors will also appear on the Development Tools console among other errors and warnings when running from the DevServer. You can also just run the linter from the command line or add it to the scripts array in package.json as I did, so you can just run yarn tslint in the future:

If you followed exactly the steps above, the linter should print one error:
src/components/NameCard.tsx:3:18
ERROR: 3:18 interface-name interface name must start with a capitalized I
According to the style guide which we use, interfaces must start with I. If you don’t agree with this rule, you can turn it off. Otherwise, you can rename the NameCardProps interface to INameCardProps

CSS module support

So far we have no CSS support at all, it was intentional as without automatically generated typing for TypeScript is rather painful. The loader module typings-for-css-modules-loader  can be used in place of the usual css-loader and it will generate the required *.css.d.ts files on the fly. Also adding the postcss-loader for auto-prefixing and style-loader to add styles to the DOM, all with the configuration which I borrowed from create-react-app. First install the required packages:

Configure Webpack for CSS modules with TS support

As summarized above, I have added the following to the development configuration of Webpack stored in webpack.dev.conf.js. This is all just the same configuration which I copied over as mentioned before, except for the namedExport setting which is specific to the typing module. It exports the valid classnames from the CSS, instead of adding an interface, but drops the invalid object names, like the ones with dashes. You may also want to add the css-loader option camelCase:true  to automatically convert those, read more about it in the module’s documentation.

Note, add autoprefixer to both the development and production configurations:

webpack.dev.conf.js:

The production configuration requires a bit more attention, will use the mini-css-extract-plugin to extract the CSS into separate files. The rest of the loaders will be the same, except that we should minify the output. Make these changes to the webpack.prod.conf.js file.

Load the CSS extract plugin:

Add the plugin configuration to the plugins array:

And finally configure the loader modules:

Minify the CSS in production

As the minimize option was removed from the css-loader we need to use a plugin to do this task. Following the recommendation of the mini-css-extract-plugin documentation, I’m using the optimize-css-assets-webpack-plugin. Note, that originally we had no optimization section in the configuration file and the mode: production setting automatically included the JS uglifier. Now this default is overridden when we add the new CSS minifier plugin, therefore we also explicitly need to specify the JS uglify plugin.

Require the modules to optimize JS and CSS:

Add a new optimization section:

Check if CSS modules are loaded

Create a couple of CSS files, one for the src/App.tsx and one for the src/components/NameCard.tsx.

App.css:

Feel free to add whatever styles you want to the NameCard.css file.

Then import the classes from these files into the respective TS files and use the imported classes.

Important: the import statement itself will not actually import the CSS file (module) until it is referenced and while it is not actually loaded the wonder by the typings-for-css-modules-loader doesn’t happen. Therefore the type definition files are not generated, so the import statement by itself will throw a “Cannot resolve module” error. Only import the CSS file if you also reference the imported module.

Modify the App.tsx file:

Similarly, import the NameCard.css classes into the NameCard.tsx file and use the classes like {classes.NameCard}, etc. If all went well, when the application is compiled you will see the .css.d.ts files and the styles which you applied to the elements.

Unit testing

For unit testing I will add Jest and Enzyme. Fortunately as always there is a module to make the life easier, add and configure ts-jest which will preprocess TypeScript and of course add Jest and Enzyme:

And add Jest configuration to the package.json as documented in the ts-jest instructions:

Transform CSS files

As you may have noted above in the configuration, we want to transform the CSS modules for Jest. For this I created the jest/cssTransform.js file in the project folder and borrowed it from the create-react-app package. As it also says in its comment, it replaces the CSS imports with empty objects.

Create a unit test

Let’s see if this setup works and create a unit test for the NameCard React component. I created a NameCard.test.tsx next to the NameCard.tsx file.

Not a fully fledged test, but hopefully enough to get the idea. Now you can run the test by calling yarn jest from the project folder. It should print something like:

Are we ready?

Not really, but I think we are on the right track. The Webpack configuration is missing some modules to add URL/file loaders to magically import images and other files. Note, that those will need to be also transformed for Jest, similar to the CSS we did above. Additionally instead of generating ES5 from TypeScript we could use Babel with all its niceties to transpile the code and add polyfills, but this post is already too long.

Get the source code

To use the code which I created while writing this post, you can clone my Git repository from https://github.com/seabadger-io/react-ts-ks.git:

 




Tagged with