├── .github
└── FUNDING.yml
├── .gitignore
├── README.md
├── asset-modules
└── README.md
├── babel-polyfill
├── README.md
├── dist
│ ├── main.js
│ ├── src_bar_js.js
│ ├── src_foo_js.js
│ └── vendors-node_modules_core-js_modules_es_set_js.js
├── package-lock.json
├── package.json
├── src
│ ├── bar.js
│ ├── foo.js
│ └── index.js
└── webpack.config.js
├── babel
├── README.md
├── dist
│ ├── main.js
│ └── src_lazy-load_js.js
├── package-lock.json
├── package.json
├── src
│ ├── index.js
│ ├── lazy-load.js
│ └── tree-shaking.js
└── webpack.config.js
├── bundle
├── README.md
├── dist
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ ├── a.js
│ ├── b.js
│ ├── index.js
│ └── shared-component.js
└── webpack.config.js
├── caching
├── README.md
├── dist
│ ├── assets
│ │ └── alice.73728f89fad1b84b9a3d.jpg
│ ├── css
│ │ ├── main.4a126fcfbbf8336080a5.css
│ │ └── main.4a126fcfbbf8336080a5.css.map
│ ├── index.html
│ └── js
│ │ ├── main.8a76ca7e8337f1fd02ec.js
│ │ └── main.8a76ca7e8337f1fd02ec.js.map
├── package-lock.json
├── package.json
├── src
│ ├── alice.jpg
│ ├── index.html
│ ├── index.js
│ └── style.css
└── webpack.config.js
├── chunks-types
└── README.md
├── code-splitting
├── README.md
├── dist
│ ├── main.js
│ ├── src_a_js.js
│ ├── src_b_js.js
│ └── vendors-node_modules_react_index_js.js
├── package-lock.json
├── package.json
├── src
│ ├── a.js
│ ├── b.js
│ ├── index.js
│ ├── large-component.js
│ └── small-component.js
└── webpack.config.js
├── composing-configs
├── README.md
├── configs
│ ├── const.js
│ ├── webpack.config.js
│ ├── webpack.dev.js
│ ├── webpack.parts.js
│ ├── webpack.prod.js
│ └── webpack.serve.js
├── dist
│ ├── css
│ │ ├── main.1af36816c458bacb01d8.css
│ │ └── main.1af36816c458bacb01d8.css.map
│ ├── index.html
│ └── js
│ │ ├── main.9d57fd1eba571e7bf8e8.js
│ │ └── main.9d57fd1eba571e7bf8e8.js.map
├── package-lock.json
├── package.json
└── src
│ ├── index.html
│ ├── index.js
│ ├── style.css
│ └── var.css
├── context-module
├── README.md
├── dist
│ ├── 0f7001a62be1d14e6b63.jpg
│ ├── 73728f89fad1b84b9a3d.jpg
│ ├── index.html
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ ├── images
│ │ ├── alice.jpg
│ │ └── nebula.jpg
│ ├── index.html
│ ├── index.js
│ └── load-images.js
└── webpack.config.js
├── debug-webpack
└── README.md
├── dynamic-import
├── README.md
├── dist
│ ├── index.html
│ ├── main.js
│ ├── src_modules_module-1_js.js
│ └── src_modules_module-2_js.js
├── package-lock.json
├── package.json
├── src
│ ├── index.html
│ ├── index.js
│ ├── lazy-load-module.js
│ └── modules
│ │ ├── module-1.js
│ │ └── module-2.js
└── webpack.config.js
├── html-plugin-template
├── README.md
├── dist
│ ├── index.html
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ └── index.js
├── template-utils.js
├── template.ejs
└── webpack.config.js
├── lazy-load-image
├── README.md
├── dist
│ ├── 0f7001a62be1d14e6b63.jpg
│ ├── index.html
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ ├── index.js
│ └── nebula.jpg
└── webpack.config.js
├── lazy-load-multiple-images-folders
├── README.md
├── dist
│ ├── 08b5bae29ab24ce24487.jpg
│ ├── 0c9627955a614a979a10.jpg
│ ├── 0f7001a62be1d14e6b63.jpg
│ ├── 73728f89fad1b84b9a3d.jpg
│ ├── index.html
│ ├── main.js
│ └── src_buildImages_js.js
├── package-lock.json
├── package.json
├── src
│ ├── buildImages.js
│ ├── getImagesNames.js
│ ├── images
│ │ ├── 1
│ │ │ ├── alice.jpg
│ │ │ └── nebula.jpg
│ │ └── 2
│ │ │ ├── doré-inferno-dante.jpg
│ │ │ └── waterhouse-miranda.jpg
│ ├── index.html
│ └── index.js
└── webpack.config.js
├── lazy-load-multiple-images
├── README.md
├── dist
│ ├── 0c9627955a614a979a10.jpg
│ ├── 0f7001a62be1d14e6b63.jpg
│ ├── 73728f89fad1b84b9a3d.jpg
│ ├── index.html
│ ├── main.js
│ └── src_images_js.js
├── package-lock.json
├── package.json
├── src
│ ├── generateImage.js
│ ├── getImagesNames.js
│ ├── images.js
│ ├── images
│ │ ├── alice.jpg
│ │ ├── inferno.jpg
│ │ └── nebula.jpg
│ ├── index.html
│ ├── index.js
│ └── lazyLoadImage.js
└── webpack.config.js
├── load-css
├── README.md
├── dist
│ ├── index.html
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ ├── index.html
│ ├── index.js
│ └── style.css
└── webpack.config.js
├── load-images
├── README.md
├── dist
│ ├── 0c9627955a614a979a10.jpg
│ ├── 0f7001a62be1d14e6b63.jpg
│ ├── 73728f89fad1b84b9a3d.jpg
│ ├── index.html
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ ├── alice.jpg
│ ├── index.html
│ ├── index.js
│ ├── inferno.jpg
│ ├── nebula.jpg
│ ├── style.css
│ └── sun.svg
└── webpack.config.js
├── loaders-plugins
└── README.md
├── minify
├── README.md
├── dist
│ ├── index.html
│ ├── main.css
│ ├── main.css.map
│ ├── main.js
│ └── main.js.map
├── package-lock.json
├── package.json
├── src
│ ├── index.html
│ ├── index.js
│ └── style.css
└── webpack.config.js
├── mode
├── README.md
├── dist
│ └── main.js
├── package-lock.json
├── package.json
└── src
│ └── index.js
├── multiple-configurations
├── README.md
├── dist
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ └── index.js
└── webpack.config.js
├── postcss
├── README.md
├── dist
│ ├── index.html
│ ├── main.css
│ ├── main.css.map
│ ├── main.js
│ └── main.js.map
├── package-lock.json
├── package.json
├── src
│ ├── index.html
│ ├── index.js
│ ├── partials
│ │ └── var.css
│ └── style.css
└── webpack.config.js
├── publicpath
└── README.md
├── remove-unused-css
├── README.md
├── dist
│ ├── index.html
│ ├── main.css
│ ├── main.js
│ └── main.js.map
├── package-lock.json
├── package.json
├── src
│ ├── h1.js
│ ├── index.html
│ ├── index.js
│ ├── style.css
│ └── var.css
└── webpack.config.js
├── sass
├── README.md
├── dist
│ ├── 0f7001a62be1d14e6b63.jpg
│ ├── index.html
│ ├── main.css
│ ├── main.css.map
│ ├── main.js
│ └── main.js.map
├── package-lock.json
├── package.json
├── src
│ ├── images
│ │ └── nebula.jpg
│ ├── index.html
│ ├── index.js
│ └── scss
│ │ ├── partials
│ │ ├── image-nebula.scss
│ │ └── mixin.scss
│ │ └── style.scss
└── webpack.config.js
├── separate-css
├── README.md
├── dist
│ ├── index.html
│ ├── main.css
│ ├── main.css.map
│ ├── main.js
│ └── main.js.map
├── package-lock.json
├── package.json
├── src
│ ├── index.html
│ ├── index.js
│ ├── style.css
│ └── var.css
└── webpack.config.js
├── separate-runtime
├── README.md
├── dist
│ ├── main.js
│ └── runtime.js
├── package-lock.json
├── package.json
├── src
│ └── index.js
└── webpack.config.js
├── source-maps
├── README.md
├── dist
│ ├── index.html
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ ├── index.html
│ ├── index.js
│ ├── style.css
│ └── var.css
└── webpack.config.js
├── static-site-generator
├── README.md
├── dist
│ ├── about
│ │ └── index.html
│ ├── contact
│ │ └── index.html
│ ├── index.html
│ └── main.js
├── html-beautify-webpack-plugin.js
├── package-lock.json
├── package.json
├── readFiles.js
├── src
│ ├── index.js
│ └── pages
│ │ ├── about.js
│ │ ├── contact.js
│ │ ├── home.js
│ │ └── partials
│ │ └── nav.js
├── template-utils.js
├── template.ejs
└── webpack.config.js
└── tree-shaking
├── README.md
├── dist
└── main.js
├── package-lock.json
├── package.json
├── src
├── index.js
└── tree-shaking.js
└── webpack.config.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: https://www.paypal.me/lucapoldelmengo
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Dd]esktop.ini
2 | $RECYCLE.BIN
3 | node_modules
4 | .vscode
5 | .notes
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Learn Webpack
2 |
3 | A collection of simple [Webpack](https://webpack.js.org/) examples:
4 |
5 | - [Mode](./mode/README.md): Enable webpack built-in optimizations for development and production.
6 | - [Multiple Configurations](./multiple-configurations/README.md): Use `--env` flag to create different config based on the environment.
7 | - [Bundle](./bundle/README.md): Merge multiple javascripts files.
8 | - [Separate Runtime](./separate-runtime/README.md): Extract webpack runtime logic in a separate file.
9 | - [Tree Shaking](./tree-shaking/README.md): Remove unused javascript codes from the app.
10 | - [Chunks Types](./chunks-types/README.md): Explain what a webpack chunk is.
11 | - [Dynamic Import](./dynamic-import/README.md): Use `import()` to load part of an app on demand.
12 | - [Code Splitting](./code-splitting/README.md): Divide an app into smaller files.
13 | - [Loaders and Plugins](./loaders-plugins/README.md): Explain what webpack loaders and plugins are.
14 | - [Asset Modules](./asset-modules/README.md): Import additional type of assets without configuring loaders.
15 | - [Babel](./babel/README.md): Use `babel-loader` to transpile ES2015 code into into a backwards compatible version of javascript.
16 | - [Babel Polyfill](./babel-polyfill/README.md): Apply pollyfills to provide a backwards compatible version of a javascript feature.
17 | - [Html Plugin Template](./html-plugin-template/README.md): Generate html files with `html-webpack-plugin`.
18 | - [Load CSS](./load-css/README.md): Use `css-loader` and `style-loader` to parse stylesheets and place them into html page.
19 | - [Source Maps](./source-maps/README.md): Emit javascript and css source maps.
20 | - [Public Path](./publicpath/README.md): Specify a base path for all the assets within your applicaion.
21 | - [Separate CSS](./separate-css/README.md): Use `mini-css-extract-plugin` to extract stylesheets into a separate file.
22 | - [Remove Unused CSS](./remove-unused-css/README.md): Use `purgecss` to remove unused part of stylesheets.
23 | - [Load Images](./load-images/README.md): Use `asset/resource` to parse and emit images and `asset/source` to parse raw svg files.
24 | - [SASS](./sass/README.md): Load sass files with `sass-loader`.
25 | - [PostCSS](./postcss/README.md): Enable postcss with `postcss-loader`.
26 | - [Minify](./minify/README.md): Enable code minification by using `mode.production`, `css-minimizer-webpack-plugin` and `HtmlWebpackPlugin.minify`.
27 | - [Caching](./caching/README.md): Setup client level caching by adding hash to filenames.
28 | - [Context Module](./context-module/README.md): Run `require.context()` at compile time to import assets.
29 | - [Debug Webpack](./debug-webpack/README.md): Debug webpack configuration using nodejs `--inspect` tool.
30 | - [Lazy Load Image](./lazy-load-image/README.md): Dynamically import an image with `import()`.
31 | - [Lazy Load Multiple Images](./lazy-load-multiple-images/README.md): Use `require.context()` to include all images from a folder and dynamically load them with `import()`.
32 | - [Lazy Load Multiple Images Folders](./lazy-load-multiple-images-folders/README.md): Dynamically import images inside multiple folders when a button is clicked.
33 | - [Composing Configurations](./composing-configs/README.md): Organize webpack configs in separate files and merge them with `webpack-merge`.
34 | - [Static Site Generator](./static-site-generator/README.md): Create a simple SSG on top of `HtmlWebpackPlugin`.
35 |
--------------------------------------------------------------------------------
/asset-modules/README.md:
--------------------------------------------------------------------------------
1 | # Asset Modules
2 |
3 | By default webpack only recognize js and json files. From webpack v5 loading additional type of assets (images, icons, fonts, text, and more) can be done using [asset modules](https://webpack.js.org/guides/asset-modules/) without configuring loaders:
4 |
5 | - `asset/resource` emits a separate file and exports the URL. Previously achievable by using [file-loader](https://v4.webpack.js.org/loaders/file-loader/).
6 | - `asset/inline` exports a data URI of the asset. Previously achievable by using [url-loader](https://v4.webpack.js.org/loaders/url-loader/).
7 | - `asset/source` exports the source code of the asset. Previously achievable by using [raw-loader](https://v4.webpack.js.org/loaders/raw-loader/).
8 | - `asset` automatically chooses between exporting a data URI and emitting a separate file. Previously achievable by using url-loader with asset size limit.
9 |
10 | ## Futher reading
11 |
12 | [load-images](../load-images/README.md)
13 |
--------------------------------------------------------------------------------
/babel-polyfill/README.md:
--------------------------------------------------------------------------------
1 | # Load polyfills with Babel
2 |
3 | A [polyfill](https://en.wikipedia.org/wiki/Polyfill_(programming)) is a pice of code that is used to provide a backwards compatible version of a feature. In web development a polyfill allow you to enable a modern feature to work in older browsers that do not support it natively.
4 |
5 | Polyfills should be loaded and executed before the feature you want to patch. You can import polyfills on top of your app, or if you use webpack you can set them to be the first entry point, or to maximize performance you can lazy load them.
6 |
7 | In this example [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env) and [core-js](https://github.com/zloirock/core-js) are used to load polyfills.
8 |
9 | ## Testing different babel configurations
10 |
11 | - Set [useBuiltIns](https://babeljs.io/docs/en/babel-preset-env#usebuiltins) to configure how babel handles polyfills.
12 | - Select the browsers you want to target with [browserslist](https://github.com/browserslist/browserslist).
13 | - `npm install core-js --save`.
14 | - Import all `core-js` library or specific part of it.
15 |
16 | All the builds below are compiled in development mode by running `npm run dev`.
17 |
18 | ### Load all polyfills for specific browsers
19 |
20 | - Set `useBuiltIns` to `"entry"` which will add all polyfills of the targeting browsers.
21 | - Import core-js `import "core-js/stable"` in your app before the feature you want to patch (e.g. import it at top of main entry point), remember to import it only once in your app to avoid duplications.
22 |
23 | If you set `"browserslist": "last 2 Chrome versions"` and run the build:
24 |
25 | ```txt
26 | asset main.js 145 KiB [emitted] (name: main)
27 | asset src_bar_js.js 322 bytes [emitted]
28 | asset src_foo_js.js 322 bytes [emitted]
29 | ```
30 |
31 | *main.js* contains polyfills for the last 2 chrome versions.
32 |
33 | If you set `"browserslist": "ie 11"` and run the build:
34 |
35 | ```txt
36 | asset main.js 860 KiB [emitted] (name: main)
37 | asset src_bar_js.js 337 bytes [emitted]
38 | asset src_foo_js.js 337 bytes [emitted]
39 | ```
40 |
41 | *main.js* contains polyfills for ie 11, babel also transpile ES6 syntax inside *src_bar_js.js* and *src_foo_js.js*.
42 |
43 | ### Automatically polyfills only used features
44 |
45 | - Set `useBuiltIns` to `"usage"` which will add specific imports for polyfills only for the features that are used in the app.
46 | - Remove `import "core-js/stable"` because babel will handles imports.
47 |
48 | If you set `browserslist: "last 2 Chrome versions"` and run the build:
49 |
50 | ```txt
51 | asset main.js 12.7 KiB [emitted] (name: main)
52 | asset src_bar_js.js 322 bytes [compared for emit]
53 | asset src_foo_js.js 322 bytes [compared for emit]
54 | ```
55 |
56 | The last 2 chrome versions support `Promise` and `Set` therefor polyfill are not emitted.
57 |
58 | If you set `"browserslist": "ie 11"` and run the build:
59 |
60 | ```txt
61 | asset main.js 177 KiB [emitted] (name: main)
62 | asset vendors-node_modules_core-js_modules_es_set_js.js 28.2 KiB [emitted] (id hint: vendo
63 | rs)
64 | asset src_bar_js.js 2.88 KiB [emitted]
65 | asset src_foo_js.js 2.88 KiB [emitted]
66 | ```
67 |
68 | ### Manually import polyfills
69 |
70 | To avoid loading unecessary polyfills you can import them manually, for example by adding a new webpack entry point `polyfills: ["core-js/features/promise", "core-js/features/set]`.
71 |
72 | ## Notes
73 |
74 | By default [core-js sets polyfills only when they are required](https://github.com/zloirock/core-js#configurable-level-of-aggressiveness).
75 |
76 | If you want to polyfill generators and async function use [regenerator-runtime](https://www.npmjs.com/package/regenerator-runtime) (read [corejs#babelpolyfill](https://github.com/zloirock/core-js#babelpolyfill) for more info).
77 |
78 | ## Tips
79 |
80 | You can run `npx browserslist` to get a list of browsers your project supports.
81 |
82 | ## Alternative method for loading polyfills
83 |
84 | Try [polyfill.io](https://polyfill.io/) service, which accepts a request for a set of browser features and returns only the polyfills that are needed by the requesting browser.
85 |
86 | ## Further reading
87 |
88 | - [SurviveJS - Polyfilling Features](https://survivejs.com/webpack/loading/javascript/#polyfilling-features)
89 | - [Webpack Guides - Shimming Loading Polyfills](https://webpack.js.org/guides/shimming/#loading-polyfills)
90 |
--------------------------------------------------------------------------------
/babel-polyfill/dist/src_bar_js.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | (self["webpackChunk"] = self["webpackChunk"] || []).push([["src_bar_js"],{
3 |
4 | /***/ "./src/bar.js":
5 | /*!********************!*\
6 | !*** ./src/bar.js ***!
7 | \********************/
8 | /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
9 |
10 | __webpack_require__.r(__webpack_exports__);
11 | /* harmony import */ var core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es.object.to-string.js */ "./node_modules/core-js/modules/es.object.to-string.js");
12 | /* harmony import */ var core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_0__);
13 | /* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/es.promise.js */ "./node_modules/core-js/modules/es.promise.js");
14 | /* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1__);
15 | /* harmony import */ var core_js_modules_es_array_iterator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! core-js/modules/es.array.iterator.js */ "./node_modules/core-js/modules/es.array.iterator.js");
16 | /* harmony import */ var core_js_modules_es_array_iterator_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_array_iterator_js__WEBPACK_IMPORTED_MODULE_2__);
17 | /* harmony import */ var core_js_modules_es_set_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! core-js/modules/es.set.js */ "./node_modules/core-js/modules/es.set.js");
18 | /* harmony import */ var core_js_modules_es_set_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_set_js__WEBPACK_IMPORTED_MODULE_3__);
19 | /* harmony import */ var core_js_modules_es_string_iterator_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! core-js/modules/es.string.iterator.js */ "./node_modules/core-js/modules/es.string.iterator.js");
20 | /* harmony import */ var core_js_modules_es_string_iterator_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_string_iterator_js__WEBPACK_IMPORTED_MODULE_4__);
21 | /* harmony import */ var core_js_modules_web_dom_collections_iterator_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! core-js/modules/web.dom-collections.iterator.js */ "./node_modules/core-js/modules/web.dom-collections.iterator.js");
22 | /* harmony import */ var core_js_modules_web_dom_collections_iterator_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_collections_iterator_js__WEBPACK_IMPORTED_MODULE_5__);
23 |
24 |
25 |
26 |
27 |
28 |
29 | console.log("bar.js loaded!");
30 | Promise.resolve(99).then(function (x) {
31 | return console.log(x);
32 | });
33 | new Set([1, 2, 3, 2, 1]);
34 |
35 | /***/ })
36 |
37 | }]);
--------------------------------------------------------------------------------
/babel-polyfill/dist/src_foo_js.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | (self["webpackChunk"] = self["webpackChunk"] || []).push([["src_foo_js"],{
3 |
4 | /***/ "./src/foo.js":
5 | /*!********************!*\
6 | !*** ./src/foo.js ***!
7 | \********************/
8 | /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
9 |
10 | __webpack_require__.r(__webpack_exports__);
11 | /* harmony import */ var core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es.object.to-string.js */ "./node_modules/core-js/modules/es.object.to-string.js");
12 | /* harmony import */ var core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_0__);
13 | /* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/es.promise.js */ "./node_modules/core-js/modules/es.promise.js");
14 | /* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_1__);
15 | /* harmony import */ var core_js_modules_es_array_iterator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! core-js/modules/es.array.iterator.js */ "./node_modules/core-js/modules/es.array.iterator.js");
16 | /* harmony import */ var core_js_modules_es_array_iterator_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_array_iterator_js__WEBPACK_IMPORTED_MODULE_2__);
17 | /* harmony import */ var core_js_modules_es_set_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! core-js/modules/es.set.js */ "./node_modules/core-js/modules/es.set.js");
18 | /* harmony import */ var core_js_modules_es_set_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_set_js__WEBPACK_IMPORTED_MODULE_3__);
19 | /* harmony import */ var core_js_modules_es_string_iterator_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! core-js/modules/es.string.iterator.js */ "./node_modules/core-js/modules/es.string.iterator.js");
20 | /* harmony import */ var core_js_modules_es_string_iterator_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_string_iterator_js__WEBPACK_IMPORTED_MODULE_4__);
21 | /* harmony import */ var core_js_modules_web_dom_collections_iterator_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! core-js/modules/web.dom-collections.iterator.js */ "./node_modules/core-js/modules/web.dom-collections.iterator.js");
22 | /* harmony import */ var core_js_modules_web_dom_collections_iterator_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_web_dom_collections_iterator_js__WEBPACK_IMPORTED_MODULE_5__);
23 |
24 |
25 |
26 |
27 |
28 |
29 | console.log("foo.js loaded!");
30 | Promise.resolve(42).then(function (x) {
31 | return console.log(x);
32 | });
33 | new Set([4, 5, 6, 5, 4]);
34 |
35 | /***/ })
36 |
37 | }]);
--------------------------------------------------------------------------------
/babel-polyfill/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod"
5 | },
6 | "homepage": "https://github.com/pldg/learn-webpack",
7 | "license": "MIT",
8 | "devDependencies": {
9 | "@babel/core": "^7.18.6",
10 | "@babel/preset-env": "^7.18.6",
11 | "babel-loader": "^8.2.5",
12 | "webpack": "^5.73.0",
13 | "webpack-cli": "^4.10.0"
14 | },
15 | "dependencies": {
16 | "core-js": "^3.23.3"
17 | },
18 | "browserslist": [
19 | "ie 11"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/babel-polyfill/src/bar.js:
--------------------------------------------------------------------------------
1 | console.log("bar.js loaded!");
2 |
3 | Promise.resolve(99).then((x) => console.log(x));
4 | new Set([1, 2, 3, 2, 1]);
5 |
--------------------------------------------------------------------------------
/babel-polyfill/src/foo.js:
--------------------------------------------------------------------------------
1 | console.log("foo.js loaded!");
2 |
3 | Promise.resolve(42).then((x) => console.log(x));
4 | new Set([4, 5, 6, 5, 4]);
5 |
--------------------------------------------------------------------------------
/babel-polyfill/src/index.js:
--------------------------------------------------------------------------------
1 | // Import core-js only once in your app
2 | // Use this only with `useBuiltIns: "entry"`
3 | //import "core-js/stable";
4 |
5 | import("./foo");
6 | import("./bar");
7 |
8 | console.log("index.js loaded!");
9 |
--------------------------------------------------------------------------------
/babel-polyfill/webpack.config.js:
--------------------------------------------------------------------------------
1 | const pkg = require("./package.json");
2 |
3 | module.exports = (env) => {
4 | const isProd = env.target === "prod";
5 |
6 | return {
7 | output: {
8 | clean: true,
9 | },
10 | mode: isProd ? "production" : "development",
11 | devtool: false,
12 | module: {
13 | rules: [
14 | {
15 | test: /\.m?js$/,
16 | exclude: /node_modules/,
17 | use: {
18 | loader: "babel-loader",
19 | options: {
20 | presets: [
21 | [
22 | "@babel/preset-env",
23 | {
24 | useBuiltIns: "usage",
25 | // Important: Specify core-js **minor** version
26 | // https://github.com/zloirock/core-js#babelpreset-env
27 | corejs: pkg.dependencies["core-js"],
28 | },
29 | ],
30 | ],
31 | },
32 | },
33 | },
34 | ],
35 | },
36 | };
37 | };
38 |
--------------------------------------------------------------------------------
/babel/README.md:
--------------------------------------------------------------------------------
1 | # Transpile javascript with Babel
2 |
3 | [Babel](https://babeljs.io/) is a tool to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript. It transform new syntax into old one, apply polyfills, and more.
4 |
5 | In webpack use [babel-loader](https://github.com/babel/babel-loader) to transpile ES2015+ code to support older browsers.
6 |
7 | *babel.config.js* handle Babel configuration.
8 |
9 | You'll need [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env.html) to be able to transpile your javascript code. It can be configured to include only transforms needed for the browsers you want to support. It can also be set to load the needed polyfills (see [babel-polyfill](../babel-polyfill) example).
10 |
11 | By setting [browserslist](https://github.com/browserslist/browserslist) you can choose which browser to target for your final bundle. In this example it's set inside *package.json*.
12 |
13 | ## Explain this example
14 |
15 | Read comments inside *src/\*js* files. Run the build `npm run dev` and inspect results inside *dist* folder.
16 |
17 | ## Notes
18 |
19 | [Tree shaking](../tree-shaking) should works by default when running in *production mode*, if there are problems you can set babel [modules](https://babeljs.io/docs/en/next/babel-preset-env#modules) option to `false`.
20 |
21 | You may also want to use [babel-plugin-transform-imports](https://www.npmjs.com/package/babel-plugin-transform-imports) to load only specific part of an ES2015 library.
22 |
23 | If you have problem with [dynamic import](../dynamic-import) try to add [syntax-dynamic-import](https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import) Babel plugin.
24 |
--------------------------------------------------------------------------------
/babel/dist/src_lazy-load_js.js:
--------------------------------------------------------------------------------
1 | (self["webpackChunk"] = self["webpackChunk"] || []).push([["src_lazy-load_js"],{
2 |
3 | /***/ "./src/lazy-load.js":
4 | /*!**************************!*\
5 | !*** ./src/lazy-load.js ***!
6 | \**************************/
7 | /***/ (function() {
8 |
9 | console.log("lazy-load.js loaded!");
10 |
11 | /***/ })
12 |
13 | }]);
--------------------------------------------------------------------------------
/babel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod"
5 | },
6 | "homepage": "https://github.com/pldg/learn-webpack",
7 | "license": "MIT",
8 | "devDependencies": {
9 | "@babel/core": "^7.18.6",
10 | "@babel/preset-env": "^7.18.6",
11 | "babel-loader": "^8.2.5",
12 | "webpack": "^5.73.0",
13 | "webpack-cli": "^4.10.0"
14 | },
15 | "browserslist": [
16 | "ie 11"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/babel/src/index.js:
--------------------------------------------------------------------------------
1 | // Test if tree-shaking works with Babel
2 | // Note: it should work only when build in *production mode*
3 | import { foo } from "./tree-shaking";
4 |
5 | foo();
6 |
7 | // Test if Babel support dynamic import, it should output a separate file
8 | import("./lazy-load");
9 |
10 | // The `const` keyword and the arrow function should be transpiled by Babel
11 | // because "browserlist" is set to "ie 11", if you set it to "default" they'll
12 | // not be transpiled because newer browser support those sytax
13 | const p = new Promise((resolve) => resolve());
14 |
15 | console.log("index.js loaded!");
16 |
--------------------------------------------------------------------------------
/babel/src/lazy-load.js:
--------------------------------------------------------------------------------
1 | console.log("lazy-load.js loaded!");
2 |
--------------------------------------------------------------------------------
/babel/src/tree-shaking.js:
--------------------------------------------------------------------------------
1 | const foo = () => console.log("foo!");
2 | const bar = () => console.log("bar!");
3 |
4 | export { foo, bar };
5 |
--------------------------------------------------------------------------------
/babel/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = (env) => {
2 | const isProd = env.target === "prod";
3 |
4 | return {
5 | output: {
6 | clean: true,
7 | },
8 | mode: isProd ? "production" : "development",
9 | devtool: false,
10 | module: {
11 | rules: [
12 | {
13 | test: /\.js$/,
14 | exclude: /node_modules/,
15 | use: {
16 | loader: "babel-loader",
17 | options: {
18 | presets: [["@babel/preset-env"]],
19 | },
20 | },
21 | },
22 | ],
23 | },
24 | };
25 | };
26 |
--------------------------------------------------------------------------------
/bundle/README.md:
--------------------------------------------------------------------------------
1 | # Bundle
2 |
3 | You can use webpack to merge multiple javascripts files into one single bundle. The output of the final bundle depends on webpack configuration (the [mode](../mode/README.md) option will load the default optimizations).
4 |
5 | ## Explain this example
6 |
7 | We expect the code inside *shared-component.js* to not be duplicated even if it's imported inside *a.js* and *b.js*. This will work on both development and production modes.
8 |
9 | Run `npm run prod` for production build:
10 |
11 | ```js
12 | (()=>{var o={42:()=>{console.log("shared-component.js loaded!")}},e={};function s(r){var l=e[r];if(void 0!==l)return l.exports;var d=e[r]={exports:{}};return o[r](d,d.exports,s),d.exports}(()=>{"use strict";s(42),console.log("a.js loaded!"),console.log("b.js loaded!"),console.log("index.js loaded!")})()})();
13 | ```
14 |
15 | ## Notes
16 |
17 | In this example [`env`](https://webpack.js.org/guides/environment-variables/) is used to load two different webpack configurations, one for production (`npm run prod`) and one for development (`npm run dev`), for more info see [multiple-configurations](../multiple-configurations/README.md) example.
18 |
19 | ## Tips
20 |
21 | To make the output cleaner to examine set [`devtool: false`](https://webpack.js.org/configuration/devtool/) (webpack generate eval based source maps by default and doing this will disable the behavior).
22 |
23 | From webpack 5.20 you can use [clean](https://webpack.js.org/configuration/output/#outputclean) option to clean the output directory before emit.
24 |
--------------------------------------------------------------------------------
/bundle/dist/main.js:
--------------------------------------------------------------------------------
1 | (()=>{var o={42:()=>{console.log("shared-component.js loaded!")}},e={};function s(r){var l=e[r];if(void 0!==l)return l.exports;var d=e[r]={exports:{}};return o[r](d,d.exports,s),d.exports}(()=>{"use strict";s(42),console.log("a.js loaded!"),console.log("b.js loaded!"),console.log("index.js loaded!")})()})();
--------------------------------------------------------------------------------
/bundle/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod"
5 | },
6 | "homepage": "https://github.com/pldg/learn-webpack",
7 | "license": "MIT",
8 | "devDependencies": {
9 | "webpack": "^5.73.0",
10 | "webpack-cli": "^4.10.0"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/bundle/src/a.js:
--------------------------------------------------------------------------------
1 | import './shared-component';
2 |
3 | console.log('a.js loaded!');
--------------------------------------------------------------------------------
/bundle/src/b.js:
--------------------------------------------------------------------------------
1 | import './shared-component';
2 |
3 | console.log('b.js loaded!');
--------------------------------------------------------------------------------
/bundle/src/index.js:
--------------------------------------------------------------------------------
1 | import './a';
2 | import './b';
3 |
4 | console.log('index.js loaded!');
--------------------------------------------------------------------------------
/bundle/src/shared-component.js:
--------------------------------------------------------------------------------
1 | console.log('shared-component.js loaded!');
--------------------------------------------------------------------------------
/bundle/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = (env) => {
2 | const isProd = env.target === "prod";
3 |
4 | return {
5 | output: {
6 | // Clean the output directory before emit
7 | clean: true,
8 | },
9 | // Make development output cleaner by removing source map
10 | devtool: false,
11 | // Run different webpack configurations based on environment
12 | mode: isProd ? "production" : "development",
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/caching/README.md:
--------------------------------------------------------------------------------
1 | # Caching
2 |
3 | When an user visit a website for the first time a browser save the assets in its memory and won't download them again (unless filenames has changed). This technique is called [caching](https://en.wikipedia.org/wiki/Cache_(computing)), it's useful to prevent unecessary network traffic and therefor speed up connections to websites.
4 |
5 | If you want the browser to download the latest version of the website you need to control the cache. You can [control caching via server configuration](https://stackoverflow.com/questions/49547/how-do-we-control-web-page-caching-across-all-browsers) or [setup a client level caching using webpack](https://webpack.js.org/guides/caching/#root).
6 |
7 | ## Client level caching
8 |
9 | Client level caching is done by adding an hash to filename.
10 |
11 | Webpack use [placeholders](https://webpack.js.org/configuration/output/#template-strings) to add specific information to webpack output:
12 |
13 | - `[name]` Only filename without extension or path.
14 | - `[ext]` Extension with leading `.`.
15 | - `[id]` The name of the chunk, if set, otherwise the id of the chunk, or id of the module.
16 | - `[contenthash]` Hash generated based on content (the hash change when the file change).
17 | - `[querry]` Querry with leading `?`.
18 |
19 | ## Configure webpack for caching
20 |
21 | ### [Add hash to javascript](https://webpack.js.org/configuration/output/#outputfilename)
22 |
23 | ```js
24 | {
25 | output: {
26 | filename: "js/[name].[contenthash].js"
27 | }
28 | }
29 | ```
30 |
31 | For on-demand-loaded chunks use [`output.chunkFilename`](https://webpack.js.org/configuration/output/#outputchunkfilename).
32 |
33 | ### [Add hash to assets](https://webpack.js.org/guides/asset-modules/#custom-output-filename)
34 |
35 | ```js
36 | {
37 | output: {
38 | assetModuleFilename: 'assets/[name].[contenthash][ext][query]'
39 | }
40 | }
41 | ```
42 |
43 | Or you can configure hash for specific asset type:
44 |
45 | ```js
46 | {
47 | test: /\.html/,
48 | type: 'asset/resource',
49 | generator: {
50 | filename: 'static/[name].[contenthash][ext][query]'
51 | }
52 | }
53 | ```
54 |
55 | ### [Add hash to css](https://github.com/webpack-contrib/mini-css-extract-plugin#advanced-configuration-example)
56 |
57 | ```js
58 | plugins: [
59 | new MiniCssExtractPlugin({
60 | filename: "css/[name].[contenthash].css"
61 | })
62 | ]
63 | ```
64 |
65 | ### Output
66 |
67 | Webpack will generates filenames like these:
68 |
69 | ```txt
70 | js/main.d587bbd6e38337f5accd.js
71 | assets/alice.0c9627955a614a979a10.jpg
72 | static/index.0f7001a62be1d14e6b63.html
73 | css/main.73728f89fad1b84b9a3d.css
74 | ```
75 |
76 | Everytime a file change webpack see it and change the hash in the output filename. This invalidate browser cache, therefor the client (browser) will download the new version of the file.
77 |
78 | ## Tips
79 |
80 | You can set a specific lenght for an hash, for example `[contenthash:6]` will limit the hash lenght to 6 characters.
81 |
82 | Caching is very usefull in conjunction with [bundle splitting](../bundle-splitting/README.md) technique. You can split your app into multiple files and deploy only those whose content has changed.
83 |
84 | Do not use hash while developing.
85 |
86 | ## Further reading
87 |
88 | - [Webpack Guides - Caching](https://webpack.js.org/guides/caching/)
89 | - [SurviveJS - Adding Hash to Filenames](https://survivejs.com/webpack/optimizing/adding-hashes-to-filenames/)
90 |
--------------------------------------------------------------------------------
/caching/dist/assets/alice.73728f89fad1b84b9a3d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/caching/dist/assets/alice.73728f89fad1b84b9a3d.jpg
--------------------------------------------------------------------------------
/caching/dist/css/main.4a126fcfbbf8336080a5.css:
--------------------------------------------------------------------------------
1 | /*!*********************************************************************************************!*\
2 | !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].use[1]!./src/style.css ***!
3 | \*********************************************************************************************/
4 | body {
5 | background-color: lightblue;
6 | }
7 |
8 | img {
9 | display: block;
10 | max-width: 20vw;
11 | }
12 |
13 | /*# sourceMappingURL=main.4a126fcfbbf8336080a5.css.map*/
--------------------------------------------------------------------------------
/caching/dist/css/main.4a126fcfbbf8336080a5.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"css/main.4a126fcfbbf8336080a5.css","mappings":";;;AAAA;EACE,2BAA2B;AAC7B;;AAEA;EACE,cAAc;EACd,eAAe;AACjB,C","sources":["webpack:///./src/style.css"],"sourcesContent":["body {\r\n background-color: lightblue;\r\n}\r\n\r\nimg {\r\n display: block;\r\n max-width: 20vw;\r\n}"],"names":[],"sourceRoot":""}
--------------------------------------------------------------------------------
/caching/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/caching/dist/js/main.8a76ca7e8337f1fd02ec.js:
--------------------------------------------------------------------------------
1 | /******/ (() => { // webpackBootstrap
2 | /******/ "use strict";
3 | /******/ var __webpack_modules__ = ({
4 |
5 | /***/ "./src/style.css":
6 | /*!***********************!*\
7 | !*** ./src/style.css ***!
8 | \***********************/
9 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
10 |
11 | __webpack_require__.r(__webpack_exports__);
12 | // extracted by mini-css-extract-plugin
13 |
14 |
15 | /***/ })
16 |
17 | /******/ });
18 | /************************************************************************/
19 | /******/ // The module cache
20 | /******/ var __webpack_module_cache__ = {};
21 | /******/
22 | /******/ // The require function
23 | /******/ function __webpack_require__(moduleId) {
24 | /******/ // Check if module is in cache
25 | /******/ var cachedModule = __webpack_module_cache__[moduleId];
26 | /******/ if (cachedModule !== undefined) {
27 | /******/ return cachedModule.exports;
28 | /******/ }
29 | /******/ // Create a new module (and put it into the cache)
30 | /******/ var module = __webpack_module_cache__[moduleId] = {
31 | /******/ // no module.id needed
32 | /******/ // no module.loaded needed
33 | /******/ exports: {}
34 | /******/ };
35 | /******/
36 | /******/ // Execute the module function
37 | /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
38 | /******/
39 | /******/ // Return the exports of the module
40 | /******/ return module.exports;
41 | /******/ }
42 | /******/
43 | /************************************************************************/
44 | /******/ /* webpack/runtime/make namespace object */
45 | /******/ (() => {
46 | /******/ // define __esModule on exports
47 | /******/ __webpack_require__.r = (exports) => {
48 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
49 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
50 | /******/ }
51 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
52 | /******/ };
53 | /******/ })();
54 | /******/
55 | /************************************************************************/
56 | var __webpack_exports__ = {};
57 | // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
58 | (() => {
59 | /*!**********************!*\
60 | !*** ./src/index.js ***!
61 | \**********************/
62 | __webpack_require__.r(__webpack_exports__);
63 | /* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./style.css */ "./src/style.css");
64 |
65 |
66 | console.log("index.js loaded!");
67 |
68 | })();
69 |
70 | /******/ })()
71 | ;
72 | //# sourceMappingURL=main.8a76ca7e8337f1fd02ec.js.map
--------------------------------------------------------------------------------
/caching/dist/js/main.8a76ca7e8337f1fd02ec.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"js/main.8a76ca7e8337f1fd02ec.js","mappings":";;;;;;;;;;;AAAA;;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;ACNqB;AACrB;AACA","sources":["webpack:///./src/style.css?aff7","webpack:///webpack/bootstrap","webpack:///webpack/runtime/make namespace object","webpack:///./src/index.js"],"sourcesContent":["// extracted by mini-css-extract-plugin\nexport {};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import \"./style.css\";\r\n\r\nconsole.log(\"index.js loaded!\");\r\n"],"names":[],"sourceRoot":""}
--------------------------------------------------------------------------------
/caching/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod",
5 | "serve": "webpack serve --env target=serve"
6 | },
7 | "homepage": "https://github.com/pldg/learn-webpack",
8 | "license": "MIT",
9 | "devDependencies": {
10 | "css-loader": "^6.7.1",
11 | "html-webpack-plugin": "^5.5.0",
12 | "mini-css-extract-plugin": "^2.6.1",
13 | "style-loader": "^3.3.1",
14 | "webpack": "^5.73.0",
15 | "webpack-cli": "^4.10.0",
16 | "webpack-dev-server": "^4.9.3"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/caching/src/alice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/caching/src/alice.jpg
--------------------------------------------------------------------------------
/caching/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Document
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/caching/src/index.js:
--------------------------------------------------------------------------------
1 | import "./style.css";
2 |
3 | console.log("index.js loaded!");
4 |
--------------------------------------------------------------------------------
/caching/src/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: lightblue;
3 | }
4 |
5 | img {
6 | display: block;
7 | max-width: 20vw;
8 | }
--------------------------------------------------------------------------------
/caching/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 | const MiniCssExtractPlugin = require("mini-css-extract-plugin");
3 |
4 | module.exports = (env) => {
5 | const isProd = env.target === "prod";
6 | const isDevServer = env.target === "serve";
7 |
8 | return {
9 | output: {
10 | clean: true,
11 | filename: "js/[name].[contenthash].js",
12 | assetModuleFilename: "assets/[name].[hash][ext][query]",
13 | },
14 | devtool: isDevServer ? "eval" : "source-map",
15 | devServer: {
16 | hot: true,
17 | },
18 | mode: isProd ? "production" : "development",
19 | module: {
20 | rules: [
21 | {
22 | test: /\.jpg$/,
23 | type: "asset/resource",
24 | },
25 | {
26 | test: /\.css$/,
27 | use: [
28 | {
29 | loader: isDevServer
30 | ? "style-loader"
31 | : MiniCssExtractPlugin.loader,
32 | },
33 | {
34 | loader: "css-loader",
35 | options: {
36 | sourceMap: true,
37 | },
38 | },
39 | ],
40 | },
41 | ],
42 | },
43 | plugins: [
44 | new HtmlWebpackPlugin({
45 | template: "./src/index.html",
46 | }),
47 | ].concat(
48 | isDevServer
49 | ? []
50 | : new MiniCssExtractPlugin({
51 | filename: "css/[name].[contenthash].css",
52 | chunkFilename: "css/[id].[contenthash].css",
53 | })
54 | ),
55 | };
56 | };
57 |
--------------------------------------------------------------------------------
/chunks-types/README.md:
--------------------------------------------------------------------------------
1 | # Chunks types
2 |
3 | A [chunk](https://webpack.js.org/glossary/#c) can refer to a single module (file) from your app or to a bundle emitted by Webpack which contains multiple modules:
4 |
5 | - An **entry chunk** is an emitted file which contains the webpack runtime code. They must be executed first. If you set a file in [entry points](https://webpack.js.org/concepts/entry-points/) it's considered an entry chunk.
6 | - An **initial chunk** is loaded synchronously using `import "./some-module"`, however doesn't contain the runtime code. Loading an initial chunk must always happen after its entry chunk is loaded first. This loads synchronously after the entry chunk has parsed and evaluated in the browser.
7 | - An **async chunk** is lazy loaded. For example when using `import("./some-module")` webpack will separate any of the modules into a chunk wich will be loaded asynchronously. See [dynamic-import](../dynamic-import) for an example.
8 |
9 | ## Further reading
10 |
11 | - [Chunk Objects](https://webpack.js.org/api/stats/#chunk-objects)
12 | - [Chunks Types and Bundle](https://github.com/webpack/webpack.js.org/issues/970#issuecomment-305525560)
13 | - [What are Module Chunk and Bundle in Webpack](https://stackoverflow.com/questions/42523436/what-are-module-chunk-and-bundle-in-webpack/48024612)
--------------------------------------------------------------------------------
/code-splitting/README.md:
--------------------------------------------------------------------------------
1 | # Code Splitting
2 |
3 | [Code Splitting](https://webpack.js.org/guides/code-splitting/#root) is a technique used to divide your app into more smaller files.
4 |
5 | By default webpack create a new bundle file for every [entry chunk and async chunk](../chunks-types/README.md), to avoid duplicated dependencies across them and for other kinds of optimizations webpack comes with [SplitChunksPlugin](https://webpack.js.org/plugins/split-chunks-plugin/) with some configuration pre-set out of the box.
6 |
7 | SplitChunksPlugin allow to split your code into more files without having to do it by yourself. This is useful to improve performance of the app, the browser will [cache](../caching) your app files instead of re-downloading them every time an user visit the website.
8 |
9 | Out of the box SplitChunksPlugin only split chunks based on its [default configuration](https://webpack.js.org/plugins/split-chunks-plugin/#defaults).
10 |
11 | ## Explain this example
12 |
13 | After you run `npm run dev`, the bundle *main.js* (also called *entry chunk*) contains the webpack runtime plus codes from entry point *index.js*.
14 |
15 | Inside *index.js* there are some dynamic imports:
16 |
17 | ```js
18 | import("react"); // -> vendors-node_modules_react_index_js.js
19 | import("./a"); // -> src_a_js.js
20 | import("./b"); // -> src_b_js.js
21 | ```
22 |
23 | [Dynamic Import](./dynamic-import) is a webpack specific sytnax used to tell webpack to create separate files for those modules (also called async chunks).
24 |
25 | Note that *small-component.js* is inside both *src_a_js.js* and *dev/src_b_js.js* because it's smaller then 20kb, as per default SplitChunksPlugin configuration.
26 |
27 | Because `react` it's a vendor dependency from node_modules it's extracted in a separate file called *vendors-node_modules_react_index_js.js*.
28 |
29 | ## Do more tests
30 |
31 | If you import a file that weight more then 20kb it'll be splitted into a separate file. Inside *a.js* and *b.js* change the import satements to include the large file `import "large-component.js"` insdead of the small one.
32 |
33 | Try to change all dynamic imports to regular import statements and run the build: everything will be included inside *main.js* bundle.
34 |
35 | ## Configuration
36 |
37 | If you want to split all chunks types set `optimization.splitChunks.chunks: 'all'`.
38 |
39 | To have more control over code splitting you can use [cacheGroups](https://webpack.js.org/plugins/split-chunks-plugin/#split-chunks-example-1) option.
40 |
41 | > If you're changing the configuration, you should measure the impact of your changes to ensure there's a real benefit.
42 |
43 | ## Note
44 |
45 | If you want to give a specific name to an async chunk, you can use [`webpackChunkName`](https://webpack.js.org/api/module-methods/#import-) as comment inside dynamic import, this will cause the files to be named `[name].js` instead of just `[id].js`:
46 |
47 | ```js
48 | // The output file will be named react.js
49 | import(/* webpackChunkName: 'react' */ 'react');
50 | ```
51 |
52 | Webpack 5 will automatically assign useful file names in development mode even when not using [webpackChunkName](https://webpack.js.org/migrate/5/#using--webpackchunkname--).
53 |
54 | ## Further reading
55 |
56 | - [Separate Runtime](../separate-runtime/README.md)
57 | - [Webpack 4 Code Splitting Chunk Graph and the SplitChunks Optimization](https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366)
58 | - [Rip CommonsChunkPlugin](https://gist.github.com/sokra/1522d586b8e5c0f5072d7565c2bee693)
59 |
--------------------------------------------------------------------------------
/code-splitting/dist/src_a_js.js:
--------------------------------------------------------------------------------
1 | (self["webpackChunk"] = self["webpackChunk"] || []).push([["src_a_js"],{
2 |
3 | /***/ "./src/a.js":
4 | /*!******************!*\
5 | !*** ./src/a.js ***!
6 | \******************/
7 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8 |
9 | "use strict";
10 | __webpack_require__.r(__webpack_exports__);
11 | /* harmony import */ var _small_component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./small-component */ "./src/small-component.js");
12 | /* harmony import */ var _small_component__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_small_component__WEBPACK_IMPORTED_MODULE_0__);
13 | // This is a regular import, and because it's smaller then 20kb it'll be bundle
14 | // inside src_a_js.js. Instead, if you import large-component.js it'll be
15 | // splitted in a different file
16 |
17 |
18 | console.log("a.js loaded!");
19 |
20 |
21 | /***/ }),
22 |
23 | /***/ "./src/small-component.js":
24 | /*!********************************!*\
25 | !*** ./src/small-component.js ***!
26 | \********************************/
27 | /***/ ((module) => {
28 |
29 | console.log("small-component.js loaded!");
30 |
31 | module.exports = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
32 |
33 |
34 | /***/ })
35 |
36 | }]);
--------------------------------------------------------------------------------
/code-splitting/dist/src_b_js.js:
--------------------------------------------------------------------------------
1 | (self["webpackChunk"] = self["webpackChunk"] || []).push([["src_b_js"],{
2 |
3 | /***/ "./src/b.js":
4 | /*!******************!*\
5 | !*** ./src/b.js ***!
6 | \******************/
7 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
8 |
9 | "use strict";
10 | __webpack_require__.r(__webpack_exports__);
11 | /* harmony import */ var _small_component__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./small-component */ "./src/small-component.js");
12 | /* harmony import */ var _small_component__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_small_component__WEBPACK_IMPORTED_MODULE_0__);
13 | // This is a regular import, and because it's smaller then 20kb it'll be bundle
14 | // inside src_b_js.js. Instead, if you import large-component.js it'll be
15 | // splitted in a different file
16 |
17 |
18 | console.log("b.js loaded!");
19 |
20 |
21 | /***/ }),
22 |
23 | /***/ "./src/small-component.js":
24 | /*!********************************!*\
25 | !*** ./src/small-component.js ***!
26 | \********************************/
27 | /***/ ((module) => {
28 |
29 | console.log("small-component.js loaded!");
30 |
31 | module.exports = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
32 |
33 |
34 | /***/ })
35 |
36 | }]);
--------------------------------------------------------------------------------
/code-splitting/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod"
5 | },
6 | "homepage": "https://github.com/pldg/learn-webpack",
7 | "license": "MIT",
8 | "devDependencies": {
9 | "webpack": "^5.73.0",
10 | "webpack-cli": "^4.10.0"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/code-splitting/src/a.js:
--------------------------------------------------------------------------------
1 | // This is a regular import, and because it's smaller then 20kb it'll be bundle
2 | // inside src_a_js.js. Instead, if you import large-component.js it'll be
3 | // splitted in a different file
4 | import "./small-component";
5 |
6 | console.log("a.js loaded!");
7 |
--------------------------------------------------------------------------------
/code-splitting/src/b.js:
--------------------------------------------------------------------------------
1 | // This is a regular import, and because it's smaller then 20kb it'll be bundle
2 | // inside src_b_js.js. Instead, if you import large-component.js it'll be
3 | // splitted in a different file
4 | import "./small-component";
5 |
6 | console.log("b.js loaded!");
7 |
--------------------------------------------------------------------------------
/code-splitting/src/index.js:
--------------------------------------------------------------------------------
1 | // Those are dynamic imports therefor they'll be splitted different files. If
2 | // you change them to regular import statements everything will be included
3 | // inside main.js bundle
4 | import("react");
5 | import("./a");
6 | import("./b");
7 |
8 | console.log("index.js loaded!");
9 |
--------------------------------------------------------------------------------
/code-splitting/src/small-component.js:
--------------------------------------------------------------------------------
1 | console.log("small-component.js loaded!");
2 |
3 | module.exports = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
4 |
--------------------------------------------------------------------------------
/code-splitting/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = (env) => {
2 | const isProd = env.target === "prod";
3 |
4 | return {
5 | output: {
6 | clean: true,
7 | },
8 | mode: isProd ? "production" : "development",
9 | devtool: false,
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/composing-configs/README.md:
--------------------------------------------------------------------------------
1 | # Composing Configurations
2 |
3 | Separate production and development configurations in different files. Merge configurations files with [webpack-merge](https://github.com/survivejs/webpack-merge).
4 |
5 | ## Further reading
6 |
7 | [SurviveJS - Composing Configuration](https://survivejs.com/webpack/developing/composing-configuration/)
8 |
--------------------------------------------------------------------------------
/composing-configs/configs/const.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | const cwd = process.cwd();
4 |
5 | const PATHS = {
6 | cwd,
7 | configs: __dirname,
8 | input: path.resolve(cwd, "src"),
9 | output: path.resolve(cwd, "dist"),
10 | };
11 |
12 | module.exports = {
13 | PATHS,
14 | };
15 |
--------------------------------------------------------------------------------
/composing-configs/configs/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = (env) => {
2 | if (!env) throw new Error("You must set --env flag");
3 |
4 | if (env.target === "prod") return require("./webpack.prod");
5 | if (env.target === "dev") return require("./webpack.dev");
6 | if (env.target === "serve") return require("./webpack.serve");
7 | };
8 |
--------------------------------------------------------------------------------
/composing-configs/configs/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const { merge } = require("webpack-merge");
2 | const { PATHS } = require("./const");
3 | const parts = require("./webpack.parts");
4 |
5 | const devConfig = merge([
6 | parts.output({
7 | clean: true,
8 | filename: "js/[name].[contenthash].js",
9 | assetModuleFilename: "assets/[name].[hash][ext][query]",
10 | }),
11 | parts.mode("development"),
12 | parts.devtool("source-map"),
13 | parts.html({
14 | template: PATHS.input + "/index.html",
15 | }),
16 | parts.extractCSS({
17 | outputDir: "css/",
18 | cssLoaderOptions: {
19 | sourceMap: true,
20 | },
21 | }),
22 | ]);
23 |
24 | module.exports = devConfig;
25 |
--------------------------------------------------------------------------------
/composing-configs/configs/webpack.parts.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Set entry points
3 | * @param {Object} entry
4 | */
5 | exports.entry = (entry) => ({
6 | entry,
7 | });
8 |
9 | /**
10 | * Set output directory
11 | * @param {Object} output
12 | */
13 | exports.output = (output) => ({
14 | output,
15 | });
16 |
17 | /**
18 | * Set webpack mode
19 | * @param {String} mode
20 | */
21 | exports.mode = (mode) => ({
22 | mode,
23 | });
24 |
25 | /**
26 | * Set sourcemaps
27 | * @param {String} devtool
28 | */
29 | exports.devtool = (devtool) => ({
30 | devtool,
31 | });
32 |
33 | /**
34 | * Set development server
35 | * @param {Object} [devServer]
36 | */
37 | exports.devServer = (devServer) => {
38 | devServer = Object.assign(
39 | {
40 | compress: true,
41 | port: 8080,
42 | },
43 | devServer
44 | );
45 |
46 | return {
47 | devServer,
48 | };
49 | };
50 |
51 | /**
52 | * Generate html file
53 | * @param {Object} [options]
54 | */
55 | exports.html = (options) => {
56 | const HtmlWebpackPlugin = require("html-webpack-plugin");
57 |
58 | return {
59 | plugins: [new HtmlWebpackPlugin(options)],
60 | };
61 | };
62 |
63 | /**
64 | * Inline css
65 | * @param {Object} [cssLoaderOptions]
66 | */
67 | exports.inlineCSS = (cssLoaderOptions) => ({
68 | module: {
69 | rules: [
70 | {
71 | test: /\.css$/,
72 | use: [
73 | "style-loader",
74 | {
75 | loader: "css-loader",
76 | options: cssLoaderOptions,
77 | },
78 | ],
79 | },
80 | ],
81 | },
82 | });
83 |
84 | /**
85 | * Extract css into a separate file
86 | * @param {Object} [ctx]
87 | * @param {String} [ctx.outputDir]
88 | * @param {Object} [ctx.cssLoaderOptions]
89 | * @param {Object} [ctx.extractPluginOptions]
90 | */
91 | exports.extractCSS = ({
92 | outputDir = "",
93 | cssLoaderOptions,
94 | extractPluginOptions,
95 | }) => {
96 | const MiniCssExtractPlugin = require("mini-css-extract-plugin");
97 |
98 | return {
99 | module: {
100 | rules: [
101 | {
102 | test: /\.css$/,
103 | use: [
104 | {
105 | loader: MiniCssExtractPlugin.loader,
106 | options: extractPluginOptions,
107 | },
108 | {
109 | loader: "css-loader",
110 | options: cssLoaderOptions,
111 | },
112 | ],
113 | },
114 | ],
115 | },
116 | plugins: [
117 | new MiniCssExtractPlugin({
118 | filename: `${outputDir}[name].[contenthash].css`,
119 | chunkFilename: `${outputDir}[id].[contenthash].css`,
120 | }),
121 | ],
122 | };
123 | };
124 |
--------------------------------------------------------------------------------
/composing-configs/configs/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require("webpack-merge");
2 | const { PATHS } = require("./const");
3 | const parts = require("./webpack.parts");
4 |
5 | const prodConfig = merge([
6 | parts.output({
7 | clean: true,
8 | filename: "js/[name].[contenthash].js",
9 | assetModuleFilename: "assets/[name].[hash][ext][query]",
10 | }),
11 | parts.mode("production"),
12 | parts.html({
13 | template: PATHS.input + "/index.html",
14 | minify: {
15 | collapseWhitespace: true,
16 | removeComments: true,
17 | },
18 | }),
19 | parts.extractCSS({
20 | outputDir: "css/",
21 | }),
22 | ]);
23 |
24 | module.exports = prodConfig;
25 |
--------------------------------------------------------------------------------
/composing-configs/configs/webpack.serve.js:
--------------------------------------------------------------------------------
1 | const { merge } = require("webpack-merge");
2 | const { PATHS } = require("./const");
3 | const parts = require("./webpack.parts");
4 |
5 | const devServerConfig = merge([
6 | parts.mode("development"),
7 | parts.devtool("eval"),
8 | parts.devServer({
9 | hot: true,
10 | }),
11 | parts.html({
12 | template: PATHS.input + "/index.html",
13 | }),
14 | parts.inlineCSS({
15 | sourceMap: true,
16 | }),
17 | ]);
18 |
19 | module.exports = devServerConfig;
20 |
--------------------------------------------------------------------------------
/composing-configs/dist/css/main.1af36816c458bacb01d8.css:
--------------------------------------------------------------------------------
1 | /*!*******************************************************************************************!*\
2 | !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[0].use[1]!./src/var.css ***!
3 | \*******************************************************************************************/
4 | :root {
5 | --bgc: brown;
6 | --color: white;
7 | }
8 |
9 | /*!*********************************************************************************************!*\
10 | !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[0].use[1]!./src/style.css ***!
11 | \*********************************************************************************************/
12 | body {
13 | background-color: var(--bgc);
14 | color: var(--color);
15 | }
16 |
17 |
18 | /*# sourceMappingURL=main.1af36816c458bacb01d8.css.map*/
--------------------------------------------------------------------------------
/composing-configs/dist/css/main.1af36816c458bacb01d8.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"css/main.1af36816c458bacb01d8.css","mappings":";;;AAAA;EACE,YAAY;EACZ,cAAc;AAChB;;;;;ACDA;EACE,4BAA4B;EAC5B,mBAAmB;AACrB","sources":["webpack:///./src/var.css","webpack:///./src/style.css"],"sourcesContent":[":root {\r\n --bgc: brown;\r\n --color: white;\r\n}\r\n","@import \"./var.css\";\r\n\r\nbody {\r\n background-color: var(--bgc);\r\n color: var(--color);\r\n}\r\n"],"names":[],"sourceRoot":""}
--------------------------------------------------------------------------------
/composing-configs/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Composing Configuration
9 |
10 |
11 |
12 |
13 | Welcome
14 |
15 |
16 |
--------------------------------------------------------------------------------
/composing-configs/dist/js/main.9d57fd1eba571e7bf8e8.js:
--------------------------------------------------------------------------------
1 | /******/ (() => { // webpackBootstrap
2 | /******/ "use strict";
3 | /******/ var __webpack_modules__ = ({
4 |
5 | /***/ "./src/style.css":
6 | /*!***********************!*\
7 | !*** ./src/style.css ***!
8 | \***********************/
9 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
10 |
11 | __webpack_require__.r(__webpack_exports__);
12 | // extracted by mini-css-extract-plugin
13 |
14 |
15 | /***/ })
16 |
17 | /******/ });
18 | /************************************************************************/
19 | /******/ // The module cache
20 | /******/ var __webpack_module_cache__ = {};
21 | /******/
22 | /******/ // The require function
23 | /******/ function __webpack_require__(moduleId) {
24 | /******/ // Check if module is in cache
25 | /******/ var cachedModule = __webpack_module_cache__[moduleId];
26 | /******/ if (cachedModule !== undefined) {
27 | /******/ return cachedModule.exports;
28 | /******/ }
29 | /******/ // Create a new module (and put it into the cache)
30 | /******/ var module = __webpack_module_cache__[moduleId] = {
31 | /******/ // no module.id needed
32 | /******/ // no module.loaded needed
33 | /******/ exports: {}
34 | /******/ };
35 | /******/
36 | /******/ // Execute the module function
37 | /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
38 | /******/
39 | /******/ // Return the exports of the module
40 | /******/ return module.exports;
41 | /******/ }
42 | /******/
43 | /************************************************************************/
44 | /******/ /* webpack/runtime/make namespace object */
45 | /******/ (() => {
46 | /******/ // define __esModule on exports
47 | /******/ __webpack_require__.r = (exports) => {
48 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
49 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
50 | /******/ }
51 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
52 | /******/ };
53 | /******/ })();
54 | /******/
55 | /************************************************************************/
56 | var __webpack_exports__ = {};
57 | // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
58 | (() => {
59 | /*!**********************!*\
60 | !*** ./src/index.js ***!
61 | \**********************/
62 | __webpack_require__.r(__webpack_exports__);
63 | /* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./style.css */ "./src/style.css");
64 |
65 |
66 | console.log("index.js loaded!");
67 |
68 | })();
69 |
70 | /******/ })()
71 | ;
72 | //# sourceMappingURL=main.9d57fd1eba571e7bf8e8.js.map
--------------------------------------------------------------------------------
/composing-configs/dist/js/main.9d57fd1eba571e7bf8e8.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"js/main.9d57fd1eba571e7bf8e8.js","mappings":";;;;;;;;;;;AAAA;;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;ACNqB;AACrB;AACA","sources":["webpack:///./src/style.css?1453","webpack:///webpack/bootstrap","webpack:///webpack/runtime/make namespace object","webpack:///./src/index.js"],"sourcesContent":["// extracted by mini-css-extract-plugin\nexport {};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import \"./style.css\";\r\n\r\nconsole.log(\"index.js loaded!\");\r\n"],"names":[],"sourceRoot":""}
--------------------------------------------------------------------------------
/composing-configs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev --config ./configs/webpack.config.js",
4 | "prod": "webpack --env target=prod --config ./configs/webpack.config.js",
5 | "serve": "webpack serve --env target=serve --config ./configs/webpack.config.js"
6 | },
7 | "homepage": "https://github.com/pldg/learn-webpack",
8 | "license": "MIT",
9 | "devDependencies": {
10 | "css-loader": "^6.7.1",
11 | "html-webpack-plugin": "^5.5.0",
12 | "mini-css-extract-plugin": "^2.6.1",
13 | "style-loader": "^3.3.1",
14 | "webpack": "^5.73.0",
15 | "webpack-cli": "^4.10.0",
16 | "webpack-dev-server": "^4.9.3",
17 | "webpack-merge": "^5.8.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/composing-configs/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Composing Configuration
9 |
10 |
11 |
12 |
13 | Welcome
14 |
15 |
16 |
--------------------------------------------------------------------------------
/composing-configs/src/index.js:
--------------------------------------------------------------------------------
1 | import "./style.css";
2 |
3 | console.log("index.js loaded!");
4 |
--------------------------------------------------------------------------------
/composing-configs/src/style.css:
--------------------------------------------------------------------------------
1 | @import "./var.css";
2 |
3 | body {
4 | background-color: var(--bgc);
5 | color: var(--color);
6 | }
7 |
--------------------------------------------------------------------------------
/composing-configs/src/var.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --bgc: brown;
3 | --color: white;
4 | }
5 |
--------------------------------------------------------------------------------
/context-module/README.md:
--------------------------------------------------------------------------------
1 | # Context Module `require.context()`
2 |
3 | The concept of [context](https://webpack.js.org/configuration/entry-context/#context) in webpack (and nodejs) refer to some directory that is used as a base for resolving paths to modules.
4 |
5 | Webpack analyze `require()`, `import`, `import()` and `require.context` at compile time to build a [dependency graph](https://webpack.js.org/concepts/dependency-graph/#root) of all modules.
6 |
7 | The [`require.context()`](https://webpack.js.org/guides/dependency-management/#requirecontext) function is a special webpack feature that allow you to get all module paths (files) from a base directory. One common use case is to dynamically import static assets.
8 |
9 | In other words, `require.context()` is a form of [code splitting](../code-splitting/README.md) .
10 |
11 | ## How it works
12 |
13 | ```js
14 | const r = require.context(
15 | // Directory path must be literal
16 | "./path/to/directory",
17 | // Recursive (use subdirectories)
18 | false,
19 | // Match files with regexp
20 | /\.jpg$/
21 | );
22 |
23 | const imagesPaths = r.keys();
24 | // -> ["./image1.jpg", "./image2.jpg"]
25 |
26 | const parsed_image1 = r(imagesPaths[0]);
27 | // -> 73728f89fad1b84b9a3d.jpg
28 | ```
29 |
30 | ## Explain this example
31 |
32 | In this example `require.context` is used to get all images inside a directory. When you require *load-images.js* inside html, html-webpack-plugin run it and images become part of dependencies graph, *load-images.js* is not added in output build, it's just parsed by webpack at compilation time.
33 |
34 | ## How to debug webpack
35 |
36 | To see the logs inside *load-images.js* you have to [debug webpack using nodejs debug tool](../debug-webpack/README.md).
37 |
38 | ## Notes
39 |
40 | You can't pass variable as argument inside `require.context()` because it must be statically analyzable ([webpack#4772](https://github.com/webpack/webpack/issues/4772), [so#42118921](https://stackoverflow.com/a/42118921)).
41 |
42 | Even if you comment out require statements inside html:
43 |
44 | ```html
45 |
46 |
47 | ```
48 |
49 | Webpack will run *load-images.js* anyway (if you have errors inside *load-images.js* they will show up) but do not output images.
50 |
51 | To debug *load-images.js* see how to [debug webpack](../debug-webpack/README.md).
52 |
53 | ## Further reading
54 |
55 | - [SurviveJS - Dynamic Loading with require.context](https://survivejs.com/webpack/techniques/dynamic-loading/#dynamic-loading-with-require-context)
56 | - [so#54066904 - What is require.context](https://stackoverflow.com/a/54066904)
57 |
--------------------------------------------------------------------------------
/context-module/dist/0f7001a62be1d14e6b63.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/context-module/dist/0f7001a62be1d14e6b63.jpg
--------------------------------------------------------------------------------
/context-module/dist/73728f89fad1b84b9a3d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/context-module/dist/73728f89fad1b84b9a3d.jpg
--------------------------------------------------------------------------------
/context-module/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/context-module/dist/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
3 | * This devtool is neither made for production nor for readable output files.
4 | * It uses "eval()" calls to create a separate source file in the browser devtools.
5 | * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
6 | * or disable the default devtool with "devtool: false".
7 | * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
8 | */
9 | /******/ (() => { // webpackBootstrap
10 | /******/ var __webpack_modules__ = ({
11 |
12 | /***/ "./src/index.js":
13 | /*!**********************!*\
14 | !*** ./src/index.js ***!
15 | \**********************/
16 | /***/ (() => {
17 |
18 | eval("console.log(\"index.js loaded\");\n\n\n//# sourceURL=webpack:///./src/index.js?");
19 |
20 | /***/ })
21 |
22 | /******/ });
23 | /************************************************************************/
24 | /******/
25 | /******/ // startup
26 | /******/ // Load entry module and return exports
27 | /******/ // This entry module can't be inlined because the eval devtool is used.
28 | /******/ var __webpack_exports__ = {};
29 | /******/ __webpack_modules__["./src/index.js"]();
30 | /******/
31 | /******/ })()
32 | ;
--------------------------------------------------------------------------------
/context-module/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod"
5 | },
6 | "homepage": "https://github.com/pldg/learn-webpack",
7 | "license": "MIT",
8 | "devDependencies": {
9 | "html-webpack-plugin": "^5.5.0",
10 | "webpack": "^5.73.0",
11 | "webpack-cli": "^4.10.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/context-module/src/images/alice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/context-module/src/images/alice.jpg
--------------------------------------------------------------------------------
/context-module/src/images/nebula.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/context-module/src/images/nebula.jpg
--------------------------------------------------------------------------------
/context-module/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/context-module/src/index.js:
--------------------------------------------------------------------------------
1 | console.log("index.js loaded");
2 |
--------------------------------------------------------------------------------
/context-module/src/load-images.js:
--------------------------------------------------------------------------------
1 | // This file is required inside index.html, it will be parsed at compile time
2 | // and it'll not be part of the final bundle
3 |
4 | // Search for .jpg files inside ./images directory (exluding subfolders)
5 | const r = require.context("./images", false, /\.jpg$/);
6 |
7 | // Get images paths
8 | // -> ["./image1.jpg", "./image2.jpg"]
9 | const imagesPaths = r.keys();
10 |
11 | // Compile images with webpack
12 | // -> 73728f89fad1b84b9a3d.jpg
13 | const parsed_image1 = r(imagesPaths[0]);
14 | const parsed_image2 = r(imagesPaths[1]);
15 |
16 | // Export compiled images to be used inside index.html
17 | module.exports = [parsed_image1, parsed_image2];
18 |
--------------------------------------------------------------------------------
/context-module/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 |
3 | module.exports = (env) => {
4 | const isProd = env.target === "prod";
5 |
6 | return {
7 | output: {
8 | clean: true,
9 | },
10 | devtool: isProd ? false : "eval",
11 | devServer: {
12 | hot: true,
13 | },
14 | mode: isProd ? "production" : "development",
15 | module: {
16 | rules: [
17 | {
18 | test: /\.jpg$/,
19 | type: "asset/resource",
20 | },
21 | ],
22 | },
23 | plugins: [
24 | new HtmlWebpackPlugin({
25 | template: "./src/index.html",
26 | }),
27 | ],
28 | };
29 | };
30 |
--------------------------------------------------------------------------------
/debug-webpack/README.md:
--------------------------------------------------------------------------------
1 | # Debug Webpack
2 |
3 | You can debug webpack configuration using [nodejs debug tool](https://nodejs.org/en/docs/guides/debugging-getting-started/):
4 |
5 | - Open terminal and run `node --inspect-brk node_modules/webpack/bin/webpack.js`.
6 | - `--inspect-brk` will pause your app execution at line 1.
7 | - You'll see a message like *Debugger listening on ws://127.0.0.1:9229/0f2c936f-b1cd-4ac9-aab3-f63b0f33d55e*.
8 | - Now you have to attach the debugger.
9 |
10 | Attach debugger in Chrome:
11 |
12 | - Open Chrome browser.
13 | - Type *about:inspect* in url bar and enter.
14 | - Click on *Open dedicated DevTools for Node*.
15 | - You may need to wait a bit before the debugger is attached.
16 | - Now in the terminal you'll see a new message *Debugger attached*.
17 | - App is paused at first line, click *Resume script execution* to run it and see the logs.
18 |
19 | Attach debugger in VSCode:
20 |
21 | - Open debug tab in VSCode.
22 | - Click on open *lunch.json*.
23 | - Click on *"Add Configuration"* button in the bottom left corner.
24 | - Choose *"nodejs:attach"* configuration.
25 | - You can also use debug menu or command palette to add the same settings as above.
26 | - From debug tab select your configuration (*Attach*) and click the green play button.
27 | - You may need to wait a bit before the debugger is attached.
28 | - In the terminal you'll see a new message *Debugger attached*.
29 | - App is paused at first line, click *Continue* to run it and see the logs.
30 |
--------------------------------------------------------------------------------
/dynamic-import/README.md:
--------------------------------------------------------------------------------
1 | # Dynamic import
2 |
3 | If you want to improve the performance of your app you can use a technique called [code splitting](https://webpack.js.org/glossary/#c) to divide your assets into smaller one which you can load on demand using [dynamic import](https://webpack.js.org/guides/code-splitting/#dynamic-imports) (also called [lazy loading](https://webpack.js.org/guides/lazy-loading/)).
4 |
5 | ## How dynamic import works
6 |
7 | To lazy load a module you use the [import()](https://webpack.js.org/api/module-methods/#import-1) function, a webpack specific feature that takes a path to a module as argument and return a [Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise).
8 |
9 | ```js
10 | import('./some-module').then(module => console.log(module.default));
11 | ```
12 |
13 | Every time you use dynamic import webpack produce a new chunk for that module.
14 |
15 | You can also [lazy load images](../lazy-load-image) and other type of assets.
16 |
17 | ## Using a variable inside dynamic import
18 |
19 | To use [dynamic expressions](https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import) inside `import()` webpack requires at least some file location information because the path must be statically analyzable:
20 |
21 | ```js
22 | // `./modules/` is necessary for webpack to statically analyze the path
23 | import(`./modules/${myModule}`);
24 | ```
25 |
26 | In this case it'll produce a new chunk for each module inside *./modules/*.
27 |
28 | ## Configure dynamic import
29 |
30 | You can use [magic comments](https://webpack.js.org/api/module-methods/#magic-comments) to futher optimize the dynamic import funcion.
31 |
32 | ## Explain this example
33 |
34 | In this example there are two modules, *module-1.js* and *module-2.js*. Only the former one is loaded dynamically but as you can see in the output folder both modules are begin exported, *src_module-1_js.js* and *src_module-2_js.js*. This happen because we use a dynamic expression to load the module.
35 |
36 | ```txt
37 | asset main.js 13.3 KiB [emitted] (name: main)
38 | asset src_modules_module-1_js.js 646 bytes [compared for emit]
39 | asset src_modules_module-2_js.js 646 bytes [compared for emit]
40 | asset index.html 341 bytes [compared for emit]
41 | runtime modules 6.51 KiB 9 modules
42 | built modules 896 bytes [built]
43 | modules by path ./src/modules/ 240 bytes
44 | ./src/modules/ lazy ^\.\/.*$ namespace object 160 bytes [built] [code generated]
45 | ./src/modules/module-1.js 40 bytes [optional] [built] [code generated]
46 | ./src/modules/module-2.js 40 bytes [optional] [built] [code generated]
47 | modules by path ./src/*.js 656 bytes
48 | ./src/index.js 120 bytes [built] [code generated]
49 | ./src/lazy-load-module.js 536 bytes [built] [code generated]
50 | webpack 5.45.1 compiled successfully in 233 ms
51 | ```
52 |
53 | Open *index.html* and click the button, then check devtools console and network tabs.
54 |
55 | ## Notes
56 |
57 | If you use babel for transpiling it needs [@babel/plugin-syntax-dynamic-import](https://babeljs.io/docs/plugins/syntax-dynamic-import/) plugin to support `import()` function.
58 |
59 | ## Further reading
60 |
61 | - [SurviveJS - Code Splitting](https://survivejs.com/webpack/building/code-splitting/)
62 | - [Webpack 4 Import and CommonJS](https://medium.com/webpack/webpack-4-import-and-commonjs-d619d626b655)
63 | - [Webpack and Dynamic Imports doing it right](https://medium.com/front-end-hacking/webpack-and-dynamic-impor-doing-it-right-72549ff49234)
64 |
--------------------------------------------------------------------------------
/dynamic-import/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/dynamic-import/dist/src_modules_module-1_js.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | (self["webpackChunk"] = self["webpackChunk"] || []).push([["src_modules_module-1_js"],{
3 |
4 | /***/ "./src/modules/module-1.js":
5 | /*!*********************************!*\
6 | !*** ./src/modules/module-1.js ***!
7 | \*********************************/
8 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9 |
10 | __webpack_require__.r(__webpack_exports__);
11 | /* harmony export */ __webpack_require__.d(__webpack_exports__, {
12 | /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
13 | /* harmony export */ });
14 | /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("Hello from module-1.js");
15 |
16 |
17 | /***/ })
18 |
19 | }]);
--------------------------------------------------------------------------------
/dynamic-import/dist/src_modules_module-2_js.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | (self["webpackChunk"] = self["webpackChunk"] || []).push([["src_modules_module-2_js"],{
3 |
4 | /***/ "./src/modules/module-2.js":
5 | /*!*********************************!*\
6 | !*** ./src/modules/module-2.js ***!
7 | \*********************************/
8 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9 |
10 | __webpack_require__.r(__webpack_exports__);
11 | /* harmony export */ __webpack_require__.d(__webpack_exports__, {
12 | /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
13 | /* harmony export */ });
14 | /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ("Hello from module-2.js");
15 |
16 |
17 | /***/ })
18 |
19 | }]);
--------------------------------------------------------------------------------
/dynamic-import/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod"
5 | },
6 | "homepage": "https://github.com/pldg/learn-webpack",
7 | "license": "MIT",
8 | "devDependencies": {
9 | "html-webpack-plugin": "^5.5.0",
10 | "webpack": "^5.73.0",
11 | "webpack-cli": "^4.10.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/dynamic-import/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/dynamic-import/src/index.js:
--------------------------------------------------------------------------------
1 | import lazyLoadModule from "./lazy-load-module";
2 |
3 | lazyLoadModule("module-1.js");
4 |
5 | console.log("index.js loaded!");
6 |
--------------------------------------------------------------------------------
/dynamic-import/src/lazy-load-module.js:
--------------------------------------------------------------------------------
1 | // @param {string} module - Path to a module you want to dynamic load, moduels
2 | // base path is `./modules/`
3 | export default (module) => {
4 | document.querySelector(".btn").onclick = () => {
5 | // `./modules/` is necessary because the path must be statically analizable,
6 | // if the modules reside in different folder you must change the path here
7 | import(`./modules/${module}`)
8 | // You need `.default` to load the module
9 | .then((m) => console.log(m.default))
10 | .catch((err) => console.error(err));
11 | };
12 | };
13 |
--------------------------------------------------------------------------------
/dynamic-import/src/modules/module-1.js:
--------------------------------------------------------------------------------
1 | export default "Hello from module-1.js";
2 |
--------------------------------------------------------------------------------
/dynamic-import/src/modules/module-2.js:
--------------------------------------------------------------------------------
1 | export default "Hello from module-2.js";
2 |
--------------------------------------------------------------------------------
/dynamic-import/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 |
3 | module.exports = (env) => {
4 | const isProd = env.target === "prod";
5 |
6 | return {
7 | output: {
8 | clean: true,
9 | },
10 | mode: isProd ? "production" : "development",
11 | devtool: false,
12 | plugins: [new HtmlWebpackPlugin({ template: "src/index.html" })],
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/html-plugin-template/README.md:
--------------------------------------------------------------------------------
1 | # Generate html file from an custom template
2 |
3 | You can use [Html Webpack Plugin](https://github.com/jantimon/html-webpack-plugin) to generate html files.
4 |
5 | If the default generated html doesn't meet your needs you can supply your own [template](https://github.com/jantimon/html-webpack-plugin#writing-your-own-templates).
6 |
7 | ## Explain this example
8 |
9 | Read *template.html*, *template-utils.js* and *webpack.config.js*.
10 |
11 | ## Note
12 |
13 | The template is by default rendered using [lodash](https://lodash.com/docs/4.17.10#template), do not confuse it with [ejs](http://ejs.co/) (you can't use `<%- include partial.html %>` to include a partial).
14 |
--------------------------------------------------------------------------------
/html-plugin-template/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | HTML Template
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/html-plugin-template/dist/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
3 | * This devtool is neither made for production nor for readable output files.
4 | * It uses "eval()" calls to create a separate source file in the browser devtools.
5 | * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
6 | * or disable the default devtool with "devtool: false".
7 | * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
8 | */
9 | /******/ (() => { // webpackBootstrap
10 | /******/ var __webpack_modules__ = ({
11 |
12 | /***/ "./src/index.js":
13 | /*!**********************!*\
14 | !*** ./src/index.js ***!
15 | \**********************/
16 | /***/ (() => {
17 |
18 | eval("console.log('index.js loaded!');\n\n//# sourceURL=webpack:///./src/index.js?");
19 |
20 | /***/ })
21 |
22 | /******/ });
23 | /************************************************************************/
24 | /******/
25 | /******/ // startup
26 | /******/ // Load entry module and return exports
27 | /******/ // This entry module can't be inlined because the eval devtool is used.
28 | /******/ var __webpack_exports__ = {};
29 | /******/ __webpack_modules__["./src/index.js"]();
30 | /******/
31 | /******/ })()
32 | ;
--------------------------------------------------------------------------------
/html-plugin-template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod"
5 | },
6 | "homepage": "https://github.com/pldg/learn-webpack",
7 | "license": "MIT",
8 | "devDependencies": {
9 | "html-webpack-plugin": "^5.5.0",
10 | "webpack": "^5.73.0",
11 | "webpack-cli": "^4.10.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/html-plugin-template/src/index.js:
--------------------------------------------------------------------------------
1 | console.log('index.js loaded!');
--------------------------------------------------------------------------------
/html-plugin-template/template-utils.js:
--------------------------------------------------------------------------------
1 | // https://github.com/vxna/mini-html-webpack-template/blob/master/src/index.js#L57
2 | function generateTags(attributes = [], type = '') {
3 | const closing = type === ('script' || 'style') ? `>${type}>` : '>';
4 | return attributes
5 | .map(attr => `<${type}${generateAttributes(attr)}${closing}`)
6 | .join('');
7 | }
8 |
9 | // https://github.com/styleguidist/mini-html-webpack-plugin/blob/master/src/index.ts#L56
10 | function generateAttributes(attributes = {}) {
11 | const stringAttributes = Object.entries(attributes);
12 |
13 | if (stringAttributes.length === 0) return '';
14 |
15 | return ' ' + stringAttributes
16 | .map((attr) => {
17 | if (attr[1] === true) return attr[0];
18 | return `${attr[0]}="${attr[1]}"`;
19 | })
20 | .join(' ');
21 | }
22 |
23 | function generateHeadTags(head = {}) {
24 | const {
25 | meta = [],
26 | links = [],
27 | style = [],
28 | scripts = [],
29 | snippet = ''
30 | } = head;
31 | let tags = '';
32 |
33 | // Tags will appear in html following the same order as below
34 | if (head) {
35 | if (meta) tags += generateTags(meta, 'meta');
36 | if (links) tags += generateTags(links, 'link');
37 | if (style) tags += generateTags(style, 'style');
38 | if (scripts) tags += generateTags(scripts, 'script');
39 | if (snippet) tags += snippet;
40 | }
41 |
42 | return tags;
43 | }
44 |
45 | function generateBodyTags(body = {}) {
46 | const {
47 | scripts = [],
48 | snippet = ''
49 | } = body;
50 | let tags = '';
51 |
52 | if (body) {
53 | if (scripts) tags += generateTags(scripts, 'script');
54 | if (snippet) tags += snippet;
55 | }
56 |
57 | return tags;
58 | }
59 |
60 | function generateWebpackCSSReferences(files, webpackConfig, attributes) {
61 | let css = '';
62 |
63 | for (const key in files.css) {
64 | if (files.cssIntegrity) {
65 | css += `
66 | `;
71 | } else {
72 | css += ``;
75 | }
76 | }
77 |
78 | return css;
79 | }
80 |
81 | function generateWebpackJSReferences(files, webpackConfig, attributes) {
82 | let js = '';
83 |
84 | for (const key in files.js) {
85 | if (files.jsIntegrity) {
86 | js += ``;
91 | } else {
92 | js += ``;
95 | }
96 | }
97 |
98 | return js;
99 | }
100 |
101 | module.exports = {
102 | generateAttributes,
103 | generateHeadTags,
104 | generateBodyTags,
105 | generateWebpackCSSReferences,
106 | generateWebpackJSReferences
107 | }
--------------------------------------------------------------------------------
/html-plugin-template/template.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const {
3 | generateAttributes,
4 | generateHeadTags,
5 | generateBodyTags,
6 | generateWebpackCSSReferences,
7 | generateWebpackJSReferences
8 | } = require('./template-utils');
9 |
10 | const {
11 | options: {
12 | title = '',
13 | head = {},
14 | body = {},
15 | attributes: {
16 | html: htmlAttrs = {},
17 | head: headAttrs = {},
18 | css: cssAttrs = {},
19 | body: bodyAttrs = {},
20 | js: jsAttrs = {}
21 | } = {}
22 | },
23 | files
24 | } = htmlWebpackPlugin;
25 | %>
26 |
27 |
28 | >
29 | >
30 |
31 | <%= title %>
32 |
33 | <%= generateHeadTags(head) %>
34 | <%= generateWebpackCSSReferences(files, webpackConfig, cssAttrs) %>
35 |
36 | >
37 | <%= generateBodyTags(body) %>
38 | <%= generateWebpackJSReferences(files, webpackConfig, jsAttrs) %>
39 |
40 |
--------------------------------------------------------------------------------
/html-plugin-template/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 |
3 | module.exports = (env) => {
4 | const isProd = env.target === "prod";
5 |
6 | return {
7 | output: {
8 | clean: true,
9 | },
10 | mode: isProd ? "production" : "development",
11 | plugins: [
12 | new HtmlWebpackPlugin({
13 | // Do not inject assets (import them inside template)
14 | inject: false,
15 | // Load custom template (by default use lodash syntax)
16 | template: "./template.ejs",
17 | // `title`, `attributes`, `head`, `body` variables are exposed inside
18 | // the template:
19 | title: "HTML Template",
20 | attributes: {
21 | html: {
22 | lang: "en",
23 | },
24 | js: {
25 | defer: true,
26 | },
27 | },
28 | head: {
29 | meta: [
30 | {
31 | name: "description",
32 | content: "Custom webpack html template",
33 | },
34 | ],
35 | links: [
36 | {
37 | href: "https://fonts.googleapis.com/css?family=Roboto:400&display=swap",
38 | rel: "stylesheet",
39 | },
40 | ],
41 | },
42 | body: {
43 | scripts: [
44 | {
45 | src: "https://polyfill.io/v3/polyfill.min.js?flags=gated",
46 | type: "text/javascript",
47 | crossorigin: "anonymous",
48 | defer: true,
49 | },
50 | ],
51 | },
52 | }),
53 | ],
54 | };
55 | };
56 |
--------------------------------------------------------------------------------
/lazy-load-image/README.md:
--------------------------------------------------------------------------------
1 | # Lazy load image
2 |
3 | Before reading this repo check out [dynamic import](../dynamic-import/) example.
4 |
5 | We can [`import()`](https://webpack.js.org/api/module-methods/#import-1) an image as module with [`asset/resource`](https://webpack.js.org/guides/asset-modules/#resource-assets).
6 |
7 | Every time we use dynamic import, webpack produce a new chunk for that module. This happen also when you use it to import images:
8 |
9 | ```js
10 | import(`imageName.jpg`).then(src => img.src = src.default);
11 | ```
12 |
13 | To get rid of the additional network request we can include this new chunk inside the main bundle. To do so we set a special parameter `eager` via comments (in webpack it's called "magic comment"):
14 |
15 | ```js
16 | import(
17 | /* webpackMode: "eager" */
18 | `imageName.jpg`
19 | )
20 | .then(src => img.src = src.default)
21 | .catch(err => console.error(err));
22 | ```
23 |
24 | You can also pass other parameters as [magic comments](https://webpack.js.org/api/module-methods/#magic-comments).
25 |
--------------------------------------------------------------------------------
/lazy-load-image/dist/0f7001a62be1d14e6b63.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-image/dist/0f7001a62be1d14e6b63.jpg
--------------------------------------------------------------------------------
/lazy-load-image/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Webpack App
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lazy-load-image/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod",
5 | "serve": "webpack serve --env target=serve"
6 | },
7 | "homepage": "https://github.com/pldg/learn-webpack",
8 | "license": "MIT",
9 | "devDependencies": {
10 | "html-webpack-plugin": "^5.5.0",
11 | "webpack": "^5.73.0",
12 | "webpack-cli": "^4.10.0",
13 | "webpack-dev-server": "^4.9.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lazy-load-image/src/index.js:
--------------------------------------------------------------------------------
1 | const img = document.createElement("img");
2 | const button = document.createElement("button");
3 |
4 | button.innerHTML = "Click to load image";
5 |
6 | button.onclick = () => {
7 | import(
8 | /* webpackMode: "eager" */
9 | "./nebula.jpg"
10 | )
11 | .then((src) => (img.src = src.default))
12 | .catch((err) => console.error(err));
13 | };
14 |
15 | document.body.prepend(img);
16 | document.body.prepend(button);
17 |
--------------------------------------------------------------------------------
/lazy-load-image/src/nebula.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-image/src/nebula.jpg
--------------------------------------------------------------------------------
/lazy-load-image/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 |
3 | module.exports = (env) => {
4 | const isProd = env.target === "prod";
5 |
6 | return {
7 | output: {
8 | clean: true,
9 | },
10 | mode: isProd ? "production" : "development",
11 | module: {
12 | rules: [
13 | {
14 | test: /\.jpg$/,
15 | type: "asset/resource",
16 | },
17 | ],
18 | },
19 | plugins: [new HtmlWebpackPlugin()],
20 | };
21 | };
22 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/README.md:
--------------------------------------------------------------------------------
1 | # Lazy load images folders
2 |
3 | Dynamically import folders with images when a button is clicked (follow up from [lazy-load-multiple-images](../lazy-load-multiple-images) example).
4 |
5 | ## How to
6 |
7 | Set a `data-image` attribute to each button with an unique id (it'll be used to track which image folder to load):
8 |
9 | ```html
10 |
11 |
12 |
13 |
14 |
15 | ```
16 |
17 | Import images using [require.context](../context-module/README.md) and extract their filenames:
18 |
19 | ```js
20 | export function getImagesNames_1() {
21 | const r = require.context("./images/1/", false, /\.jpg$/);
22 | return getFilenames(r);
23 | }
24 |
25 | export function getImagesNames_2() {
26 | const r = require.context("./images/2/", false, /\.jpg$/);
27 | return getFilenames(r);
28 | }
29 |
30 | function getFilenames(r) {
31 | // Return an array list of filenames (with extension)
32 | const importAll = (r) => r.keys().map((file) => file.match(/[^\/]+$/)[0]);
33 | return importAll(r);
34 | }
35 | ```
36 |
37 | Create a function to lazy load the appropriate image folder based on the id of the button:
38 |
39 | - Order image folders inside an array (index will point to correct folder id).
40 | - Generate `
` tags.
41 | - Use [`import()`](../dynamic-import/README.md) to set `src` attribute to each image on demand.
42 |
43 | ```js
44 | import { getImagesNames_1, getImagesNames_2 } from "./getImagesNames";
45 |
46 | const imagesContainer = document.querySelector("#images");
47 | const imagesNames = [getImagesNames_1(), getImagesNames_2()];
48 |
49 | export default function buildImages(id) {
50 | const currentImageFolder = imagesNames[parseInt(id) - 1];
51 |
52 | currentImageFolder.forEach((imageName) => {
53 | const img = document.createElement("img");
54 |
55 | imagesContainer.appendChild(img);
56 |
57 | import(
58 | /* webpackMode: "lazy-once" */
59 | `./images/${id}/${imageName}`
60 | )
61 | .then((src) => (img.src = src.default))
62 | .catch((err) => console.error(err));
63 | });
64 | }
65 | ```
66 |
67 | Lazy load the build images function to create a separate chunk. Call `buildImages(id)` with the correct button id:
68 |
69 | ```js
70 | const button1 = document.querySelector('[data-img="1"]');
71 | const button2 = document.querySelector('[data-img="2"]');
72 |
73 | function showImages(event) {
74 | const id = event.target.dataset.img;
75 |
76 | import(/* webpackPrefetch: true */ `./buildImages`)
77 | .then((r) => {
78 | const buildImages = r.default;
79 | buildImages(id);
80 | })
81 | .catch((err) => console.error(err));
82 | }
83 |
84 | button1.addEventListener("click", showImages);
85 | button2.addEventListener("click", showImages);
86 | ```
87 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/dist/08b5bae29ab24ce24487.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/dist/08b5bae29ab24ce24487.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/dist/0c9627955a614a979a10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/dist/0c9627955a614a979a10.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/dist/0f7001a62be1d14e6b63.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/dist/0f7001a62be1d14e6b63.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/dist/73728f89fad1b84b9a3d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/dist/73728f89fad1b84b9a3d.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Lazy Load Multiple Images Folders
10 |
11 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod",
5 | "serve": "webpack serve --env target=serve"
6 | },
7 | "homepage": "https://github.com/pldg/learn-webpack",
8 | "license": "MIT",
9 | "devDependencies": {
10 | "html-webpack-plugin": "^5.5.0",
11 | "webpack": "^5.73.0",
12 | "webpack-cli": "^4.10.0",
13 | "webpack-dev-server": "^4.9.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/buildImages.js:
--------------------------------------------------------------------------------
1 | import { getImagesNames_1, getImagesNames_2 } from "./getImagesNames";
2 |
3 | const imagesContainer = document.querySelector("#images");
4 | const imagesNames = [getImagesNames_1(), getImagesNames_2()];
5 |
6 | export default function buildImages(id) {
7 | const currentImageFolder = imagesNames[parseInt(id) - 1];
8 |
9 | currentImageFolder.forEach((imageName) => {
10 | const img = document.createElement("img");
11 |
12 | imagesContainer.appendChild(img);
13 |
14 | import(
15 | /* webpackMode: "lazy-once" */
16 | `./images/${id}/${imageName}`
17 | )
18 | .then((src) => (img.src = src.default))
19 | .catch((err) => console.error(err));
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/getImagesNames.js:
--------------------------------------------------------------------------------
1 | export function getImagesNames_1() {
2 | const r = require.context("./images/1/", false, /\.jpg$/);
3 | return getFilenames(r);
4 | }
5 |
6 | export function getImagesNames_2() {
7 | const r = require.context("./images/2/", false, /\.jpg$/);
8 | return getFilenames(r);
9 | }
10 |
11 | function getFilenames(r) {
12 | // Return an array list of filenames (with extension)
13 | const importAll = (r) => r.keys().map((file) => file.match(/[^\/]+$/)[0]);
14 | return importAll(r);
15 | }
16 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/images/1/alice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/src/images/1/alice.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/images/1/nebula.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/src/images/1/nebula.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/images/2/doré-inferno-dante.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/src/images/2/doré-inferno-dante.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/images/2/waterhouse-miranda.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images-folders/src/images/2/waterhouse-miranda.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Lazy Load Multiple Images Folders
10 |
11 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/src/index.js:
--------------------------------------------------------------------------------
1 | const button1 = document.querySelector('[data-img="1"]');
2 | const button2 = document.querySelector('[data-img="2"]');
3 |
4 | function showImages(event) {
5 | const id = event.target.dataset.img;
6 |
7 | import(/* webpackPrefetch: true */ `./buildImages`)
8 | .then((r) => {
9 | const buildImages = r.default;
10 | buildImages(id);
11 | })
12 | .catch((err) => console.error(err));
13 | }
14 |
15 | button1.addEventListener("click", showImages);
16 | button2.addEventListener("click", showImages);
17 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images-folders/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 |
3 | module.exports = (env) => {
4 | const isProd = env.target === "prod";
5 |
6 | return {
7 | output: {
8 | clean: true,
9 | },
10 | devtool: false,
11 | mode: isProd ? "production" : "development",
12 | module: {
13 | rules: [
14 | {
15 | test: /\.jpg$/,
16 | type: "asset/resource",
17 | },
18 | ],
19 | },
20 | plugins: [
21 | new HtmlWebpackPlugin({
22 | template: "./src/index.html",
23 | }),
24 | ],
25 | };
26 | };
--------------------------------------------------------------------------------
/lazy-load-multiple-images/dist/0c9627955a614a979a10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images/dist/0c9627955a614a979a10.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images/dist/0f7001a62be1d14e6b63.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images/dist/0f7001a62be1d14e6b63.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images/dist/73728f89fad1b84b9a3d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images/dist/73728f89fad1b84b9a3d.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Lazy Load Multiple Images
10 |
11 |
12 |
13 |
14 |
33 | Open DevTools network tab
Then, scroll down to show images
34 |
35 |
Lazy loaded images:
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "webpack --env target=dev",
4 | "prod": "webpack --env target=prod",
5 | "serve": "webpack serve --env target=serve"
6 | },
7 | "homepage": "https://github.com/pldg/learn-webpack",
8 | "license": "MIT",
9 | "devDependencies": {
10 | "html-webpack-plugin": "^5.5.0",
11 | "webpack": "^5.73.0",
12 | "webpack-cli": "^4.10.0",
13 | "webpack-dev-server": "^4.9.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/generateImage.js:
--------------------------------------------------------------------------------
1 | import lazyLoadImage from "./lazyLoadImage";
2 |
3 | export default (container, imageName) => {
4 | const img = document.createElement("img");
5 | container.appendChild(img);
6 |
7 | lazyLoadImage(imageName, img);
8 | };
9 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/getImagesNames.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | const r = require.context("./images", false, /\.jpg$/);
3 |
4 | // Return an array list of filenames (with extension)
5 | const importAll = (r) => r.keys().map((file) => file.match(/[^\/]+$/)[0]);
6 |
7 | return importAll(r);
8 | };
9 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/images.js:
--------------------------------------------------------------------------------
1 | import getImagesNames from "./getImagesNames";
2 | import generateImage from "./generateImage";
3 |
4 | const images = document.querySelector("#images");
5 | const imagesNames = getImagesNames();
6 |
7 | // Generate
element
8 | // Lazy load each image and set its src attribute
9 | // Append
to #images container
10 | imagesNames.forEach((name) => generateImage(images, name));
11 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/images/alice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images/src/images/alice.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/images/inferno.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images/src/images/inferno.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/images/nebula.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pldg/learn-webpack/bbab15edbe21a409f5015b9d82eac196dc2ccc5f/lazy-load-multiple-images/src/images/nebula.jpg
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Lazy Load Multiple Images
10 |
11 |
12 |
13 |
14 |
33 | Open DevTools network tab
Then, scroll down to show images
34 |
35 |
Lazy loaded images:
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/index.js:
--------------------------------------------------------------------------------
1 | // Check if an element is partially or completely visible in viewport
2 | // consider only top element position
3 | const isElementInViewport = (element) => {
4 | const rect = element.getBoundingClientRect();
5 | return (
6 | rect.top <= (window.innerHeight || document.documentElement.clientHeight)
7 | );
8 | };
9 |
10 | const images = document.querySelector("#images");
11 |
12 | const showImages = () => {
13 | const showImages = isElementInViewport(images);
14 |
15 | if (showImages)
16 | import(
17 | /* webpackPrefetch: true */
18 | "./images"
19 | );
20 | };
21 |
22 | window.addEventListener("scroll", showImages);
23 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/src/lazyLoadImage.js:
--------------------------------------------------------------------------------
1 | export default (imageName, img) => {
2 | import(
3 | /* webpackMode: "lazy-once" */
4 | `./images/${imageName}`
5 | )
6 | .then((src) => (img.src = src.default))
7 | .catch((err) => console.error(err));
8 | };
9 |
--------------------------------------------------------------------------------
/lazy-load-multiple-images/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require("html-webpack-plugin");
2 |
3 | module.exports = (env) => {
4 | const isProd = env.target === "prod";
5 |
6 | return {
7 | output: {
8 | clean: true,
9 | },
10 | devtool: false,
11 | mode: isProd ? "production" : "development",
12 | module: {
13 | rules: [
14 | {
15 | test: /\.jpg$/,
16 | type: "asset/resource",
17 | },
18 | ],
19 | },
20 | plugins: [
21 | new HtmlWebpackPlugin({
22 | template: "./src/index.html",
23 | }),
24 | ],
25 | };
26 | };
--------------------------------------------------------------------------------
/load-css/README.md:
--------------------------------------------------------------------------------
1 | # Load css
2 |
3 | ## Configure webpack to process stylesheets
4 |
5 | To load css use [css-loader](https://github.com/webpack-contrib/css-loader) and [style-loader](https://github.com/webpack-contrib/style-loader):
6 |
7 | - css-loader resolve `@imports` and `url()` inside css files and insert the result as string inside the javascript bundle.
8 | - style-loader take the css resolved by css-loader and insert a `