/webpack-react-redux-starter

Starter kit for React projects: webpack 4, React 16, React Hot Loader 4, Browsersync, ESLint, Mocha, CSS Modules, stylelint, and more.

Primary LanguageJavaScriptMIT LicenseMIT

DEPRECATED

This starter was created at the beginning of 2017. Currently, it is not relevant, because there are more modern tools that allow you to achieve a higher level of developer experience with a much simpler setup. So, I keep this repo in the public archive exclusively as a "museum exhibit" of how things were in the past.

webpack-react-redux-starter

🚀 Modern starter kit for React projects.

Features

  • Every dependency in project devDependencies is listed in the README and has a description with it's purpose explanation
  • Not too complex, but at the same time not oversimplified
  • Only cutting-edge technologies
  • Great developer experience:
    • Advanced live reload (with Hot Module Replacement ❤️) on every file save
    • Unit testing changed and affected files with Mocha on every file save
    • Syncronized browser testing support with Browsersync
  • Assets pipeline

NOTE: Not intended for isomorphic apps!

NOTE: Work in progress. See TODO

Prerequisites

You should have Node.js installed. Optionally you can install and use Yarn as package manager, but it's possible to use npm instead, which ships with Node.js.

Installation

  • Clone this repo with git clone https://github.com/stsiarzhanau/webpack-react-redux-starter or (if you don't use Git) just click on the green Clone or download button and then on Download ZIP
  • Unzip downloaded file (if you have used 2nd option)
  • Open terminal and go to the project's root folder (webpack-react-redux-starter)
  • Run npm install (or yarn install) to install project dependencies

Usage

  • Run npm start -s (or yarn start) to start development server

    NOTE: The -s flag is optional. It enables silent mode, which suppresses unnecessary messages from npm, and so we get cleaner output.

  • Open index.js file in src/ui/components/NavLinks folder and follow instructions to see how syntax errors are being handled and watch Hot Module Replacement in action

  • Open index.test.js file in src/ui/components/NavLinks folder and follow instructions to see how Mocha reports about failed unit tests

  • Open styles.css file in src/ui/components/NavLinks folder and play with CSS to see how style changes are hot reloaded on file save without full page refresh

  • Stop development server

    NOTE: In most cases it could be done by hitting Ctrl+C in the terminal

  • Run npm run build -s (or yarn build) to bundle project.

  • Run npm run start:prod -s (or yarn start:prod) to serve files from build folder, so you can test production version of the app.

  • If build was successful, webpack-bundle-analyzer will start to serve report file at http://127.0.0.1:8888. You can go to that address in the browser to look at and analyse bundle structure.

What's Inside?

