Plugins

Extend the installer with plugins.

Introduction

Plugins follow the shape of Rollup plugins: an object with a name and hooks.

import { ComponentInstaller, type InstallerPlugin } from "fuma-cli/registry/installer";

const myPlugin: InstallerPlugin = {
  name: "my-plugin",
  // hooks
};

new ComponentInstaller(connector, {
  plugins: [myPlugin],
});
HookWhenFirst result wins
resolveIdDeciding the output path of a file.Yes
transformAfter imports of a file are linked.No, chained
writeBundleAfter files are written.No

Hooks are called with a context as this:

PropertyDescription
cwdThe consumer's project.
baseDirAbsolute path of outDir.base.
getFramework()The configured/detected framework.

resolveId

Return the output path as id, or undefined for the default location.

An external file is not installed: imports of it point to id, and its own imports & dependencies are skipped.

import { existsSync } from "node:fs";
import path from "node:path";

// reuse the consumer's Shadcn UI components
const reuseUI: InstallerPlugin = {
  name: "reuse-ui",
  resolveId(file) {
    if (file.info.type !== "ui") return;

    const existing = path.join(this.baseDir, "components/ui", path.basename(file.path));
    if (existsSync(existing)) return { id: existing, external: true };
  },
};
FieldDescription
file.id<registry>:<path>
file.registryName of registry, after registryAliases.
file.pathPath in registry.
file.infoThe manifest entry: type, target, etc.

transform

Transform the code of JavaScript/TypeScript files, useful for codemods, license headers, or framework-specific tweaks.

const banner: InstallerPlugin = {
  name: "banner",
  transform(code, file) {
    return `// installed from ${file.id}\n${code}`;
  },
};

writeBundle

Receive the files that were written, e.g. to register them somewhere else in the consumer's project.

const log: InstallerPlugin = {
  name: "log",
  writeBundle(files) {
    for (const file of files) console.log(file.output);
  },
};

Last updated on

On this page