React + Webpack + TypeScript Project Setup

  • 2019-04-09 01:22 AM
  • 271

React + Webpack + TypeScript Project Setup: Let's get React and TypeScript setup, its a lot easier than you think!

TL;DR: Can’t possibly summarize this in a single sentence but there is a repo that you can fork which has all of the boilerplate setup.

Let’s Get Started!!

Firstly you will need to create a directory/repo for your project, once you have done this you will need to initialize the project. Run one of the following snippets in your terminal to do so:

//If you want to use npm 
npm init
//If you want to use yarn 🧶
yarn init

Just follow along with the prompts in the terminal, once the initialization is complete you should see a package.json file in the directory. If you don’t know what a package.json file is then this post is not for you, I would recommend that you play with React and npm more before continuing.

Add Some Dependencies 😵

The 3 dependencies that you’ll need are react, react-dom, and typescript which are all pretty straightforward, but you’ll also need 24 devDependencies.

To install the 3 dependencies run the following

//Optionally you can install styled-components
//npm
npm install react react-dom typescript
//yarn 🧶
yarn add react react-dom typescript

For this post, we’ll be using babel so you’ll need to install babel-loader, a couple of babel plugins, and some babel presets.

Something to note is that as of babel v7, babel ships with Typescript support so we don’t need a separate TypeScript loader 😃.

//npm
npm install babel-loader @babel/plugin-external-helpers @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread @babel/preset-env @babel/preset-react @babel/preset-typescript --save-dev
//yarn 🧶
yarn add babel-loader @babel/plugin-external-helpers @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread @babel/preset-env @babel/preset-react @babel/preset-typescript -D

Ok on to installing the testing specific devDependencies. We’ll be using Jest and Enzyme along with some enzyme specific dependencies.

//npm
npm install enzyme enzyme-adapter-react-16 enzyme-to-json jest ts-jest --save-dev
//yarn 🧶
yarn add enzyme enzyme-adapter-react-16 enzyme-to-json jest ts-jest -D

Ok just a couple more dependencies, obviously we need Webpack, Wepback plugins, and the types for the various dependencies

//npm
npm install @types/enzyme @types/enzyme-adapter-react-16 @types/jest @types/react @types/react-dom html-webpack-plugin source-map-loader webpack webpack-cli webpack-dev-server webpack-hot-middleware --save-dev
//yarn 🧶
yarn add @types/enzyme @types/enzyme-adapter-react-16 @types/jest @types/react @types/react-dom html-webpack-plugin source-map-loader webpack webpack-cli webpack-dev-server webpack-hot-middleware -D

Oof ok we’re done with the dependencies finally… 🎊

Webpack Config 🛠

You should be somewhat familiar with Webpack configs but all we’re doing is checking the mode and adjusting the config based on the environment mode (production or development). Since I’m using styled-components the only loader I need is babel-loader with the various plugins and presets, but if you have CSS in your project you’ll need a CSS loader.

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = (env, { mode = 'development' }) => {
  const config = {
    mode,
    entry: {
      app: './src/index.tsx',
    },
    devtool: '',
    resolve: {
      extensions: ['.js', '.jsx', '.ts', '.tsx'],
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx|tsx|ts)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env',
                '@babel/preset-react',
                '@babel/preset-typescript',
              ],
              plugins: [
                '@babel/plugin-external-helpers',
                'babel-plugin-styled-components',
                '@babel/plugin-proposal-class-properties',
                '@babel/plugin-proposal-object-rest-spread',
              ],
            },
          },
        },
      ],
    },
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'index.js',
      libraryTarget: 'umd',
      publicPath: '/dist/',
      umdNamedDefine: true,
    },
    optimization: {
      mangleWasmImports: true,
      mergeDuplicateChunks: true,
      minimize: true,
      nodeEnv: 'production',
    },
    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': '"production"',
      }),
    ],
  };

  /**
   * If in development mode adjust the config accordingly
   */
  if (mode === 'development') {
    config.devtool = 'source-map';
    config.output = {
      filename: '[name]/index.js',
    };
    config.module.rules.push({
      loader: 'source-map-loader',
      test: /\.js$/,
      exclude: /node_modules/,
      enforce: 'pre',
    });
    config.plugins = [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': '"development"',
      }),
      new HtmlWebpackPlugin({
        filename: path.resolve(__dirname, 'dist/index.html'),
        template: path.resolve(__dirname, 'src', 'index.html'),
      }),
      new webpack.HotModuleReplacementPlugin(),
    ];
    config.devServer = {
      contentBase: path.resolve(__dirname, 'dist'),
      publicPath: '/',
      stats: {
        colors: true,
        hash: false,
        version: false,
        timings: true,
        assets: true,
        chunks: false,
        modules: false,
        reasons: false,
        children: false,
        source: false,
        errors: true,
        errorDetails: true,
        warnings: false,
        publicPath: false,
      },
    };
    config.optimization = {
      mangleWasmImports: true,
      mergeDuplicateChunks: true,
      minimize: false,
      nodeEnv: 'development',
    };
  }
  return config;
};