Package Description / Purpose
Module Bundler / Development Server
webpack It's an incredibly configurable multi-purpose tool which do a lot of useful things. Out of the box it just bundles our JavaScript and JSON files (though do it extremely well). But with the help of plugins and loaders it can handle other file types too. So we can easily implement full assets pipeline. Also, in most cases, with the help of npm scripts it can be a good substitution for task runners (such as gulp or Grunt). Moreover, when combined with additional packages (such as webpack-dev-server, express or browser-sync (with the help of webpack-dev-middleware in the last two cases)), it can provide us with super handy development environment.
browser-sync This package provides us with a server which we use to serve our files during development. It also starts to serve our production files from build folder when we successfully bundle our project with npm run build -s (or yarn run build) command. The main advantage of using browser-sync over servers like express is that it can sync multiple browsers. It means that, when we have multiple browsers opened and make some interactions in one of them (like scrolling, for example), we can see the result of that interaction in other browsers too (which is really great feature for cross-browser testing). Moreover, it can sync browsers not only on the same machine but also on all devices connected to the same Wi-Fi network. So, we can, for example, test how our responsive design looks simultaneously on desktop, laptop, as well as on different tablets and phones. NOTE: It seems, that desktop can't use a wired connection that routes to the Wi-Fi network. Both desktop and mobile must be on Wi-Fi.
webpack-dev-middleware This package is a glue between browser-sync (or any other server that we use instead) and webpack. One of it's cool features is that files emitted from webpack during development aren't being written on disk and can be served directly from the memory, which is much faster approach.
webpack-hot-middleware This bit of software allows us to use (with webpack-dev-middleware) really awesome webpack feature: Hot Module Replacement (HMR). It's sort of better live reload. When we update some code and save our file, we can immediately see those changes in a browser. Moreover, it happens without full page reload, cause only changes that affect updated module (file) are being injected. Another cool feature that webpack-dev-middleware gives to us is an overlay with error messages which appears in our browser window when any error occurs. So there's no need to keep our terminal and devTools console constantly opened.
react-hot-loader React Hot Loader (RHL) is a plugin that allows React components to be live reloaded without the loss of state. It works with webpack (as loader) and other bundlers that support both Hot Module Replacement (HMR) and Babel plugins.
Babel
@babel/core Babel compiler core. This package is required to other Babel related packages work properly.
@babel/node Allows us to use babel-node command in our npm scripts. To have babel-node command in our disposal is necessary if we want to use ES6 modules syntax (like import webpack from 'webpack;) in our scripts that will run in Node.js environment (like scripts located in tools and config folders).
@babel/preset-env Out of the box (if we didn't provide any plugin or preset) Babel will just parse our ES6+ code to some structure known as Abstract Syntax Tree (AST) and then generate it back. Output code will be the same as input code (ES6+). As we want to transform ES6+ to ES5 we need to include transformers (plugins) that will transform ES6+ code parsed by Babel compiler before it will be generated back. Preset is a collection of plugins. babel-preset-env includes all plugins that provide transformations for standardized ES6+ features (that are in ES2015, ES2016, ES2017... specs). But you can configure it to only include the polyfills and transforms needed for the browsers you support. Compiling only what's needed can make your bundles smaller and your life easier.. More about this preset
@babel/plugin-proposal-... JavaScript constantly evolves. New proposed features before coming a part of a release (such as ES6/ES2015) should be approved. babel-preset-env doesn't include plugins for such features by default. In Babel 6 such plugins was groupped into official stage presets (like babel-preset-stage-3). But since Babel 7 plugins for proposed features are shipped separately. More about proposed features.
@babel/plugin-syntax-... Syntax plugins for proposed features that are not included into babel-preset-env. These plugins only allow Babel to parse specific types of syntax (not transform).
@babel/preset-react Preset of Babel plugins which allow to transform syntaxes that are not part of JavaScript itself, but are commonly used in React applications (such as JSX or flow), to ES5.
babel-loader Loader for webpack. With this package webpack is able to transform ES6+ to ES5 while it bundles our files. It happens not only when we make production build with npm run build -s (or yarn run build) command, but everytime we save our .js / .jsx files during development (thanks to our development server).
webpack Plugins (3rd party)
html-webpack-plugin To serve our files server needs index.html file which contains <script src="..."> tag(s) pointing to our bundle(s). But there are different webpack usage scenarios when bundle name(s) are being changed during every compilation. html-webpack-plugin basically do the following. It takes provided template and generates new index.html file with correct bundle name(s) every time we compile our project. This plugin also has a lot of additional options and third party addons which extend it's basic functionality.
compression-webpack-plugin We can drastically decrease app files size (and page loading time as a result) using compression (for example gzip). This plugin generates compressed versions of all assets that could be effectively compressed (like .css and .js bundles, for example).
webpack-bundle-analyzer Webpack plugin and that represents bundle content as convenient interactive zoomable treemap. This plugin will help you realize what's really inside your bundle, find out what modules make up the most of it's size and optimize it. By default, that treemap representation will be automatically opened in default browser when webpack finishes bundle compilation.
terser-webpack-plugin Plugin that allows to minify JavaScript bundles for production.
ESLint
eslint ESLint is a tool to check our JavaScript coding style. It not only helps to keep our code clean and consistent, but prevents many possible errors which can break our app. To check any given file(s) you can run eslint command with path(s) to the file(s) and optional arguments (see ESLint CLI reference) from command line. If your code has any stylistic issues you'll see ESLint messages in your console window. This starter kit also has preconfigured eslint npm script. If you run npm run eslint (or yarn run eslint) command it will check all files located in src, tools, and config folders, as well as in the root of your project. This project sticks to Airbnb Style Guide (with the help of eslint-config-airbnb package), but you can easily override any rules in .eslintrc.js file (which is located in the project root) or just use another style guide that better fits your needs and habits. It also worth mentioning that ESLint itself (not only it's rules) is highly configurable tool.
babel-eslint Alternative parser for ESLint that allows you to lint ALL valid Babel code. We don't need to use babel-eslint if we are using ES2015 (ES6), ES2016 (ES7) or ES2017 (ES8). ESLint actually supports ES2015/ES2016/ES2017, JSX, and object rest/spread by default now. But as we use babel-preset-stage-3 (and probably you'll want to try other experimental features) I've included this package into the project.
eslint-config-airbnb Generally it's just a set of rules and preconfigured parser options (we can look at which parser options it includes here). NOTE: This package relies on rules that are defined in eslint package itself as well as in eslint-plugin-import, eslint-plugin-jsx-a11y and eslint-plugin-react packages. So it's important to have the CORRECT VERSIONS of those packages installed! See eslint-config-airbnb package README for instructions.
eslint-plugin-import This ESLint plugin intends to support linting of ES2015+ (ES6+) import/export syntax, and prevent issues with misspelling of file paths and import names.
eslint-plugin-jsx-a11y This ESLint plugin allows us to check if our JSX code meets accessibility standards (WAI-ARIA) so our app will be more accessible to people with disabilities.
eslint-plugin-react This package adds React specific linting rules to ESLint.
eslint-loader Loader for webpack. With the help of this package it's not necessary to manually run eslint command every time when we want to check our files. With eslint-loader our files will be checked on every file save during development and also when we bundle our project with npm run build -s (or yarn run build) command. If our JavaScript code has any style issues, linter messages will be shown in the console window. As alternative you can use eslint-watch package.
eslint-import-resolver-webpack When we bundle our code with webpack it's possible to simplify file paths in our ES6 import statements (due to resolver library that webpack uses under the hood, and resolve configuration object settings in webpack config). For example, we can omit the file extension and part of the file path (just use import Button from 'components/button' instead of import Button from './src/components/button/index.js'). But with such approach we'll end up with lint errors from eslint-plugin-import, cause it will think that we probably misspell path to our module. eslint-import-resolver-webpack provides eslint-plugin-import with information about our resolve settings to prevent false-positive lint errors and warnings. NOTE: We need to configure this package settings in our .eslintrc.js file (see package README).
Unit Testing
jsdom React code is intended to run in browsers. Our test framework (Mocha) is able to run tests in browsers. Also we can use Mocha with Karma test runner that can run tests in real browsers like Chrome or headless browsers (browsers without graphical interface) like PhantomJS and do many other cool stuff. But testing in browsers is relatively slow (partly because of slow speed of Document Object Model (DOM) interface). jsdom is a lightweight and fast JavaScript implementation of DOM for Node.js which allows our Mocha tests to run much faster.
mochapack Integrates mocha test runner with webpack precompiler. Provides a much better watch mode than mocha.
mocha Mocha is a JavaScript test framework. It's very popular testing tool and have a lot of features. Though, there are good alternatives to Mocha. For example AVA and Jest.
chai Mocha has no built-in assertion library. But it allows us to use any assertion library that we want. We'll use here chai as it provides a lot of assertions out of the box, has many plugins (extensions), and can be used both for Behaviour Driven Development (BDD) and Test Driven Development (TDD). There are many alternatives for chai. For example mjackson/expect.
chai-as-promised Extends chai with assertions about promises.
sinon Sinon is a library that provides test spies, stubs and mocks for testing JavaScript code and works with any unit testing framework. More about spies, stubs and mocks.
sinon-chai This package provides a set of custom assertions for using sinon with chai.
react-test-renderer This package provides an experimental React renderer that can be used to render React components to pure JavaScript objects, without depending on the DOM or a native mobile environment. Essentially, this package makes it easy to grab a snapshot of the "DOM tree" rendered by a React DOM or React Native component without using a browser or jsdom. We should install this package in addition to enzyme (as well as react-dom) so enzyme can work properly.
enzyme This package is another React-specific testing utility. It's built on top of react-dom/test-utils and react-test-renderer, but with Enzyme it's much easier to assert, manipulate, and traverse React components' output. Enzyme's API is meant to be intuitive and flexible by mimicking jQuery's API for DOM manipulation and traversal.
enzyme-adapter-react-16 We need to install enzyme along with an Adapter corresponding to the version of React (or other UI Component library) we are using.
chai-enzyme This chai plugin provides additional assertions for testing React components with Enzyme.
cheerio Implementation of core jQuery designed specifically for the server. We don't utilize this package directly. It's installed, because chai-enzyme requires it as a peer dependency.
nyc CLI for istanbul that collects coverage data & generates coverage reports. More info.
istanbul-instrumenter-loader Loader that wraps code with hooks to track coverage when your code get's executed. More info.
webpack-node-externals Allows webpack not to bundle packages from node_modules into test bundle and just require them instead that improves test build speed.
Assets/Styles
file-loader This loader helps us to handle assets (like images, custom fonts, video, etc.). When in our code we refer to some asset (for example logo.png), file-loader copies that logo.png file into output directory (build) and creates an entry with path to this file inside of resulting bundle (bundle.js). So, our production code in resulting bundle, which needs the logo.png, knows where to find that image. file-loader can be configured through options. To read more about possible options see package README.
url-loader url-loader is a wrapper around the file-loader. If file size of some asset is smaller than a specified limit (in bytes) this loader can just inline it into resulting bundle as base64 encoded Data URI. Otherwise file will be automatically processed by the file-loader. Such an approach allows us to save some additional HTTP requests, but also increases resulting bundle size. So we should specify the right limit to observe balance. We can pass to url-loader all the options of file-loader and, of course, limit option.
svg-sprite-loader Webpack loader for creating SVG sprites to provide us with great icon system.
svgo SVG files, especially exported from various editors, usually contain a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result. SVGO does this job.
svgo-loader svgo-loader applies SVGO optimizations to .svg files BEFORE they are being added to the SVG sprite. We can also pass some options to it in order to override SVGO defaults.
style-loader Basically this loader does the following. It takes project's CSS code and injects it into the resulting bundle together with some JavaScript code that wraps that CSS into <style> tag and then dynamically (on runtime) injects that <style> tag into markup (index.html). Such approach is good for development, as it allows styles to be hot reloaded without full page refresh (thanks to Hot Module Replacement). For production build it's better to use mini-css-extract-plugin (which generates separate CSS bundle(s)) instead of the style-loader. Also it's worth mentioning that style-loader should be used together with other loaders (usually css-loader) that will actually transform CSS, so it could be injected into resulting bundle. style-loader is just responsible for injecting itself. style-loader have some options. To read more about possible options see package README.
css-loader It's possible just to convert CSS into string (with raw-loader) and then pass it to style-loader. The latter will inject that CSS string into JS bundle and our CSS will work as expected. But if our CSS code contains some "calls" to assets inside CSS files (like @import 'style.css' or background: url(path/to/asset.js)) those assets will be unaccessible. It happens cause they were not actually required by webpack and so neither copied into otput directory nor inlined into resulting bundle. css-loader solves that problem. It finds all those @import's and url()'s in CSS code and require assets they point to. Then appropriate loaders (like url-loader or file-loader) do their job (see above) and we have our assets inlined into JS bundle or copied into build folder. We can also extend css-loader functionality (through it's options). For example, we can enable CSS Modules support or minification. To read more about possible options see package README. If you are not familiar with CSS Modules take a look at this article and also at this one.
mini-css-extract-plugin This plugin is useful for production builds. Instead of inlining CSS code into resulting JavaScript bundle (like style-loader does) it creates separated CSS bundle file(s). If our total stylesheet volume is big, it will be faster because the CSS bundle is loaded in parallel to the JS bundle. Before webpack v4 extract-text-webpack-plugin was used instead
postcss-loader This loader allows us to process our stylesheets with PostCSS plugins. It reads plugins configuration from postcss.config.js file.
imagemin-webpack-plugin This plugin is useful for production builds. It allows us to minify (compress) our image assets and SVG's. Under the hood it uses a bunch of imagemin plugins. To read more about included plugins and their options see package README.
optimize-css-assets-webpack-plugin This plugin uses css-nano internally and allows to minify css assets in production mode. Comparing to using css-nano as PostCSS plugin with post-css-loader it minifies the whole bundle(s) instead of separate chunks which could be potentially faster and with a smaller result.
PostCSS Plugins
autoprefixer The most popular PostCSS plugin. It automatically adds vendor prefixes to CSS rules using data from Can I Use website. NOTE: We can list browsers to support in browserslist configuration file.
lost This PostCSS plugin provides us with a powerful and easy to use grid system. More info.
postcss-flexbugs-fixes Flexbox allows us to build responsive layouts without using any grids like lost (which optionally uses Flexbox under the hood anyway). But Flexbox has many bugs, especially in the old browsers. This PostCSS plugin tries to fix all of flexbug's issues.
stylelint
stylelint Linter that helps you avoid errors and enforce conventions in your styles.
stylelint-webpack-plugin This webpack plugin allows us to lint our stylesheets when we make our production build and also on every file save during development. If our stylesheets code have any stylistic issues, linter messages will be shown in the console window.
stylelint-config-standard The standard shareable config for stylelint. It's recommended by stylelint team as foundation for your own config.
Misc
cross-env In some of our npm scripts we need to set environment variables. For example, NODE_ENV, which we use to tell webpack and friends in which environment (development or production) given command should run. But in different OS we have to set it in a slightly different way. For UNIX based OS we shoud use syntax like NODE_ENV=production. For Windows - set NODE_ENV=production. cross-env package allows us to set environment variables in Unix style and have it work on Windows too.
npm-run-all This package allows us to run multiple npm scripts in parallel or sequential (and do it in a cross-platform way). For example, we can write run-p serve:src test:watch instead of npm run serve:src & test:watch. So, with npm-run-all our npm scripts not only look more concise, but work fine on Windows (which treats & operator in a different way than UNIX based OS).
rimraf This bit of software provides us with rimraf command (the cross-platform equivalent to Unix rm -rf command, which allows us to remove folders with all their contents).
chalk This package allows us to choose colors for console.log messages that some of our scripts in tools folder print to terminal.
compression We use compression-webpack-plugin in our project to get compressed versions of app files. This middleware allows our local production server (tools/prodServer.js, tools/build.js) serve compressed files instead of the original when possible. So we can test app performance without deploying it on the back-end server.
husky This package allows to add different git hooks. For example lint files before you can commit your changes. Those hooks can be configured in package.json like "precommit": "lint-staged".
lint-staged With the help of this package we can run linter(s) only for files that are staged for commit. It's much more faster than to lint all files when project is large enough.
whatwg-fetch Polyfill that implements a subset of the standard Fetch specification (that is a modern replacement for XMLHttpRequest) for not-so-modern browsers like IE. If we target only modern browsers that support Fetch we can remove this polyfill.
redux-devtools-extension This package connects redux store with Chrome Redux DevTools extension.
browserslist This package allows to configure target browsers in one place and share those stettings between different front-end tools, like autoprefixer, stylelint and @babel/preset-env

Acknowledgements

This project is inspired by Cory House coryhouse/react-slingshot

Some ideas are taken from following starter kits:

Juho Vepsäläinen "SurviveJS - Webpack" book was a huge help.

TODO

  • Add an example app that will utilize such technologies as:
    • react-router v4
    • redux
    • redux-saga
  • Write test suites that will demonstrate all test tools, which are used in the project, in action