Registry
Configure the registries to compile.
Overview
A registry is a directory of source files that belongs to a package. It is configured like a bundler: components are the entries, and files are the rules for installing them.
import { compile, writeRegistry } from "fuma-cli/compiler";
const out = await compile({
root: {
name: "acme-ui",
dir: "packages/ui/src",
components: {
button: "button.tsx",
},
files: {
"button.tsx": { type: "ui" },
"utils/*": { type: "lib" },
},
},
});
await writeRegistry(out, { dir: "public/registry" });compile() starts from the entries, follows the imports of each installed file, and returns the output in memory.
Options
| Option | Description |
|---|---|
name | Unique name, it prefixes file IDs like acme-ui:button.tsx. |
dir | The source directory, file paths are relative to it. |
components | What consumers can install, see below. |
files | Glob pattern to the rule for installing matched files, see Files. |
entries | Source files of package exports, see below. |
external | Paths the consumer is expected to own, imports of them are kept as-is. |
dependencies | Override the version of dependencies, null for the latest. |
subRegistries | Registries compiled together, files can reference each other across them. |
components
A component is a named list of entry files. Files imported by an entry are installed along, they don't need to be listed.
const registry: Registry = {
name: "acme-ui",
dir: "src",
components: {
// one entry
card: "card/index.tsx",
// multiple entries
form: ["form/input.tsx", "form/select.tsx"],
// with info
button: { title: "Button", description: "A clickable button", entry: "button.tsx" },
},
files: {
"**": { type: "components", target: "<dir>/*" },
},
};| Field | Description |
|---|---|
entry | Entry files, relative to dir. |
title | Display name. |
description | Short description. |
unlisted | Hide from component lists, still installable. |
- Every entry must match a rule in
files, otherwise the compilation fails. - A file can be an entry of multiple components. To offer several components as one, like
all, list all their entries.
Entries can be generated, a single rule covers all of them:
import path from "node:path";
import { glob } from "tinyglobby";
const components: Registry["components"] = {};
for (const file of await glob("icons/*.tsx", { cwd: "src" })) {
components[path.basename(file, ".tsx")] = file;
}entries
The compiler needs to know which source file is behind each export of your package, to keep package imports in installed code.
By default, it reads exports in package.json and maps dist to dir:
exports | Source file |
|---|---|
"./button": "./dist/button.js" | <dir>/button.tsx (or .ts) |
".": { "import": "./dist/index.js" } | <dir>/index.ts |
Specify it when your build doesn't mirror the source directory:
const registry: Registry = {
name: "acme-ui",
dir: "src",
entries: {
".": "index.ts",
"./button": "components/button.tsx",
},
};external
Some files only exist for your own app, and the consumer has their own version.
const registry: Registry = {
name: "docs",
dir: ".",
// `import { source } from "@/lib/source"` is kept as-is
external: ["lib/source.ts"],
};dependencies
Versions are taken from package.json of the registry (dependencies, peerDependencies, then devDependencies).
- Packages of registries in the same compilation use their current version.
- Protocols like
workspace:*are replaced with the installed version. - A package only listed as
@types/*is added as a dev dependency.
const registry: Registry = {
name: "acme-ui",
dir: "src",
dependencies: {
// always the latest
"acme-core": null,
// inlined by our bundler, but needed by installed files
"fast-content-type-parse": "^3.0.0",
},
};Sub Registries
const registry: Registry = {
name: "acme",
dir: "apps/docs",
subRegistries: [
{ name: "acme/ui", dir: "packages/ui/src" },
{ name: "acme/charts", dir: "packages/charts/src" },
],
};A file in acme/charts can import acme-ui/button or ../../ui/src/button, both resolve to the same installable file.
Last updated on