TypeScript Config 👨‍💻

Everything in the TypeScript configuration is opinionated, what I mean by that is that you can edit it to your heart’s content and it shouldn’t break your application, what I have in the repo is just what I like as some defaults. Just make sure that the tsconfig.json file is at the root of your project.

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "jsx": "react",
    "module": "amd",
    "noImplicitAny": true,
    "outDir": "./dist/",
    "preserveConstEnums": true,
    "removeComments": true,
    "sourceMap": true,
    "target": "es6",
    "moduleResolution": "node",
    "strict": true,
    "alwaysStrict": true,
    "strictNullChecks": false,
    "downlevelIteration": true
  },
  "include": [
    "./src/"
  ],
  "exclude": [
    "node_modules"
  ]
}

Jest Config 🧪

The Jest configuration file is used to configure Jest, the testing framework that we’ll be using. Make sure to place the jest.config.json file at the root of your project. Again like the TypeScript config, this is opinionated so please feel free to change it, everything is pretty standard though: (We’ll talk about the setupEnzyme.js file a second)

{
  "roots": [
    "<rootDir>/src"
  ],
  "transform": {
    "^.+\\.tsx?$": "ts-jest"
  },
  "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(ts|tsx)$",
  "testPathIgnorePatterns": [
    "./src/__tests__/setupEnzyme.ts"
  ],
  "collectCoverageFrom": [
    "src/**/*.{js,jsx,ts,tsx}",
    "!/node_modules/"
  ],
  "moduleFileExtensions": [
    "ts",
    "tsx",
    "js",
    "jsx"
  ],
  "snapshotSerializers": [
    "enzyme-to-json/serializer"
  ],
  "setupFilesAfterEnv": [
    "<rootDir>/src/__tests__/setupEnzyme.ts"
  ],
  "moduleNameMapper": { 
    "\\.(css|less|scss|sass)$": "identity-obj-proxy" 
  }
}

Src 🚗

(These emojis mean nothing 😆)

At the root of your project, you’ll want to create a src directory where all of the project code will live. Inside of the src directory, we’ll have 3 directories, __tests__ this is where all of our tests will live (Some people are hella opinionated about where this lives, so it is really up to you), components obviously where your components live, and declerations this is where we will be putting the declaration files for our project. Of course, you can name these directories whatever you want and you can create other directories if you would like as well, this is just a suggested project architecture, just note that you may have to edit the various configurations if you don’t follow this architecture exactly.

Alongside these directories, we have 2 files, index.tsx, which is the entry point for the project, and index.html, which is the template html file used by the HtmlWebpack plugin.

Inside of the __tests__ directory, you’ll need to create a file named setupEnzyme.ts , this file is referenced in the jest.config.json and has a really simple config for Enzyme.

import * as Enzyme from 'enzyme';
import * as Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({
  adapter: new Adapter()
});

Go ahead and create a simple component and use it in the project entry point ( index.tsx ) and run the following command in the terminal:

npx webpack-dev-server -open -colors -hot -mode development

A browser window should pop open and you should see your component (make sure you check your terminal for errors though)!

Yay, you’re done!!! 🍰

You have all you need to get your React TypeScript project up and running. If you want to access all of the source code you can go to the following github repo. If you want to suggest a change make a pull-request!

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

React - The Complete Guide (incl Hooks, React Router, Redux)
Modern React with Redux [2019 Update]
Understanding TypeScript
Learn TypeScript From Scratch
Build Progressive Web Apps with React
Learn React 2019 - Full Course for Beginners - React 2019 Tutorial
Learn React.js - Frontend Web Development for Beginners - Web Development in 2019
Push Notifications in PWA (React.js) Using Firebase

Originally published by Ameer Sami at https://medium.com

Suggest