Webpack Concepts and Configuration

Concepts

  • Webpack is a static module bundler for modern JavaScript applications.
  • It builds a dependency graph from entry points.
  • Combines modules into bundles (static assets).
  • Webpack 4+ doesn't require a configuration file but is highly configurable.

Core Concepts:

  • Entry
  • Output
  • Loaders
  • Plugins
  • Mode
  • Browser Compatibility

Entry

  • Indicates which module Webpack uses to begin building its dependency graph.
  • Determines which other modules and libraries the entry point depends on.
  • Default value: ./src/index.js.
  • Can specify different (or multiple) entry points via the entry property in webpack.config.js.

Example:

module.exports = {
  entry: './path/to/my/entry/file.js',
};

Entry Points Syntax

  • Single Entry (Shorthand) Syntax
    • Usage: entry: string | [string]
    • Good for single entry point applications or libraries.
    • Not flexible for scaling configurations.
    • Example:
      javascript module.exports = { entry: './path/to/my/entry/file.js', };
    • Multi-Main Entry (Array Syntax):
    • Pass an array of file paths to the entry property.
    • Creates a "multi-main entry".
    • Useful for injecting multiple dependent files and graphing dependencies into one chunk.
    • Example:
      javascript module.exports = { entry: ['./src/file_1.js', './src/file_2.js'], output: { filename: 'bundle.js', }, };
  • Object Syntax
    • Usage: entry: { <entryChunkName>: string | [string] } | {}
    • More verbose and scalable way to define entries.
    • Allows for separate entry points for different parts of the application (e.g., app and adminApp).
    • Example:
      javascript module.exports = { entry: { app: './src/app.js', adminApp: './src/adminApp.js', }, };

Entry Description Object:

  • dependOn: Entry points that must be loaded before the current entry point.
  • filename: Specifies the name of each output file on disk.
  • import: Module(s) loaded upon startup.
  • library: Specify library options to bundle a library from the current entry.
  • runtime: Name of the runtime chunk. Creates a new runtime chunk when set.
  • publicPath: Public URL address for the output files of this entry.
Invalid Configurations:
  • Using runtime and dependOn together on a single entry.
  • runtime pointing to an existing entry point name.
  • Circular dependencies in dependOn.
Scenarios:
  • Separate App and Vendor Entries
    • Separate entry points for application code and vendor libraries.
    • Allows bundling of vendor code into a separate chunk.
    • Enables browser caching of vendor code separately, reducing load time.
    • Example:
      javascript module.exports = { entry: { main: './src/app.js', vendor: './src/vendor.js', }, };
  • Multi-Page Application
    • Separate entry points for each page of the application.
    • Allows for creating separate dependency graphs for each page.
    • Enables optimization techniques like optimization.splitChunks to create bundles of shared application code between pages.
    • Example:
      javascript module.exports = { entry: { pageOne: './src/pageOne/index.js', pageTwo: './src/pageTwo/index.js', pageThree: './src/pageThree/index.js', }, };

Output

  • Tells Webpack where to emit the bundles it creates and how to name the files.
  • Defaults to ./dist/main.js for the main output file and ./dist folder for other generated files.
  • Configurable via the output field in webpack.config.js.

Example:

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js',
  },
};
  • Uses output.filename and output.path properties.
  • The path module (core Node.js module) is used to manipulate file paths.

Usage

  • Minimum requirement: set output.filename within the output object.
  module.exports = {
    output: {
      filename: 'bundle.js',
    },
  };
  • Multiple Entry Points:
    • Use substitutions in output.filename to ensure unique names for each chunk.
    • [name]: name of the chunk.
    • [contenthash]: hash of the chunk's content.

Advanced Configuration:

  • Using a CDN (Content Delivery Network) and hashes for assets.
  module.exports = {
    output: {
      path: '/home/proj/cdn/assets/[fullhash]',
      publicPath: 'https://cdn.example.com/assets/[fullhash]/',
    },
  };
  • Setting publicPath dynamically at runtime using __webpack_public_path__.

