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
38 changes: 17 additions & 21 deletions src/content/contribute/plugin-patterns.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ class MyPlugin {
// Explore each chunk (build output):
for (const chunk of compilation.chunks) {
// Explore each module within the chunk (built inputs):
for (const module of chunk.getModules()) {
for (const module of compilation.chunkGraph.getChunkModules(chunk)) {
// Explore each source file path that was included into the module:
if (module.buildInfo && module.buildInfo.fileDependencies) {
for (const fileDependency of module.buildInfo.fileDependencies) {
if (module.buildInfo && module.buildInfo.snapshot) {
for (const fileDependency of module.buildInfo.snapshot.getFileIterable()) {
// we've learned a lot about the source structure now...
console.log(fileDependency);
}
Expand All @@ -50,34 +50,30 @@ export default MyPlugin;

W> **Deprecation warning**: Array functions will still work.

- `module.fileDependencies`: An array of source file paths included into a module. This includes the source JavaScript file itself (ex: `index.js`), and all dependency asset files (stylesheets, images, etc) that it has required. Reviewing dependencies is useful for seeing what source files belong to a module.
- `module.buildInfo.snapshot.getFileIterable()`: An iterable of source file paths included into a module. This includes the source JavaScript file itself (ex: `index.js`), and all dependency asset files (stylesheets, images, etc) that it has required. Reviewing dependencies is useful for seeing what source files belong to a module.
- `compilation.chunks`: A set of chunks (build outputs) in the compilation. Each chunk manages the composition of a final rendered assets.

W> **Deprecation warning**: Array functions will still work.

- `chunk.getModules()`: An array of modules that are included into a chunk. By extension, you may look through each module's dependencies to see what raw source files fed into a chunk.
- `compilation.chunkGraph.getChunkModules(chunk)`: An array of modules that are included into a chunk. By extension, you may look through each module's dependencies to see what raw source files fed into a chunk.
- `chunk.files`: A Set of output filenames generated by the chunk. You may access these asset sources from the `compilation.assets` table.

### Monitoring the watch graph

While running webpack middleware, each compilation includes a `fileDependencies` `Set` (what files are being watched) and a `fileTimestamps` `Map` that maps watched file paths to a timestamp. These are extremely useful for detecting what files have changed within the compilation:
While running webpack middleware, each compilation includes a `fileDependencies` `Set` (what files are being watched) and the compiler exposes a `modifiedFiles` `Set` with the files that triggered the current rebuild (`undefined` on the first build). These are extremely useful for detecting what files have changed within the compilation:

```js
class MyPlugin {
constructor() {
this.startTime = Date.now();
this.prevTimestamps = new Map();
}

apply(compiler) {
compiler.hooks.emit.tapAsync("MyPlugin", (compilation, callback) => {
const changedFiles = [...compilation.fileTimestamps.keys()].filter(
(watchfile) =>
(this.prevTimestamps.get(watchfile) || this.startTime) <
(compilation.fileTimestamps.get(watchfile) || Infinity),
);

this.prevTimestamps = compilation.fileTimestamps;
// only the watched files that changed since the previous build
const changedFiles = compiler.modifiedFiles
? [...compiler.modifiedFiles].filter((file) =>
compilation.fileDependencies.has(file),
)
: [];

console.log("Changed files:", changedFiles);
callback();
});
}
Expand All @@ -96,11 +92,11 @@ W> Since webpack 5, `compilation.fileDependencies`, `compilation.contextDependen

Similar to the watch graph, you can monitor changed chunks within a compilation by tracking their content hashes.

W> `compilation.chunks` is a `Set`, not an array. Calling `.filter()` or `.map()` directly on it throws a `TypeError`.
W> `compilation.chunks` is a `Set`, not an array. Calling `.filter()` or `.map()` directly on it only works through a deprecated compatibility layer (`experiments.backCompat`, enabled by default) that logs a deprecation warning, and throws a `TypeError` when that layer is disabled.
Use `for...of` or spread it into an array first.

W> `chunk.hash` is not defined on chunk objects. Use `chunk.contentHash.javascript` instead.
Accessing `chunk.hash` returns `undefined`, causing every chunk to appear changed on every rebuild.
T> `chunk.hash` is the full hash of the chunk, while `chunk.contentHash[type]` is the hash of its content per source type (e.g. `javascript`, `css`).
Prefer `chunk.contentHash.javascript` to detect changes to the JavaScript content of a chunk.

```js
class MyPlugin {
Expand Down
12 changes: 5 additions & 7 deletions src/content/contribute/release-process.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ When merging pull requests into the `main` branch, select the _Create Merge Comm

## Releasing

```bash
npm version patch && git push --follow-tags && npm publish
npm version minor && git push --follow-tags && npm publish
npm version major && git push --follow-tags && npm publish
```
Releases are automated with [changesets](https://github.com/changesets/changesets):

_This will increment the package version, commits the changes, cuts a **local tag**, push to github & publish the npm package._
1. Every user-facing pull request adds a [changeset](https://github.com/webpack/webpack/blob/main/.changeset/README.md) file in `.changeset/` declaring its bump level (`patch`, `minor` or `major`).
2. On every push to `main`, the [release workflow](https://github.com/webpack/webpack/blob/main/.github/workflows/release.yml) updates a `chore(release): new release` pull request that consumes the pending changesets, bumps the version in `package.json` and writes `CHANGELOG.md`.
3. Merging that pull request publishes the new version to npm and creates the matching git tag and [GitHub release](https://github.com/webpack/webpack/releases).

After that go to the github [releases page](https://github.com/webpack/webpack/releases) and write a Changelog for the new tag.
Cutting a release means merging the release pull request. When that happens (patch releases as soon as possible, minor releases every four weeks) is described in [`RELEASE_SCHEDULE.md`](https://github.com/webpack/webpack/blob/main/RELEASE_SCHEDULE.md).
6 changes: 3 additions & 3 deletions src/content/contribute/writing-a-loader.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ export default function (source) {

### Data Sharing

In webpack, loaders can be chained together and share data with subsequent loaders in the chain. To achieve this, you can pass data along with the content (source code) using the `this.callback` method in raw loaders. In the default exported function of a raw loader, you can pass data using the fourth argument of `this.callback`.
In webpack, loaders can be chained together and share data with subsequent loaders in the chain. To achieve this, you can pass data along with the content (source code) using the `this.callback` method. In the default exported function of a loader, you can pass data using the fourth argument of `this.callback`.

```js
export default function (source) {
const options = getOptions(this);
const options = this.getOptions();
// Pass data using the fourth argument of this.callback
this.callback(null, `export default ${JSON.stringify(source)}`, null, {
some: data,
Expand Down Expand Up @@ -367,7 +367,7 @@ Our loader will process `.txt` files and replace any instance of `[name]` with t
export default function loader(source) {
const options = this.getOptions();

source = source.replaceAll(/\[name\]/, options.name);
source = source.replaceAll("[name]", options.name);

return `export default ${JSON.stringify(source)}`;
}
Expand Down
8 changes: 4 additions & 4 deletions src/content/contribute/writing-a-plugin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -425,15 +425,15 @@ Various types of hooks supported are :
- Tapped into using `tap` method.
- Called using `call(...params)` method.

In these types of hooks, each of the plugin callbacks will be invoked one after the other with the specific `args`. If any value is returned except undefined by any plugin, then that value is returned by hook and no further plugin callback is invoked. Many useful events like `optimizeChunks`, `optimizeChunkModules` are SyncBailHooks.
In these types of hooks, each of the plugin callbacks will be invoked one after the other with the specific `args`. If any value is returned except undefined by any plugin, then that value is returned by hook and no further plugin callback is invoked. Many useful events like `optimizeChunks`, `optimizeModules` are SyncBailHooks.

- **Waterfall Hooks**
- Defined using `SyncWaterfallHook[params]`
- Tapped into using `tap` method.
- Called using `call(...params)` method

Here each of the plugins is called one after the other with the arguments from the return value of the previous plugin. The plugin must take the order of its execution into account.
It must accept arguments from the previous plugin that was executed. The value for the first plugin is `init`. Hence at least 1 param must be supplied for waterfall hooks. This pattern is used in the Tapable instances which are related to the webpack templates like `ModuleTemplate`, `ChunkTemplate` etc.
It must accept arguments from the previous plugin that was executed. The value for the first plugin is `init`. Hence at least 1 param must be supplied for waterfall hooks. This pattern is used in the rendering hooks exposed by `webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation)` like `renderChunk`, `renderMain`, `render` etc.

### Asynchronous Hooks

Expand All @@ -451,7 +451,7 @@ Various types of hooks supported are :
- Called using `callAsync(...params)` method

The plugin handler functions are called with the current value and a callback function with the signature `(err: Error, nextValue: any) -> void.` When called `nextValue` is the current value for the next handler. The current value for the first handler is `init`. After all handlers are applied, callback is called with the last value. If any handler passes a value for `err`, the callback is called with this error and no more handlers are called.
This plugin pattern is expected for events like `before-resolve` and `after-resolve`.
This plugin pattern is expected for events like `ContextModuleFactory.hooks.beforeResolve` and `afterResolve`; the `NormalModuleFactory` equivalents are AsyncSeriesBailHooks.

- **Async Series Bail**
- Defined using `AsyncSeriesBailHook[params]`
Expand Down Expand Up @@ -495,7 +495,7 @@ class AssetLoggerPlugin {
export default AssetLoggerPlugin;
```

This plugin taps into the `emit` hook of the Webpack compiler and prints the names of all generated assets to the console. It demonstrates how plugins can interact with the compilation process using Webpack hooks.
This plugin taps into the `thisCompilation` hook of the Webpack compiler and the compilation's `processAssets` hook, and prints the names of all generated assets to the console. It demonstrates how plugins can interact with the compilation process using Webpack hooks.

For example, when running a Webpack build, the output may look like:

Expand Down
Loading