Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/content/api/compilation-hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,42 @@ Store chunk info to the records. This is only triggered if [`shouldRecord`](#sho

- Callback Parameters: `chunks` `records`

## renderEmbeddedSource

<Badge text="5.110.0+" />

`AsyncSeriesWaterfallHook`

Called with source written in one language that a module emits inside another, before it is embedded. Today that is CSS or HTML reaching the bundle as a JavaScript string literal. Return the source to embed, possibly transformed.

- Callback Parameters: `source`, `info`

`info` carries `type` (the embedded source's type, `"css"` or `"html"`), `hostType` (the type it is embedded in, `"javascript"`) and `module` (the module being generated), so one tap can serve every pair.

```js
compilation.hooks.renderEmbeddedSource.tapPromise(
"MyPlugin",
async (source, info) => {
if (info.type !== "css") return source;
return new RawSource(await minifyCss(source.source()));
},
);
```

No asset ever carries this source, so an asset-level minimizer cannot reach it. That is what the hook is for.

W> Module hashes are taken before code generation, so a tap must write whatever it varies on into the hash through [`embeddedSourceHash`](#embeddedsourcehash). Otherwise the code generation cache replays output produced before your options changed.

## embeddedSourceHash

<Badge text="5.110.0+" />

`SyncHook`

Called while hashing a module that embeds a source of another language. Tap it to add whatever your [`renderEmbeddedSource`](#renderembeddedsource) tap varies on to the module hash.

- Callback Parameters: `module`, `hash`

## beforeModuleHash

`SyncHook`
Expand Down
23 changes: 23 additions & 0 deletions src/content/api/module-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,29 @@ Access to the internal object of all modules.

It provides access to the hash of the compilation.

## \_\_webpack_css_server_styles\_\_ (webpack-specific)

<Badge text="5.110.0+" />

`string`

Returns the CSS that has been collected while rendering without a DOM, as one string. When the built-in CSS support ([`experiments.css`](/configuration/experiments/#experimentscss)) runs somewhere there is no `document` to insert a `<style>` into, a server-side render for example, webpack writes the styles to a global registry instead. This variable reads that registry back, in the order the styles were applied, so the server can inline them into the HTML it sends:

```js
import { renderToString } from "react-dom/server";
import App from "./App.js";

export function render() {
const html = renderToString(<App />);
// every stylesheet the render pulled in, in application order
const css = __webpack_css_server_styles__;

return `<!doctype html><html><head><style>${css}</style></head><body>${html}</body></html>`;
}
```

The registry is keyed by style identifier and namespaced with [`output.uniqueName`](/configuration/output/#outputuniquename), so several bundles rendering in the same process do not read each other's styles.

## \_\_webpack_get_script_filename\_\_ (webpack-specific)

`function (chunkId)`
Expand Down
30 changes: 30 additions & 0 deletions src/content/api/node.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,36 @@ W> Multiple configurations will **not be run in parallel**. Each
configuration is only processed after the previous one has finished
processing.

### MultiCompiler hooks

A `MultiCompiler` exposes the child compilers' hooks as one, so a plugin can tap the set instead of every child:

| Hook | Type | Description |
| ------------------- | --------------------------------------- | -------------------------------------------------------------------------------- |
| `done` | `SyncHook<[MultiStats, Compiler[]]>` | Called once every child compilation has finished. Aggregated, not a `MultiHook`. |
| `shutdown` | `AsyncSeriesHook<[Compiler]>` | <Badge text="5.110.0+" /> Called when the compilers are closing, once per child. |
| `invalid` | `SyncHook<[string \| null, number]>` | Called when a watched file changes in any of the children. |
| `run` | `AsyncSeriesHook<[Compiler]>` | Called before a child starts a non-watch build. |
| `watchRun` | `AsyncSeriesHook<[Compiler]>` | Called before a child starts a watch build. |
| `watchClose` | `SyncHook<[]>` | Called when watching stops. |
| `infrastructureLog` | `SyncBailHook<[string, string, any[]]>` | Infrastructure logging for any child. |

Since webpack 5.110.0 the `done` hook is also handed the compilers whose build actually ran, in configuration order. In watch mode a change usually invalidates only some of the children, and the others are reported from their previous stats, so this is how a plugin tells what is new:

```js
import webpack from "webpack";

const compiler = webpack([config1, config2]);

compiler.hooks.done.tap("MyPlugin", (multiStats, changedCompilers) => {
for (const child of changedCompilers) {
console.log(`${child.name} rebuilt`);
}
});
```

On the first build every child is reported as changed. `shutdown` was added in the same release and lets a plugin release resources once, for the whole set, rather than tapping each child's own [`shutdown`](/api/compiler-hooks/#shutdown).

## Error Handling

For good error handling, you need to account for these three types of errors:
Expand Down
2 changes: 1 addition & 1 deletion src/content/configuration/entry-context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export default {

Descriptor syntax might be used to pass additional options to an entry point.

The `html` option <Badge text="5.108.0+" /> generates an HTML file for this entrypoint with its JS and CSS output chunks injected. It accepts the same values as [`output.html`](/configuration/output/#outputhtml) and overrides it option by option for this entry, so a single entry can opt out of page generation or change just its title:
The `html` option <Badge text="5.108.0+" /> generates an HTML file for this entrypoint with its JS and CSS output chunks injected. It accepts the same values as [`output.html`](/configuration/output/#outputhtml) and, since webpack 5.110.0, an object that overrides it option by option for this entry, so a single entry can opt out of page generation or change just its title:

```js
export default {
Expand Down
2 changes: 1 addition & 1 deletion src/content/configuration/experiments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ When [`experiments.css`](#experimentscss) is enabled, `.css` is likewise appende

HTML modules support [Hot Module Replacement](/concepts/hot-module-replacement/). No extra configuration is needed. It activates automatically when HMR is enabled (for example via [`devServer.hot`](/configuration/dev-server/#devserverhot)).

For a page extracted to a real `.html` file, each hot update patches `document.body.innerHTML` and `document.title` in place instead of triggering a full reload. Changes to `<head>` beyond the `<title>` (a new `<meta>`, a swapped `<link rel="icon">`, …) cannot be safely DOM-patched, so the shim falls back to a full page reload.
For a page extracted to a real `.html` file, each hot update patches `document.body.innerHTML` and `document.title` in place instead of triggering a full reload. Since webpack 5.110.0 the `<head>` is patched in place as well, so a new `<meta>`, a swapped `<link rel="icon">` or a removed `<script>` that never executed no longer costs a full page reload.

T> [Module concatenation](/configuration/optimization/#optimizationconcatenatemodules) is disabled for HTML modules while HMR is active, because each module needs its own `module.hot` scope to self-accept updates.

Expand Down
106 changes: 96 additions & 10 deletions src/content/configuration/externals.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contributors:
- anshumanv
- SaulSilver
- fi3ework
- bjohansebas
---

The `externals` configuration option provides a way of excluding dependencies from the output bundles. Instead, the created bundle relies on that dependency to be present in the consumer's (any end-user application) environment. This feature is typically most useful to **library developers**, however there are a variety of applications for it.
Expand Down Expand Up @@ -193,6 +194,31 @@ export default {
};
```

### object with options

<Badge text="5.110.0+" />

An external value can also be given as an object carrying the target under `external` plus options describing how webpack should treat it:

```js
export default {
// ...
externals: {
"@scope/icons": {
external: "commonjs @scope/icons",
sideEffects: false,
},
},
};
```

- `external` - the target, in any of the forms above (a string, an array, or an object per externals type).
- `sideEffects` - whether importing the external has side effects, the same idea as the [`sideEffects` flag](/guides/tree-shaking/#mark-the-file-as-side-effect-free) in a `package.json`.

webpack cannot analyze an external, so it has to assume that importing one does something observable and keeps the import even when nothing reads its exports. `sideEffects: false` states the opposite, and lets webpack drop the external entirely when none of its exports are used. This matters most for a large external imported by a barrel file, where the request would otherwise survive into every chunk that touches the barrel.

W> Only set it when the package really is free of side effects. Getting it wrong removes an import that the runtime was relying on for its effect, and the failure shows up at runtime rather than at build time.

### function

- `function ({ context, request, contextInfo, getResolve }, callback)`
Expand Down Expand Up @@ -1053,16 +1079,19 @@ jq(".my-element").animate(/* ... */);

Enable presets of externals for specific targets.

| Option | Description | Input Type |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `electron` | Treat common electron built-in modules in main and preload context like `electron`, `ipc` or `shell` as external and load them via `require()` when used. | boolean |
| `electronMain` | Treat electron built-in modules in the main context like `app`, `ipc-main` or `shell` as external and load them via `require()` when used. | boolean |
| `electronPreload` | Treat electron built-in modules in the preload context like `web-frame`, `ipc-renderer` or `shell` as external and load them via require() when used. | boolean |
| `electronRenderer` | Treat electron built-in modules in the renderer context like `web-frame`, `ipc-renderer` or `shell` as external and load them via `require()` when used. | boolean |
| `node` | Treat node.js built-in modules like `fs`, `path` or `vm` as external and load them via `require()` when used. | boolean |
| `nwjs` | Treat `NW.js` legacy `nw.gui` module as external and load it via `require()` when used. | boolean |
| `web` | Treat references to `http(s)://...` and `std:...` as external and load them via `import` when used. **(Note that this changes execution order as externals are executed before any other code in the chunk)**. | boolean |
| `webAsync` | Treat references to `http(s)://...` and `std:...` as external and load them via `async import()` when used **(Note that this external type is an `async` module, which has various effects on the execution)**. | boolean |
| Option | Description | Input Type |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| `electron` | Treat common electron built-in modules in main and preload context like `electron`, `ipc` or `shell` as external and load them via `require()` when used. | boolean |
| `electronMain` | Treat electron built-in modules in the main context like `app`, `ipc-main` or `shell` as external and load them via `require()` when used. | boolean |
| `electronPreload` | Treat electron built-in modules in the preload context like `web-frame`, `ipc-renderer` or `shell` as external and load them via require() when used. | boolean |
| `electronRenderer` | Treat electron built-in modules in the renderer context like `web-frame`, `ipc-renderer` or `shell` as external and load them via `require()` when used. | boolean |
| `bun` | Treat bun built-in modules like `bun`, `bun:sqlite` or `bun:ffi`, and node.js built-in modules, as external and load them via `import` when used (for the Bun runtime). | boolean |
| `deno` | Treat node.js built-in modules like `fs`, `path` or `vm` as external and load them via the required `node:` specifier when used (for the Deno runtime). | boolean |
| `node` | Treat node.js built-in modules like `fs`, `path` or `vm` as external and load them via `require()` when used. | boolean |
| `nodeModules` | <Badge text="5.110.0+" /> Treat installed packages (requests resolving into a `node_modules` directory) as external and load them via `require()`/`import` at runtime instead of bundling them. See [`externalsPresets.nodeModules`](#externalspresetsnodemodules). | boolean, object |
| `nwjs` | Treat `NW.js` legacy `nw.gui` module as external and load it via `require()` when used. | boolean |
| `web` | Treat references to `http(s)://...` and `std:...` as external and load them via `import` when used. **(Note that this changes execution order as externals are executed before any other code in the chunk)**. | boolean |
| `webAsync` | Treat references to `http(s)://...` and `std:...` as external and load them via `async import()` when used **(Note that this external type is an `async` module, which has various effects on the execution)**. | boolean |

Note that if you're going to output ES Modules with those node.js-related presets, webpack will set the default `externalsType` to [`node-commonjs`](#externalstypenode-commonjs) which would use `createRequire` to construct a require function instead of using `require()`.

Expand All @@ -1080,3 +1109,60 @@ export default {
},
};
```

### externalsPresets.nodeModules

<Badge text="5.110.0+" />

`boolean` `object`

Treat every request that resolves into a `node_modules` directory as external and load it with `require()` or `import` at runtime, instead of bundling it. This is what a server-side build usually wants: the dependencies are already installed next to the output, so bundling them only makes the build slower and the output bigger.

**webpack.config.js**

```js
export default {
// ...
target: "node",
externalsPresets: {
nodeModules: true,
},
};
```

The preset looks at where the request resolves, not at how it is written, so a request that resolves through a symlink into `node_modules` (a pnpm store, a linked workspace package) is externalized as well. A few things are never externalized, so you do not have to list them:

- relative and absolute requests, and `#` subpath imports, which are never installed packages;
- anything that does not resolve to a file the runtime can load on its own, that is anything other than `.js`, `.mjs`, `.cjs`, `.json` and `.node`, so a package's CSS or assets imported from JavaScript stay bundled and webpack keeps processing them;
- CSS `@import` and `url()` references, which are handled by their own presets;
- a request that [`resolve.alias`](/configuration/resolve/#resolvealias) sends to a different package, since the external would keep the original request and load the wrong one.

The external is emitted as [`node-commonjs`](#externalstypenode-commonjs), or as [`module-import`](#externalstypemodule-import) when [`output.module`](/configuration/output/#outputmodule) is enabled; a `require()` dependency stays `node-commonjs` either way, so its `require()` semantics are preserved.

T> This preset replaces the [`webpack-node-externals`](https://github.com/liady/webpack-node-externals) plugin, which did the same thing from outside webpack, and it has the resolver's answer instead of guessing from the request string.

#### externalsPresets.nodeModules.allowlist

Some installed packages still have to be bundled: one that only ships ESM while the output is CommonJS, a workspace package that is not published next to the output, or a package you want processed by your loaders. Pass them in `allowlist` to keep them bundled:

```js
export default {
// ...
externalsPresets: {
nodeModules: {
allowlist: [
// an exact request
"some-esm-only-package",
// everything under a scope
/^@my-company\//,
// or decide per request
(request) => request.startsWith("internal-"),
],
},
},
};
```

Each entry is a string matched exactly, a `RegExp` tested against the request, or a function returning `true` for the requests that should stay bundled.

T> Turn on [`performance.unusedExternals`](/configuration/performance/#performanceunusedexternals) while you are tuning the list: it reports the requests listed in `externals` that no module imported, which is what a misspelled allowlist entry looks like.
Loading
Loading