Quick Start

Getting started with Fuma CLI.

Getting Started

Installation

npm i fuma-cli

Compile

Like a bundler config, a registry has entries and rules:

scripts/compile.ts
import { compile, writeRegistry } from "fuma-cli/compiler";

const out = await compile({
  root: {
    name: "acme-ui",
    // paths below are relative to it
    dir: "src",
    // what consumers install: name -> entry file
    components: {
      button: { title: "Button", entry: "button.tsx" },
    },
    // how files are installed: glob pattern -> rule
    files: {
      // `type` specifies where the file is installed
      "button.tsx": { type: "ui" },
      "utils/*": { type: "lib" },
    },
  },
});

await writeRegistry(out, {
  dir: "./public/registry",
  cleanDir: true,
});

Files imported by button.tsx are installed along, such as utils/cn.ts. They only need a matching rule, see Files for all options.

node ./scripts/compile.ts

Serve the output, here we assume it is available at https://acme-ui.com/registry.

Implement Installer

Initialize your own CLI package and install dependencies:

npm i fuma-cli @clack/prompts

Fuma CLI installer is headless, we will use @clack/prompts for the TUI.

my-cli/src/install.ts
import { ComponentInstaller } from "fuma-cli/registry/installer";
import { HttpRegistryConnector } from "fuma-cli/registry/connector";
import { confirm, isCancel, log } from "@clack/prompts";

const installer = new ComponentInstaller(
  new HttpRegistryConnector("https://acme-ui.com/registry"),
  {
    io: {
      async confirmFileOverride(file) {
        const value = await confirm({
          message: `Do you want to override ${file.output}?`,
          initialValue: false,
        });

        return !isCancel(value) && value;
      },
      onFileWritten(file) {
        log.step(file.output);
      },
    },
  },
);

export async function install(name: string) {
  const plan = await installer.install(name);
  const deps = await plan.deps();

  if (deps.hasRequired()) await deps.installRequired();
}
scripts/test-install.ts
import { install } from "../src/install";

await install("button");

Examples

Interactive Install UI

Let the user select components from the manifest.

import { autocompleteMultiselect, isCancel } from "@clack/prompts";

const manifest = await installer.fetchManifest();
const options = [];
for (const item of manifest.components) {
  if (item.unlisted) continue;
  options.push({ label: item.title ?? item.name, value: item.name, hint: item.description });
}

const value = await autocompleteMultiselect({
  message: "Select components to install",
  options,
});

if (!isCancel(value)) {
  for (const name of value) await install(name);
}

@fuma-cli/interactive ships this UI pre-built.

Last updated on

On this page