{"id":22799099,"url":"https://github.com/thomd/on-webpack","last_synced_at":"2026-05-03T06:37:05.391Z","repository":{"id":66830106,"uuid":"162192309","full_name":"thomd/on-webpack","owner":"thomd","description":"Notes on Webpack 4","archived":false,"fork":false,"pushed_at":"2019-01-21T14:00:25.000Z","size":171,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-30T19:14:53.366Z","etag":null,"topics":["javascript","webpack"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-17T21:31:17.000Z","updated_at":"2019-01-21T14:00:26.000Z","dependencies_parsed_at":"2023-03-26T14:51:32.908Z","dependency_job_id":null,"html_url":"https://github.com/thomd/on-webpack","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thomd/on-webpack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-webpack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-webpack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-webpack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-webpack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomd","download_url":"https://codeload.github.com/thomd/on-webpack/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-webpack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32560901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","webpack"],"created_at":"2024-12-12T07:07:54.516Z","updated_at":"2026-05-03T06:37:05.377Z","avatar_url":"https://github.com/thomd.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Notes on Webpack 4\n\nWebpack is a **module bundler**. In Webpack, everything is a **module**. Not only JavaScript but also everything else (style sheets, images, markup) can be a module.\n\nStarting from **entry** points, Webpack creates a dependency graph which allows for bundling single or multiple **outputs** so that you just load what you need and when you need it.\n\nWith **loaders** you can intercept your dependencies and pre-process them before they get bundled.\n\nWith **plugins** you can perform subsequent tasks like bundle optimizations (Webpack itself is considered a plugin with one behaviour by default: bundle assets).\n\nHence, Webpack is basically about **entry**, **output**, **loaders** and **plugins**.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Entry \u0026 Output](#entry--output)\n  - [Without configuration file](#without-configuration-file)\n  - [With configuration file](#with-configuration-file)\n  - [Append multiple files](#append-multiple-files)\n  - [Multiple bundles](#multiple-bundles)\n  - [Public Path](#public-path)\n- [Webpack Loaders](#webpack-loaders)\n  - [Inline Loaders](#inline-loaders)\n  - [Transpile JavaScript with Babel](#transpile-javascript-with-babel)\n  - [Transpile React JSX with Babel](#transpile-react-jsx-with-babel)\n  - [Use Babel Polyfill](#use-babel-polyfill)\n  - [Import CSS](#import-css)\n  - [Transpile SASS and PostCSS](#transpile-sass-and-postcss)\n  - [Export into separate files](#export-into-separate-files)\n  - [Transform files into base64 URIs](#transform-files-into-base64-uris)\n- [Webpack Plugins](#webpack-plugins)\n  - [Create HTML index file for bundled modules](#create-html-index-file-for-bundled-modules)\n  - [Extract CSS into separate files](#extract-css-into-separate-files)\n  - [Clean build folder before building](#clean-build-folder-before-building)\n  - [Hot Module Replacement](#hot-module-replacement)\n- [Code Splitting](#code-splitting)\n  - [Entry Points](#entry-points)\n  - [Prevent Duplication](#prevent-duplication)\n  - [Dynamic Imports](#dynamic-imports)\n- [Webpack Best Practices](#webpack-best-practices)\n  - [Webpack CLI Options](#webpack-cli-options)\n  - [Source Maps](#source-maps)\n  - [Manage multiple configurations](#manage-multiple-configurations)\n  - [Webpack Development Server](#webpack-development-server)\n  - [Externalize Dependencies to be Loaded via CDN](#externalize-dependencies-to-be-loaded-via-cdn)\n  - [Debugging Webpack](#debugging-webpack)\n  - [Inspect Webpack bundles](#inspect-webpack-bundles)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n---\n\n# Entry \u0026 Output\n\n## Without configuration file\n\n    npm init -y\n    npm i -D webpack webpack-cli\n\nThe **default entry point** is `./src/index.js`\n\nThe **default output file** is `./dist/main.js`\n\nBuild in **Development mode** for optimized speed and an un-minified bundle:\n\n    npx webpack --mode development\n\nBuild in **Production mode** to enable optimizations out of the box, including minification, scope hoisting, tree-shaking and more:\n\n    npx webpack --mode production\n\n\u003e Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e. `import` and `export`.\n\n## With configuration file\n\nCreate a `webpack.config.js` file (using default settings)\n\n```diff\n+   module.exports = {\n+     mode: 'production',\n+     entry: './src/index.js',\n+     output: {\n+       path: __dirname + '/dist',\n+       filename: 'main.js'\n+     }\n+   }\n```\n\nand run\n\n    npx webpack\n\nThe key `entry` can be a string (`'./src/index.js'`), an array (`['./src/index.js']`) or an object(`{'index': './src/index.js'}`).\n\n## Append multiple files\n\nYou can append multiple files that are **NOT dependent on each other** into one bundle using the Array format:\n\n```diff\n    module.exports = {\n-     entry: './src/index.js',\n+     entry: ['./src/index.js', './src/analytics.js']\n    }\n```\n\n## Multiple bundles\n\nIn order to create multiple bundles, you can either export multiple configurations in an array like this:\n\n    module.exports = [config1, config2]\n\nor you can use an entry object with multiple entry files and replace the output with a filename **substitution**:\n\n```diff\n    module.exports = {\n-     entry: './src/index.js',\n+     entry: {\n+       main: ['./src/index.js', './src/analytics.js'],\n+       vendor: './src/vendor.js'\n+     },\n      output: {\n        path: __dirname + '/dist',\n-       filename: 'main.js'\n+       filename: '[name].js'\n      }\n    }\n```\n\nUse glob to append all files from a folder:\n\n```diff\n    entry: {\n      main: ['./src/index.js', './src/analytics.js'],\n      vendor: './src/vendor.js'\n+     style: glob.sync('./src/**/!(_)*.scss')\n    },\n```\n\n## Public Path\n\nThe config key `output.publicPath` is used by some loaders \u0026 plugins (**url-loader**, **file-loader**, **HtmlWebpackPlugin**, etc.) to generate public URL paths for webpack output:\n\n```diff\n    module.exports = {\n      output: {\n        path: __dirname + '/dist',\n+       publicPath: 'http://my.cdn.com',\n        filename: '[name].js'\n      }\n    }\n```\n\n# Webpack Loaders\n\nLoaders describe to webpack how to process non-JavaScript modules and include these dependencies into your bundles.\n\nWithout any loader, Webpack is basically a bundler for JavaScript modules (ESM and CommonJS) which adds bootstrap code for module loading.\n\nThere are three ways to use loaders:\n\n* **Configuration** (recommended): Specify them in your `webpack.config.js` file.\n* **Inline**: Specify them explicitly in each import statement.\n* **CLI**: Specify them within a shell command.\n\n## Inline Loaders\n\nSeparate loaders from the import resource with `!`. Each part is resolved relative to the current directory:\n\n    import Styles from 'style-loader!css-loader?modules!./styles.css';\n\nIt’s possible to **override** any loaders in the configuration by **prefixing** the entire rule with `!`:\n\n    import logo from '!url-loader?limit=10000!./webpack.png'\n\nOptions can be passed with a query parameter, e.g. `?key=value\u0026foo=bar`, or a JSON object, e.g. `?{\"key\":\"value\",\"foo\":\"bar\"}`.\n\nUse `module.rules` whenever possible, as this will reduce boilerplate in your source code and allow you to debug or locate a loader faster.\n\n## Transpile JavaScript with Babel\n\nFirst install **Babel** dependencies\n\n    npm i -D @babel/core @babel/cli @babel/preset-env\n\nthen create a Babel configuration file `.babelrc`\n\n```diff\n+   {\n+     \"presets\": [\"@babel/preset-env\"]\n+   }\n```\n\nand define the supported browsers in `package.json` like this:\n\n```diff\n    {\n      \"name\": \"on-webpack\",\n+     \"browserslist\": [\n+       \"last 2 versions\",\n+       \"ie \u003e= 10\"\n+     ]\n    }\n```\n\nYou may then verify the list of browsers via\n\n    npx browserslist\n\nBe aware, that Babel only adds polyfills for [ECMAScript](https://tc39.github.io/ecma262/) methods.\nFor methods from the Browser API, for example [fetch](https://fetch.spec.whatwg.org/), you need to add the polyfill yourself.\n\nSecond, install Babel loader with\n\n    npm i -D babel-loader\n\nand either transpile with\n\n    npx webpack --mode development --module-bind js=babel-loader\n\nor add a Babel loader rule into Webpack configuration:\n\n```diff\n    module.exports = {\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js'\n+     },\n+     module: {\n+       rules: [\n+         {\n+           test: /\\.js$/,\n+           use: ['babel-loader'],\n+           exclude: /node_modules/\n+         }\n+       ]\n      }\n    }\n```\n\n## Transpile React JSX with Babel\n\nFirst install [React](https://reactjs.org/) as a runtime dependency\n\n    npm i react react-dom\n\nThen install Babels React preset and optionally some plugins (for example class-properties syntax)\n\n    npm i -D @babel/preset-react\n\nand add to the Babel configuration file `.babelrc`\n\n```diff\n    {\n-     presets: [\"@babel/preset-env\"]\n+     presets: [\"@babel/preset-env\", \"@babel/preset-react\"]\n    }\n```\n\nIf you want to use class properties, then install the babel plugin for it:\n\n    npm i -D @babel/plugin-proposal-class-properties\n\nand add into `.babelrc` as plugin:\n\n```diff\n    {\n      presets: [\"@babel/preset-env\", \"@babel/preset-react\"]\n+     plugins: [\"@babel/plugin-proposal-class-properties\"]\n    }\n```\n\nIf you want to import a React Components without a `.jsx` extension like this\n\n```diff\n-   import App as './App.jsx'\n+   import App as './App'\n```\n\nyou might need to tell webpack to resolve this extensions in `webpack.config.js`:\n\n```diff\n+   resolve: {\n+     extensions: ['.js', '.jsx'],\n+   },\n```\n\n## Use Babel Polyfill\n\nIn order to add a **standard library polyfill** for specific browsers, install **Babel Polyfill**\n\n    npm i @babel/polyfill\n\nand instruct Babel to include these built ins in `.babelrc`:\n\n```diff\n    {\n      presets: [\n-       '@babel/preset-env',\n+       ['@babel/preset-env', {\n+         useBuiltIns: 'entry'\n+       }],\n        '@babel/preset-react'\n      ]\n    }\n```\n\nThen import the polyfills in your application `./src/index.js` via\n\n```diff\n    import React from 'react'\n    import ReactDOM from 'react-dom'\n+   import '@babel/polyfill'\n    import App from './App'\n\n    ReactDOM.render(\u003cApp/\u003e, document.getElementById('app'))\n```\n\nwhich will append the **code-js** module (**~70 KB**) into your bundle. As not all features are needed by your target browsers, specify them in your browserslist in `package.json`:\n\n```diff\n    {\n      ...\n      \"browserslist\": [\n        \"last 2 versions\",\n        \"\u003e 1%\"\n      ]\n    }\n```\n\nYou can print the so specified browsers via\n\n    npm browserslist \"last 2 versions, \u003e 1%\"\n\n## Import CSS\n\nThe `css-loader` resolves `@import` and `url()` as modules like `import/require()` and returns the CSS code as JavaScript. It doesn't actually do anything with the returned CSS.\n\nThe `style-loader` adds the CSS to the DOM by injecting a `\u003cstyle\u003e` tag on run-time.\n\n    npm i -D style-loader css-loader\n\nFirst add both loaders into Webpack configuration (loaders are evaluated from right to left):\n\n```diff\n    module.exports = {\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js'\n+     },\n+     module: {\n+       rules: [\n+         {\n+           test: /\\.css$/,\n+           use: ['style-loader', 'css-loader'],\n+           exclude: /node_modules/\n+         }\n+       ]\n      }\n    }\n```\n\nThen simply import the CSS in the entry file `./src/index.js` like being a module so Webpack know it's a dependency:\n\n    import './main.css'\n\nand import subsequent CSS dependencies in `main.css` like so\n\n```diff\n+   @import '~bootstrap/dist/css/bootstrap.css';\n+   @import './app.css';\n```\n\nInjecting CSS as `\u003cstyle\u003e` tag by JavaScript is performance wise not the best idea - you should load CSS as soon as possible to avoid FOUC and leverage caching. Use the **MiniCssExtractPlugin** plugin to extract CSS as a separate file.\n\n## Transpile SASS and PostCSS\n\nInstall loaders, **SASS** and **Autoprefixer** with\n\n    npm i -D node-sass autoprefixer\n    npm i -D postcss-loader sass-loader\n\nand add a **PostCSS** configuration `postcss.config.js`\n\n```diff\n+   module.exports = {\n+     plugins: [\n+       require('autoprefixer')()\n+     ]\n+   };\n```\n\nDefine the supported browsers in `package.json` like this:\n\n```diff\n    {\n      \"name\": \"on-webpack\",\n+     \"browserslist\": [\n+       \"last 2 versions\",\n+       \"ie \u003e= 10\"\n+     ]\n    }\n```\n\nYou may check the supported browsers and CSS prefixes via\n\n    npx browserslist\n    npx autoprefixer --info\n\nThen add webpack loaders to `webpack.config.js`\n\n```diff\n    module.exports = {\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js'\n      },\n      module: {\n        rules: [\n          {\n-           test: /\\.css$/,\n+           test: /\\.scss$/,\n-           use: ['style-loader', 'css-loader'],\n+           use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],\n            exclude: /node_modules/\n          }\n        ]\n      }\n    }\n```\n\nUsing SASS allows to import selective Bootstrap components. If you for example only want to use the button component, then import the following in `main.css`:\n\n```diff\n-   @import '~bootstrap/dist/css/bootstrap.css';\n+   @import '~bootstrap/scss/functions';\n+   @import '~bootstrap/scss/variables';\n+   @import '~bootstrap/scss/mixins';\n+   @import '~bootstrap/scss/buttons';\n+   @import '~bootstrap/scss/button-group';\n\n    @import './variables';\n    @import './app';\n```\n\n## Export into separate files\n\nAs an lean alternative to the **HTMLWebpackPlugin**, the **extract-text-webpack-plugin** or the **mini-css-extract-plugin** you can also use a combination of the **file-loader** and the **extract-loader**.\n\n### Extract and separate HTML files\n\n```diff\n    {\n+     test: /\\.html$/,\n+     use: ['file-loader?name=[name].[ext]', 'extract-loader', 'html-loader']\n    }\n```\n\n### Extract and separate CSS files\n\n```diff\n    {\n+     test: /\\.scss$/,\n+     use: ['file-loader?name=[name].css', 'extract-loader', 'css-loader', 'postcss-loader', 'sass-loader']\n    }\n```\n\n### Copy Image into output folder\n\nThe file-loader make the file URL available for programmatic usage. So a use case is:\n\n```jsx\n    import url from './file.png'\n\n    function Component (props) {\n      return (\n        // dynamic\n        \u003cimg src={url}\u003e\n      )\n    }\n```\n\nSome loaders may work in conjunction with the **file-loader**, e.g the **css-loader** transforms `url(./file.png)` into `require('./file.png')` internally, which requires the **file-loader** to handle the `require('./file.png')` module request.\n\n```diff\n    {\n+     test: /\\.(png|jpe?g|gif|svg)$/,\n+     use: [\n+       {\n+         loader: 'file-loader',\n+         options: {\n+           name: '[name].[ext]'\n+         }\n+       }\n+     ]\n    }\n```\n\nIf you have a static image reference and just want to copy a file into the output folder, then simply use the **copy-webpack-plugin** plugin:\n\n```jsx\n    function Component (props) {\n      return (\n        // static (handcoded)\n        \u003cimg src=\"path/to/dist/image.png\"\u003e\n      )\n    }\n```\n\n## Transform files into base64 URIs\n\nA typical use case is to have small images **base64 encoded** to avoid HTTP requests.\n\nInstall **url-loader** with\n\n    npm i -D url-loader\n\nand define loader with a size limit for all image types:\n\n```diff\n    {\n+     test: /\\.(png|jpe?g|gif|svg)$/,\n+     use: [\n+       {\n+         loader: 'url-loader',\n+         options: {\n+           limit: 5000\n+         }\n+       }\n+     ]\n    }\n```\n\nIf the file size exceeds the defined limit, then **url-loader** automatically falls back to the\n**file-loader**.\n\nIf you want to define additional **file-loader** options or want to use an other fallback loader:\n\n```diff\n    {\n      test: /\\.(png|jpe?g|gif|svg)$/,\n      use: [\n        {\n          loader: 'url-loader',\n          options: {\n            limit: 5000,\n+           fallback: 'file-loader?name=[name].[ext]'\n          }\n        }\n      ]\n    }\n```\n\n# Webpack Plugins\n\n## Create HTML index file for bundled modules\n\nThe **HtmlWebpackPlugin** creates a new `index.html` file and add script tags for each resulting bundle. It also supports templating syntax and is highly configurable.\n\nIf you have any CSS assets (for example, CSS extracted with the **MiniCssExtractPlugin**) then these will be included with `\u003clink\u003e` tags in the HTML head.\n\nInstall plugin with\n\n    npm i -D html-webpack-plugin\n\nand add to `webpack.config.js`:\n\n```diff\n+   const HtmlWebpackPlugin = require('html-webpack-plugin')\n    module.exports = {\n+     plugins: [\n+       new HtmlWebpackPlugin()\n+     ]\n    }\n```\n\nIn case you need a specific HTML file, for example with a React application container element `\u003cdiv id=\"app\"\u003e\u003c/div\u003e`, then create a template file `./src/index.html` and reference in Webpack config:\n\n```diff\n    const HtmlWebpackPlugin = require('html-webpack-plugin')\n    module.exports = {\n      plugins: [\n-       new HtmlWebpackPlugin()\n+       new HtmlWebpackPlugin({\n+         template: './src/index.html'\n+       })\n      ]\n    }\n```\n\nYou can also use the same plugin more than once, for example to generate multiple HTML pages:\n\n```\n    module.exports = {\n      entry: {\n        one: './src/one.js',\n        two: './src/two.js',\n      },\n      output: {\n        path: __dirname + '/dist',\n        filename: '[name].js'\n      },\n      plugins: [\n        new HtmlWebpackPlugin({\n          filename: 'one.html',\n          template: './src/one.html',\n          chunks: ['one']\n        }),\n        new HtmlWebpackPlugin({\n          filename: 'two.html',\n          template: './src/two.html',\n          chunks: ['two']\n        })\n      ]\n    }\n```\n\n## Extract CSS into separate files\n\nThe **mini-css-extract-plugin** extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports on-demand-loading of CSS and source-maps.\n\n    npm i -D mini-css-extract-plugin\n\n**MiniCssExtractPlugin** includes a loader `MiniCssExtractPlugin.loader` (to be replaced with `style-loader`) that marks the assets to be extracted. Then a plugin performs its work based on this annotation.\n\nIf the CSS is not a JavaScript dependency, then add as an entry.\n\n```diff\n+   const MiniCssExtractPlugin = require(\"mini-css-extract-plugin\")\n    module.exports = {\n-     entry: './src/index.js',\n+     entry: {\n+       'main': './src/index.js',\n+       'style': './src/main.scss'\n+     },\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js'\n      },\n      module: {\n        rules: [\n          {\n            test: /\\.scss$/,\n-           use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],\n+           use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'],\n            exclude: /node_modules/\n          }\n        ]\n      },\n+     plugins: [\n+       new MiniCssExtractPlugin({\n+         filename: \"[name].css\"\n+       })\n+     ]\n    }\n```\n\nIn combination with the **HtmlWebpackPlugin**, a `\u003clink rel=\"stylesheet\" href=\"...\"\u003e` tag is rendered in the extracted `ìndex.html`.\n\n## Clean build folder before building\n\nInstall the plugin\n\n    npm i -D clean-webpack-plugin\n\nand define paths to be cleaned in `webpack.config.js`:\n\n```diff\n+   const CleanWebpackPlugin = require('clean-webpack-plugin')\n    module.exports = {\n      plugins: [\n+       new CleanWebpackPlugin(['dist'])\n      ]\n    }\n```\n\n## Hot Module Replacement\n\nHot Module Replacement (HMR) allows modules to be updated at runtime without the need for a full refresh and without loosing current state.\n\nHMR can only work with loaders that implement and understand HMR API, for example **style-loader**, **react-hot-loader**, etc. HMR is not supported by **MiniCssExtractPlugin.loader**.\n\nYou need to use Webpack via webpack-dev-server and it should only be used for development.\n\n### Setup\n\nUpdate the **webpack-dev-server** configuration and use Webpacks built in HMR plugin in `webpack.config.dev.js`:\n\n```diff\n    const merge = require('webpack-merge')\n    const base = require('./webpack.config')\n+   const webpack = require('webpack')\n\n    module.exports = merge(base, {\n      mode: 'development',\n      devtool: 'source-map',\n      devServer: {\n        port: 9000,\n        disableHostCheck: true,\n+       hot: true\n      },\n+     plugins: [\n+       new webpack.HotModuleReplacementPlugin()\n+     ]\n    })\n```\n\nAlternatively, just use the `--hot` option of the **webpack-dev-server** CLI\n\n    npx webpack-dev-server --hot\n\n### React Setup\n\nInstall **react-hot-loader**\n\n    npm i -D react-hot-loader\n\nand add to `.babelrc`:\n\n```diff\n    {\n      plugins: [\n        '@babel/plugin-proposal-class-properties',\n+       'react-hot-loader/babel'\n      ]\n    }\n```\n\nThen mark your root component `./src/App.js` as hot-exported:\n\n```diff\n    import React from 'react'\n+   import { hot } from 'react-hot-loader/root'\n\n    class App extends React.Component {\n      ...\n    }\n\n-   export default App\n+   export default hot(App)\n```\n\n# Code Splitting\n\nThere are different types of chunks:\n\n* **sync chunks** loaded synchronously with `main.js` and you would see `\u003cscript src=\"chunk.js\"\u003e\u003c/script\u003e` in source code.\n\n* **async chunks** are loaded on demand (lazy loaded). Async chunks are created using dynamic imports.\n\n* **vendor chunks** contain 3rd party code.\n\n* **common chunks** contain code which is shared between different chunks.\n\nThere are three approaches to split code into **chunks**:\n\n1. **Entry Points**: Manually split code using entry configuration.\n\n2. **Prevent Duplication**: Use the **SplitChunksPlugin** to de-duplicate and split chunks.\n\n3. **Dynamic Imports**: Split code via inline function calls within modules.\n\n## Entry Points\n\nThe easiest way to split code is to define code chunks in a `webpack.config.js` entry object:\n\n```\n    module.exports = {\n      entry: {\n        'main': './src/index.js',\n        'page': './src/page.js',\n      }\n    }\n```\n\nThe downside of this approach is\n\n* If there are any duplicated modules between entry chunks they will be included in both bundles.\n\n* It isn't as flexible and can't be used to dynamically split code with the core application logic.\n\n## Prevent Duplication\n\nPrevent Duplication with the **SplitChunksPlugin**\n\nWebpack uses this plugin internally and we can enable/configure it inside `optimization.splitChunks` block of `webpack.config.js`.\n\nTo create a **vendor chunk** file from all those import statements of files coming from `node_modules`, add into `webpack.config.js`:\n\n```diff\n    module.exports = {\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js',\n+       chunkFilename: '[name].js'\n      },\n      ...\n      optimization: {\n+       splitChunks: {\n+         cacheGroups: {\n+           default: false,\n+           vendors: false,\n+           vendor: {\n+             chunks: 'all',\n+             test: /node_modules/,\n+             name: 'vendor'\n+           }\n+         }\n+       }\n      }\n      ...\n    }\n```\n\nHere `chunks` value tells **SplitChunksPlugin** the nature of chunks to consider for evaluation. \nIt’s value can be **initial** (only add files to the chunk if they are imported inside sync chunks), **async** (only add files to the chunk if they are imported inside async chunks) or **all**.\n\nSupposed we have chunked async modules which have a common dependency, then this dependency will be inside all chunks redundantly. To avoid this, we create a **common chunk** which shares a dependency between different chunks:\n\n```diff\n      optimization: {\n        splitChunks: {\n          cacheGroups: {\n            default: false,\n            vendors: false,\n+           common: {\n+             name: 'common',\n+             minChunks: 2,\n+             chunks: 'async',\n+             reuseExistingChunk: true,\n+             enforce: true\n+           }\n          }\n        }\n      }\n```\n\n## Dynamic Imports\n\nIn order to improve the load performance of the application, we can asynchronously load bundles through code-splitting.\n\nAt the time of writing this, **dynamic imports** is a proposal and will likely [change](https://github.com/tc39/proposal-dynamic-import) in the future.\n\nTo use dynamic imports, install the babel plugin **plugin-syntax-dynamic-import**\n\n    npm i -D @babel/plugin-syntax-dynamic-import\n\nand register in `.babelrc`\n\n```diff\n    {\n      plugins: [\n        '@babel/plugin-proposal-class-properties',\n+       '@babel/plugin-syntax-dynamic-import'\n      ]\n    }\n```\n\nThen conditionally import modules like for example:\n\n```\nelement.on('click', function () {\n  import('./src/modal').then(src =\u003e ...)\n})\n```\n\nSyntactically, dynamic imports are done using `import()` as a function instead of a statement:\n\n```javascript\n    import('lodash')          // dynamic import returning a Promise\n    import 'lodash'\n```\n\n### Magic Comments for Dynamic Imports\n\nYou can add metadata for dynamic imports like this:\n\n```diff\n-   import('./src/module.js')\n+   import(/* magic comment */'./src/module.js')\n```\n\nSet the **loading strategy** (lazy, lazy-once, eager, weak) via\n\n    import(/* webpackMode: \"eager\" */'./src//module.js')\n\nSet the chunk name with\n\n    import(/* webpackChunkName: \"my-chunk\" */'./src/module.js')\n\n### Dynamic Imports with React\n\nDynamic imports in React are done by declaring a component as lazy via `React.lazy()` and loading it using the built-in `\u003cReact.Suspense\u003e` component.\n\nAs an example, add a new React Component `./src/Warning.js` and lazy load in your `./src/App.js`\n\n```diff\n    import React from 'react'\n\n+   const Warning = React.lazy(() =\u003e import('./Warning'))\n\n    class App extends React.Component {\n      render() {\n        return (\n+         \u003cReact.Suspense fallback={null}\u003e\n+           \u003cWarning /\u003e\n+         \u003c/React.Suspense\u003e :\n        )\n      }\n    }\n\n    export default App\n```\n\n# Webpack Best Practices\n\n## Webpack CLI Options\n\nRun Webpack in **watch mode** with\n\n    npx webpack --watch\n\nPrint also hidden modules with\n\n    npx webpack --display-modules\n\nDo not print modules with\n\n    npx webpack --display-max-modules=0\n\n## Source Maps\n\nGenerate **separate source maps** (preferred for **production**) with\n\n```diff\n    module.exports = {\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js'\n      },\n+     devtool: 'source-map'\n    }\n```\n\nand **inline source maps** (ideal for **development** due to their speed) with\n\n```diff\n    module.exports = {\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js'\n      },\n+     devtool: 'cheap-module-eval-source-map'\n    }\n```\n\n## Manage multiple configurations\n\nExtend a base configuration with `webpack-merge`:\n\n    npm i -D webpack-merge\n\nRemove build specific configuration from base\n\n```diff\n    module.exports = {\n-     mode: 'production',\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        filename: 'main.js'\n      }\n    }\n```\n\nand create environment specific configurations like `webpac.config.dev.js`\n\n```diff\n+   const merge = require('webpack-merge')\n+   const base = require('./webpack.config')\n+   module.exports = merge(base, {\n+     mode: 'development',\n+     devtool: 'source-map'\n+   })\n```\n\nThen build with\n\n    npx webpack --config webpack.config.dev.js\n\n## Webpack Development Server\n\nInstall with\n\n    npm i -D webpack-dev-server\n\nand replace `webpack --watch` in the npm script\n\n```diff\n{\n  \"name\": \"on-webpack\",\n  \"scripts\": {\n-   \"dev\": \"webpack --watch --config webpack.config.dev.js\",\n+   \"dev\": \"webpack-dev-server --open --config webpack.config.dev.js\",\n    \"build\": \"webpack --config webpack.config.prod.js\"\n  }\n}\n\n```\n\nor use the CLI with\n\n    npx webpack-dev-server\n\nThe dev server can be extended using the key `devServer` in the webpack configuration:\n\n```diff\n    module.exports = {\n+     devServer: {\n+       port: 9000,\n+       disableHostCheck: true,\n+       headers: {\n+         \"Access-Control-Allow-Origin\": \"*\"\n+       }\n+     }\n    }\n```\n\nIf the page content, e.g. `index.html` is outside the `dist` folder, the set the content base as:\n\n```diff\n    module.exports = {\n      devServer: {\n+       contentBase: '.'\n      }\n    }\n```\n\nWebpack-dev-server does **life-reloading**: the browser is refreshed immediately each time there is a code change. This is a different concept compared to HMR.\n\n## Externalize Dependencies to be Loaded via CDN\n\nTo externalize dependencies from the bundle (e.g. to reduce size) and load them from a CDN, declare these\ndependencies and their variable-names in `webpack.config.prod.js` like this (as an example for externalization of React):\n\n```diff\n    module.exports = merge(base, {\n      mode: 'production',\n      plugins: [\n        new BundleAnalyzerPlugin({\n          analyzerMode: 'static',\n          openAnalyzer: false\n        })\n      ],\n+     externals: {\n+       'react': 'React',\n+       'react-dom': 'ReactDOM'\n+     }\n    })\n```\n\nThen add the dependencies into `./src/index.html` for production (the template syntax `\u003c% ... %\u003e` requires to\nuse the **HTMLWebpackPlugin**):\n\n```diff\n    \u003cbody\u003e\n      \u003cdiv id=\"app\"\u003e\u003c/div\u003e\n+     \u003c% if (process.env.NODE_ENV === 'production') { %\u003e\n+       \u003cscript crossorigin src=\"https://unpkg.com/react@16/umd/react.production.min.js\"\u003e\u003c/script\u003e\n+       \u003cscript crossorigin src=\"https://unpkg.com/react-dom@16/umd/react-dom.production.min.js\"\u003e\u003c/script\u003e\n+     \u003c% } %\u003e\n    \u003c/body\u003e\n```\n\n## Debugging Webpack\n\nEither use the CLI debugger with\n\n    node inspect node_modules/.bin/webpack --config webpack.config.dev.js\n    \u003e sb('webpack.config.js', 3)\n    \u003e c\n\nor use VS Code with the following **Launch Configuration**:\n\n```json\n    \"launch\": {\n      \"configurations\": [{\n        \"type\": \"node\",\n        \"request\": \"launch\",\n        \"name\": \"Launch Webpack\",\n        \"program\": \"${workspaceFolder}/node_modules/webpack/bin/webpack.js\"\n      }]\n    }\n```\n\nOptionally add `args` or `env` like this:\n\n```diff\n    \"launch\": {\n      \"configurations\": [{\n        \"type\": \"node\",\n        \"request\": \"launch\",\n        \"name\": \"Launch Webpack\",\n        \"program\": \"${workspaceFolder}/node_modules/webpack/bin/webpack.js\",\n+       \"args\": [\n+         \"--config\", \"./some/dir/webpack.config.js\"\n+       ],\n+       \"env\" : { \n+         \"NODE_ENV\" : \"production\"\n+       }\n      }]\n    }\n```\n\n\n## Inspect Webpack bundles\n\nUse the **webpack-bundle-analyzer** plugin to generate a tree map chart of the modules within your bundle.\n\nInstall with\n\n    npm i -D webpack-bundle-analyzer\n\nand configure in the production configuration `webpack.config.prod.js`:\n\n```diff\n    const merge = require('webpack-merge')\n    const base = require('./webpack.config')\n+   const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')\n\n    module.exports = merge(base, {\n      mode: 'production',\n+     plugins: [\n+       new BundleAnalyzerPlugin()\n+     ]\n    })\n```\n\nThis starts a local webserver and opens the page automatically.\n\nIf you just like to generate a `./dits/report.html` file instead of a webserver, then change to plugin options to\n\n```diff\n      plugins: [\n-       new BundleAnalyzerPlugin()\n+       new BundleAnalyzerPlugin({\n+         analyzerMode: 'static',\n+         openAnalyzer: false\n+       })\n      ]\n```\n\nand open with\n\n    open dist/report.html\n\nAn Alternative which does not need to integrate a plugin, is to save the webpack output to JSON\n\n    npm run -s build -- --json \u003e stats.json\n\nand drag-drop this file into the [webpack-visualizer](https://chrisbateman.github.io/webpack-visualizer/) site.\n\n### inspectpack(1)\n\n`inspectpack` is an inspection tool to detect **version skews**, **duplicate files** and opportunities to **reduce file sizes**.\n\n    npm run -s build -- --json \u003e stats.json\n    npx inspectpack -s stats.json -a versions\n    npx inspectpack -s stats.json -a duplicates\n    npx inspectpack -s stats.json -a sizes\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomd%2Fon-webpack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomd%2Fon-webpack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomd%2Fon-webpack/lists"}