# Webpack

Webpack starts at an entry module, follows imports to build a dependency graph, and emits assets for the browser. A project may contain many source files while producing one or more bundles; a bundle is not necessarily the whole application in a single file.

index.ts → imported TypeScript modules → ts-loader → Webpack → dist/bundle.js
src/index.html → HtmlWebpackPlugin → dist/index.html referencing the bundle
browser → requests the generated HTML and JavaScript

TypeScript checks/transforms code; Webpack connects modules and assets. Neither is the API server. The browser executes the emitted JavaScript, not webpack.config.cjs.

# A minimal TypeScript build

These examples use Webpack 5, webpack-cli 5, ts-loader 9, HtmlWebpackPlugin 5 and TypeScript 5.x in a separate example application. They are development dependencies. Use a supported Node version compatible with those packages; they do not need to become dependencies of a server merely to serve the resulting files.

Prepare src/Student.ts, src/index.ts and tsconfig.json as shown in Types and contracts and Compiler and DOM. Add the following webpack.config.cjs; using .cjs makes the configuration's CommonJS format explicit even when application modules use import.

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

module.exports = {
  mode: 'development',
  entry: './src/index.ts',
  devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  resolve: { extensions: ['.ts', '.js'] },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
};

A minimal src/index.html template:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Students</title>
  </head>
  <body>
    <main id="root"></main>
  </body>
</html>

HtmlWebpackPlugin generates HTML that loads the emitted bundle. Do not also hardcode a duplicate bundle script into the template. Its default deferred script loading lets the HTML be parsed before the application accesses #root.

# Loaders versus plugins

Setting Responsibility
entry Starting point of the dependency graph
module.rules / loaders Transform matching module sources, such as TypeScript or CSS
resolve.extensions Resolve extensionless imports such as ./Student
plugins Extend compilation and asset generation, such as generating HTML
output Names and location of emitted assets
mode Enables defaults suited to development or production

Default ts-loader uses the TypeScript compiler and performs type checking. With transpileOnly: true, a separate check such as tsc --noEmit is necessary; Webpack itself is not a type checker. A successful transpilation is not proof of valid types or correct behavior.

# Build once, then serve assets

With the example's dependencies installed, use this script in its package.json:

{
  "scripts": {
    "build": "webpack --config webpack.config.cjs --mode production"
  }
}

npm run build finishes after emitting files. Deploy the generated assets using a static-file server or static-serving middleware; do not run a development server as the production deployment. Source maps are useful for debugging, but decide deliberately whether to publish them with production assets.

Try it: change Student.greeting(), rebuild, and inspect the generated HTML. Which tool removes type annotations, and which tool inserts the script reference?

Next: Development workflow. References: Webpack concepts (opens new window) · TypeScript guide (opens new window).