Plugin hooks

Every plugin hook of a Guchho build, when it runs and what it answers with.

#

Every hook is optional. A hook that can decline does so by returning null (or leaving the return undefined), which says "not mine" rather than "stop". Hooks may be async; the engine runs them one at a time while the build waits.

The signatures are in the generated plugin types; the order they run in is the lifecycle on the overview page.

buildStart

#
buildStart?(this: PluginContext, options: BuildOptions): void | Promise<void>

Called once per build, before any file is read. The place to open a session, read a manifest, or look at what build is about to run.

resolveId

#
resolveId?(
  this: PluginContext,
  source: string,
  importer: string | undefined,
  options: ResolveIdOptions
): ResolvedId | string | false | null | undefined | Promise<…>

Asked where a specifier resolves to, for every import and entry point. Decide three ways:

  • Return a ResolvedId (or the path as a bare string) to claim the import and stop the search.
  • Return false to mark the import external and leave the specifier as written in the output.
  • Return null (or nothing) to decline and let the next plugin, then the resolver, have it.

options says where the question came from: the importer and its namespace, the kind of import, any attributes from the import's with clause, and whether the question came from a plugin's own this.resolve.

A plugin that rewrites imports but must not answer its own questions checks options.custom and declines when it is true, because this.resolve sets it.

To claim a module has no side effects - so it can be dropped when nothing imports from it - return it with moduleSideEffects: true. This is the hook that tree shaking points at.

load

#
load?(this: PluginContext, id: string): LoadResult | string | null | undefined | Promise<…>

Asked for a module's contents, before the loader would read them. Return a LoadResult (or the source as a string) to claim the module, or null to let the next plugin and then the loader have it.

LoadResult carries code, the loader to interpret it with, a resolveDir for the module's own relative imports, a meta value ferried from the matching resolveId, and watch files/directories the answer depends on.

Together with resolveId, this is how virtual modules work: resolveId answers a synthetic id in a virtual namespace, load answers the code for it. An id in a non-file namespace is only ever read through a plugin, never the file system.

transform

#
transform?(
  this: PluginContext,
  code: string,
  id: string
): SourceDescription | string | null | undefined | Promise<…>

Transforms a module's source after it has been loaded and before it is parsed. Return the transformed source, optionally with a sourcemap for it. It is the place to strip a loader, rewrite imports textually, or preprocess a module that is not a plain JavaScript/TypeScript input.

buildEnd

#
buildEnd?(this: PluginContext, error?: Error): void | Promise<void>

Called at the end of the build, after every module is read. error is set when the build failed, which makes this the place to clean up either way - a plugin that opened something in buildStart closes it here.

generateBundle

#
generateBundle?(
  this: PluginContext,
  options: NormalizedOutputOptions,
  bundle: OutputBundle
): void | Promise<void>

Called after the output has been produced but before it is written, with a bundle the hook may mutate, add to, or delete from. This is where a plugin rewrites generated code, emits a sibling file with this.emitFile, or drops an output it wants kept out.

writeBundle

#
writeBundle?(
  this: PluginContext,
  options: NormalizedOutputOptions,
  bundle: OutputBundle
): void | Promise<void>

Called after the output has been written, with the same bundle. This is where a plugin reports on - or reads back - what actually landed on disk.

closeBundle

#
closeBundle?(this: PluginContext): void | Promise<void>

Called when the context is torn down. The last hook a plugin sees; the counterpart to buildStart when the plugin's life is the whole context rather than a single build.

See also

#

Edit this page on GitHub