Loaders

  • Processes other types of files and converts them into valid modules.
  • Allows importing any type of module (e.g., .css files).
  • Two properties in Webpack config:
    • test: identifies which file(s) should be transformed.
    • use: indicates which loader should be used.

Example:

module.exports = {
  module: {
    rules: [
      {
        test: /\.txt$/, // matches files ending with .txt
        use: 'raw-loader', // uses raw-loader to transform
      },
    ],
  },
};
  • module.rules is the correct property to define rules, NOT rules.
  • Regex for matching files should not be quoted (i.e., /\.txt$/ not '\.txt$/').

Using Loaders

  • Configuration (recommended): specify loaders in webpack.config.js.
  • Inline: specify loaders directly in import statements.
Configuration
  • module.rules allows specifying multiple loaders.
  • Provides a full overview of each loader.
  • Loaders are evaluated from right to left (bottom to top).
  • Example:
    javascript module.exports = { module: { rules: [ { test: /\.css$/, // Matches .css files use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { modules: true, }, }, { loader: 'sass-loader' } ], }, ], }, };
Inline
  • Specify loaders in import statements.
  • Separate loaders from the resource using !.
  • Can override loaders from configuration by prefixing the import statement:
    • !: disables all configured normal loaders.
    • !!: disables all configured loaders (preLoaders, loaders, postLoaders).
    • -!: disables all configured preLoaders and loaders but not postLoaders.
    • Example:
      javascript import Styles from 'style-loader!css-loader??modules!./styles.css'

Loader Features

  • Loaders can be chained, executing in reverse order.
  • Loaders can be synchronous or asynchronous.
  • Loaders run in Node.js.
  • Loaders can be configured with options objects (query parameters are deprecated).
  • Loaders can emit additional arbitrary files.
  • Loaders follow standard module resolution.
  • Loaders are named xxx-loader (e.g., json-loader).

Plugins

  • Used for a wider range of tasks: bundle optimization, asset management, and injection of environment variables.
  • To use a plugin, require() it and add it to the plugins array.
  • Most plugins are customizable through options.
  • Create an instance of a plugin using the new operator.

Anatomy of a Plugin

  • Webpack plugin is a JavaScript object with an apply method.
  • The apply method is called by the Webpack compiler, providing access to the compilation lifecycle.
  const pluginName = 'ConsoleLogOnBuildWebpackPlugin';

  class ConsoleLogOnBuildWebpackPlugin {
    apply(compiler) {
      compiler.hooks.run.tap(pluginName, (compilation) => {
        console.log('The webpack build process is starting!!');
      });
    }
  }

  module.exports = ConsoleLogOnBuildWebpackPlugin;

Using Plugins

Configuration
  • Pass a new instance of the plugin to the plugins property in webpack configuration.
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const webpack = require('webpack'); //to access built-in plugins
  const path = require('path');

  module.exports = {
    entry: './path/to/my/entry/file.js',
    output: {
      filename: 'my-first-webpack.bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
    module: {
      rules: [
        { test: /\.((js||jsx))$/, use: 'babel-loader' },
      ],
    },
    plugins: [
      new webpack.ProgressPlugin(),
      new HtmlWebpackPlugin({ template: './src/index.html' }),
    ],
  };
Node API
  • Plugins can be passed via the plugins property in the configuration.

Mode

  • Sets the mode parameter to development, production, or none.
  • Enables Webpack's built-in optimizations corresponding to each environment.
  • Default value is production.

Example:

module.exports = {
  mode: 'production',
};

Browser Compatibility

  • Webpack supports все ES5-compliant browsers (IE8 and below are not supported).
  • Requires Promise for import() and require.ensure().
  • Use a polyfill for older browsers.

Configuration

  • Webpack's configuration file is a JavaScript file that exports a Webpack configuration.
  • You can:
    • Import other files via require(...).
    • Use utilities on npm via require(...).
    • Use JavaScript control flow expressions (e.g., ?: operator).
    • Use constants or variables.
    • Write and execute functions to generate parts of the configuration.

Avoid:

  • Accessing CLI arguments (use --env instead).
  • Exporting non-deterministic values.
  • Writing long configurations (split into multiple files).

Multiple Targets

  • Exporting multiple configurations is possible along with single configuration

Using Other Configuration Languages

  • Webpack accepts configuration files written in multiple languages.

Modules

  • Break programs into discrete chunks of functionality.
  • Provide solid abstractions and encapsulation boundaries.
  • Webpack applies the concept of modules to any file in your project.

What is a webpack Module

  • Can express dependencies in various ways:
    • ES2015 import statement
    • CommonJS require() statement
    • AMD define and require statement
    • @import statement (CSS/Sass/Less)
    • url(...) or <img src=...> (stylesheets/HTML)

Supported Module Types

  • ECMAScript modules
  • CommonJS modules
  • AMD modules
  • Assets
  • WebAssembly modules
  • Loaders support modules in other languages and preprocessors.

Module Resolution

  • A resolver is a library that helps locate a module by its absolute path.
  • Webpack uses enhanced-resolve to resolve file paths.

Resolving Rules in Webpack

  • Uses enhanced-resolve to resolve path files:
    • Absolute paths
    • Relative Paths
    • Module Paths

Module Federation

  • Multiple separate builds should form a single application.
  • Separate builds act like containers.
  • Can expose and consume code among themselves.

Low-Level Concepts

  • Distinguishes between local and remote modules.
  • Loading remote modules is asynchronous.
  • Containers are created through container entries, which exposes asynchronous access to specific modules.
  • Loading and evaluating the module are separated into two steps.

High-Level Concepts

  • Each build acts as a container and consumes other builds as containers.
  • Shared modules are overridable and provided as overrides to nested containers.

Concept goals

  • Expose and consume any module type that webpack supports.
  • Chunk loading must load everything needed in parallel.
  • Control from consumer to container.

Building Blocks

  • ContainerPlugin (low level): creates a container entry with exposed modules.
  • ContainerReferencePlugin (low level): adds references to containers and allows importing remote modules.
  • ModuleFederationPlugin (high level): combines ContainerPlugin and ContainerReferencePlugin.

Use cases

  • Separate builds per page
  • Component library as container
  • Dynamic Remote Container
  • Promise Based Dynamic Remotes
  • Dynamic Public Path
  • Troubleshooting- Uncaught Error: Shared module is not available for eager consumption

Dependency Graph

  • Any time one file depends on another, Webpack treats it as a dependency.
  • Includes non-code assets (images, web fonts).
  • Entry points recursively build a dependency graph.

Targets

  • Offers multiple deployment targets.

Usage

  • Set the target value to compile to Node.js or similar env.

Multiple Targets

  • Create an isomorphic library by bundling two separate configurations.

The Manifest

  • Runtime and Manifest are responsible for webpack to connect modularized application when running in the browser

Runtime

  • The manifest that webpack needs to connect your modularized application while it's running in the browser

Manifest

  • Collection of data used by the runtime will resolve and load modules once they've been bundled and shipped to the browser.

Hot Module Replacement

  • Exchanges, adds, or removes modules while an application is running without a full reload.

How It Works

In the Application
  • Modules are swapped in and out of an application
In the Compiler
  • Compiler emits an update to allow updating from the previous version to the new version.
In a Module
  • Opt-in feature that only a ects modules containing HMR code
In the Runtime
  • Make an HTTP request to the update manifest
    -Runtime supports two methods: check and apply

Why Webpack?

  • Bundles JavaScript applications and supports ESM and CommonJS.
  • Can be extended to support different assets (images, fonts, stylesheets).
  • Automatic Dependency Collection

Under The Hood

  • Modules, Entry Points, Chunks, Chunk Groups

Chunks

  • initial: main chunk for the entry point.
  • non-initial: lazy-loaded chunk.

Output

  • output.filename: for initial chunk files.
  • output.chunkFilename: for non-initial chunk files.