---
url: /apis/plugin-api/inter-plugin-communication.md
---
# Inter-plugin communication

At some point when using many dedicated plugins, there may be the need for unrelated plugins to be able to exchange information during the build. There are several mechanisms through which Rolldown makes this possible.

## Custom resolver options

Assume you have a plugin that should resolve an import to different ids depending on how the import was generated by another plugin. One way to achieve this would be to rewrite the import to use special proxy ids, e.g. a transpiled import via `require("foo")` in a CommonJS file could become a regular import with a special id `import "foo?require=true"` so that a resolver plugin knows this.

The problem here, however, is that this proxy id may or may not cause unintended side effects when passed to other resolvers because it does not really correspond to a file. Moreover, if the id is created by plugin `A` and the resolution happens in plugin `B`, it creates a dependency between these plugins so that `A` is not usable without `B`.

Custom resolver option offer a solution here by allowing to pass additional options for plugins when manually resolving a module via [`this.resolve`](/reference/Interface.PluginContext#resolve). This happens without changing the id and thus without impairing the ability for other plugins to resolve the module correctly if the intended target plugin is not present.

```js
function requestingPlugin() {
  return {
    name: 'requesting',
    async buildStart() {
      const resolution = await this.resolve('foo', undefined, {
        custom: { resolving: { specialResolution: true } },
      });
      console.log(resolution.id); // "special"
    },
  };
}

function resolvingPlugin() {
  return {
    name: 'resolving',
    resolveId(id, importer, { custom }) {
      if (custom.resolving?.specialResolution) {
        return 'special';
      }
      return null;
    },
  };
}
```

Note the convention that custom options should be added using a property corresponding to the plugin name of the resolving plugin. It is responsibility of the resolving plugin to specify which options it respects.

## Custom module meta-data

Plugins can annotate modules with custom meta-data which can be set by themselves and other plugins via the [`resolveId`](/reference/Interface.Plugin#resolveid), [`load`](/reference/Interface.Plugin#load), and [`transform`](/reference/Interface.Plugin#transform) hooks and accessed via [`this.getModuleInfo`](/reference/Interface.PluginContext#getmoduleinfo), [`this.load`](/reference/Interface.PluginContext#load) and the [`moduleParsed`](/reference/Interface.Plugin#moduleparsed) hook. This meta-data should always be `JSON.stringify`-able and will be persisted in the cache e.g. in watch mode.

```js
function annotatingPlugin() {
  return {
    name: 'annotating',
    transform(code, id) {
      if (thisModuleIsSpecial(code, id)) {
        return { meta: { annotating: { special: true } } };
      }
    },
  };
}

function readingPlugin() {
  let parentApi;
  return {
    name: 'reading',
    buildEnd() {
      const specialModules = Array.from(this.getModuleIds()).filter(
        (id) => this.getModuleInfo(id).meta.annotating?.special,
      );
      // do something with this list
    },
  };
}
```

Note the convention that plugins that add or modify data should use a property corresponding to the plugin name, in this case `annotating`. On the other hand, any plugin can read all meta-data from other plugins via `this.getModuleInfo`.

If several plugins add meta-data or meta-data is added in different hooks, then these `meta` objects will be merged shallowly. That means if plugin `first` adds `{meta: {first: {resolved: "first"}}}` in the resolveId hook and `{meta: {first: {loaded: "first"}}}` in the load hook while plugin `second` adds `{meta: {second: {transformed: "second"}}}` in the `transform` hook, then the resulting `meta` object will be `{first: {loaded: "first"}, second: {transformed: "second"}}`. Here the result of the `resolveId` hook will be overwritten by the result of the `load` hook as the plugin was both storing them under its `first` top-level property. The `transform` data of the other plugin on the other hand will be placed next to it.

The `meta` object of a module is created as soon as Rolldown starts loading a module and is updated for each lifecycle hook of the module. If you store a reference to this object, you can also update it manually. To access the meta object of a module that has not been loaded yet, you can trigger its creation and loading the module via [`this.load`](/reference/Interface.PluginContext#load):

```js
function plugin() {
  return {
    name: 'test',
    buildStart() {
      // trigger loading a module. We could also pass an initial
      // "meta" object here, but it would be ignored if the module
      // was already loaded via other means
      this.load({ id: 'my-id' });
      // the module info is now available, we do not need to await
      // this.load
      const meta = this.getModuleInfo('my-id').meta;
      // we can also modify meta manually now
      meta.test = { some: 'data' };
    },
  };
}
```

## Direct plugin communication

For any other kind of inter-plugin communication, we recommend the pattern below. Note that `api` will never conflict with any upcoming plugin hooks.

```js
function parentPlugin() {
  return {
    name: 'parent',
    api: {
      //...methods and properties exposed for other plugins
      doSomething(...args) {
        // do something interesting
      },
    },
    // ...plugin hooks
  };
}

function dependentPlugin() {
  let parentApi;
  return {
    name: 'dependent',
    buildStart({ plugins }) {
      const parentName = 'parent';
      const parentPlugin = plugins.find((plugin) => plugin.name === parentName);
      if (!parentPlugin) {
        // or handle this silently if it is optional
        throw new Error(`This plugin depends on the "${parentName}" plugin.`);
      }
      // now you can access the API methods in subsequent hooks
      parentApi = parentPlugin.api;
    },
    transform(code, id) {
      if (thereIsAReasonToDoSomething(id)) {
        parentApi.doSomething(id);
      }
    },
  };
}
```

## Descriptive metadata

Plugins can attach descriptive metadata to modules and to themselves. This metadata does is only informational and intended to be surfaced by tooling that inspects a build, for example [Vite devtools](https://github.com/vitejs/devtools).

### Module descriptions

Tools often show a module by its id, which is often opaque. For example, `\0vite/modulepreload-polyfill.js` gives no hint about what the module is. This is useful for virtual modules. A plugin can attach a human-readable [`description`](/reference/Interface.ModuleOptions#description) to a module, returned from the [`resolveId`](/reference/Interface.Plugin#resolveid), [`load`](/reference/Interface.Plugin#load), or [`transform`](/reference/Interface.Plugin#transform) hooks.

```js
function modulePreloadPolyfillPlugin() {
  return {
    name: 'vite:modulepreload-polyfill',
    load: {
      filter: { id: /^\0vite\/modulepreload-polyfill\.js$/ },
      handler(id) {
        return {
          code: '/* ... */',
          description: 'A polyfill for `link` tag with `rel="modulepreload"`',
        };
      },
    },
  };
}
```

### Plugin metadata

A single package often ships several plugins, and a plugin's `name` does not always reveal which package it came from. A plugin can declare its originating package name and version via the [`meta`](/reference/Interface.Plugin#meta) property of the plugin object, letting tooling attribute and group plugins by package. It is also possible to attach a short description of what the plugin does via the `description` property.

```js
function vuePlugin() {
  return {
    name: 'vite:vue',
    meta: {
      packageName: '@vitejs/plugin-vue',
      version: '5.0.0',
      description: 'Handles Vue single-file components',
    },
    // ...plugin hooks
  };
}
```

See the [`PluginMeta`](/reference/Interface.PluginMeta) type for the full shape.
