diff --git a/docs/05-plugin-development.md b/docs/05-plugin-development.md
index ef2def09112..fd36dc87892 100644
--- a/docs/05-plugin-development.md
+++ b/docs/05-plugin-development.md
@@ -102,7 +102,7 @@ Notifies a plugin when watcher process closes and all open resources should be c
#### `load`
-**Type:** `(id: string) => string | null | {code: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null, meta?: {[plugin: string]: any} | null}`
**Kind:** `async, first`
**Previous Hook:** [`resolveId`](guide/en/#resolveid) or [`resolveDynamicImport`](guide/en/#resolvedynamicimport) where the loaded id was resolved. Additionally, this hook can be triggered at any time from plugin hooks by calling [`this.load`](guide/en/#thisload) to preload the module corresponding to an id.
**Next Hook:** [`transform`](guide/en/#transform) to transform the loaded file.
+**Type:** `(id: string) => string | null | {code: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null, meta?: {[plugin: string]: any} | null}`
**Kind:** `async, first`
**Previous Hook:** [`resolveId`](guide/en/#resolveid) or [`resolveDynamicImport`](guide/en/#resolvedynamicimport) where the loaded id was resolved. Additionally, this hook can be triggered at any time from plugin hooks by calling [`this.load`](guide/en/#thisload) to preload the module corresponding to an id.
**Next Hook:** [`transform`](guide/en/#transform) to transform the loaded file if no cache was used, or there was no cached copy with the same `code`, otherwise [`shouldTransformCachedModule`](guide/en/#shouldtransformcachedmodule).
Defines a custom loader. Returning `null` defers to other `load` functions (and eventually the default behavior of loading from the file system). To prevent additional parsing overhead in case e.g. this hook already used `this.parse` to generate an AST for some reason, this hook can optionally return a `{ code, ast, map }` object. The `ast` must be a standard ESTree AST with `start` and `end` properties for each node. If the transformation does not move code, you can preserve existing sourcemaps by setting `map` to `null`. Otherwise you might need to generate the source map. See [the section on source code transformations](#source-code-transformations).
@@ -222,9 +222,19 @@ Note that while `resolveId` will be called for each import of a module and can t
When triggering this hook from a plugin via [`this.resolve`](guide/en/#thisresolve), it is possible to pass a custom options object to this hook. While this object will be passed unmodified, plugins should follow the convention of adding a `custom` property with an object where the keys correspond to the names of the plugins that the options are intended for. For details see [custom resolver options](guide/en/#custom-resolver-options).
+#### `shouldTransformCachedModule`
+
+**Type:** `({id: string, code: string, ast: ESTree.Program, meta: {[plugin: string]: any}, moduleSideEffects: boolean | "no-treeshake", syntheticNamedExports: string | boolean}) => boolean`
**Kind:** `sync, first`
**Previous Hook:** [`load`](guide/en/#load) where the cached file was loaded to compare its code with the cached version.
NextHook: [`moduleParsed`](guide/en/#moduleparsed) if no plugin returns `true`, otherwise [`transform`](guide/en/#transform).
+
+If Rollup cache is used (e.g. in watch mode or explicitly via the JavaScript API), Rollup will usually skip the transform hook of a module if after the load hook, the loaded `code` is identical to the code of the cached copy. To prevent this and instead transform a module, plugins can implement this hook and return `true`.
+
+This hook can also be used to find out which modules were cached and access their cached meta information.
+
+If a plugin does not return `true`, Rollup will trigger this hook for other plugins, otherwise all other plugins will be skipped.
+
#### `transform`
-**Type:** `(code: string, id: string) => string | null | {code?: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null, meta?: {[plugin: string]: any} | null}`
**Kind:** `async, sequential`
**Previous Hook:** [`load`](guide/en/#load) where the currently handled file was loaded.
NextHook: [`moduleParsed`](guide/en/#moduleparsed) once the file has been processed and parsed.
+**Type:** `(code: string, id: string) => string | null | {code?: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null, meta?: {[plugin: string]: any} | null}`
**Kind:** `async, sequential`
**Previous Hook:** [`load`](guide/en/#load) where the currently handled file was loaded. If caching is used and there was a cached copy of that module, [`shouldTransformCachedModule`](guide/en/#shouldtransformcachedmodule) if a plugin returned `true` for that hook.
NextHook: [`moduleParsed`](guide/en/#moduleparsed) once the file has been processed and parsed.
Can be used to transform individual modules. To prevent additional parsing overhead in case e.g. this hook already used `this.parse` to generate an AST for some reason, this hook can optionally return a `{ code, ast, map }` object. The `ast` must be a standard ESTree AST with `start` and `end` properties for each node. If the transformation does not move code, you can preserve existing sourcemaps by setting `map` to `null`. Otherwise you might need to generate the source map. See [the section on source code transformations](#source-code-transformations).
diff --git a/docs/build-hooks.mmd b/docs/build-hooks.mmd
index bdb2695fcda..40c34cdcca3 100644
--- a/docs/build-hooks.mmd
+++ b/docs/build-hooks.mmd
@@ -3,6 +3,7 @@ flowchart TB
classDef hook-sequential fill:#ffd2b3,stroke:#000;
classDef hook-first fill:#fff2b3,stroke:#000;
classDef hook-sequential-sync fill:#ffd2b3,stroke:#f00;
+ classDef hook-first-sync fill:#fff2b3,stroke:#f00;
buildend("buildEnd"):::hook-parallel
click buildend "/guide/en/#buildend" _parent
@@ -25,6 +26,9 @@ flowchart TB
resolveid("resolveId"):::hook-first
click resolveid "/guide/en/#resolveid" _parent
+ shouldtransformcachedmodule("shouldTransformCachedModule"):::hook-sequential-sync
+ click shouldtransformcachedmodule "/guide/en/#shouldtransformcachedmodule" _parent
+
transform("transform"):::hook-sequential
click transform "/guide/en/#transform" _parent
@@ -41,10 +45,17 @@ flowchart TB
resolveid
--> |non-external|load
- --> transform
+ --> |not cached|transform
--> moduleparsed
.-> |no imports|buildend
+ load
+ --> |cached|shouldtransformcachedmodule
+ --> |false|moduleparsed
+
+ shouldtransformcachedmodule
+ --> |true|transform
+
moduleparsed
--> |"each import()"|resolvedynamicimport
--> |non-external|load