manteen-kit
v0.2.1
Published
Author Mantine component registries in Mantine's vocabulary and compile them to an interchange format any registry client can read.
Downloads
751
Maintainers
Readme
manteen-kit
Author Mantine component registries in Mantine's vocabulary; compile them to an interchange format any registry client can read.
For a complete repository layout, multi-file item, publishing workflow, direct-URL install and configured-namespace workflow, follow the registry-authoring guide.
bun add -d manteen-kitShips as Node-compatible ESM and requires Node >= 20.11. Any package manager works; authoring the registry in Bun is optional.
Why
Registries distribute code you're meant to own and edit — the composed layer on top of a
component library, not the library itself. Mantine has no such tooling, and the established
interchange format is shaped around a different design system: roughly a third of its item
schema (tailwind, cssVars, css, style, baseColor, iconLibrary, theme, font)
is inert for Mantine, and it can't express a version gate, a provider requirement, a theme
fragment, or Styles API selectors.
This kit lets you write the former and emit the latter.
manteen.registry.json ← your schema, your vocabulary
│ manteen-kit build
▼
r/*.json ← interchange format, machine-readableNothing you author mentions the wire vocabulary — it exists only in generated output. That keeps the output installable by any client that speaks the format while leaving the authoring layer entirely Mantine-shaped.
CLI
manteen-kit build [catalog.json] [outDir] # default: ./manteen.registry.json → ./public/r
manteen-kit merge-theme <base.ts> <fragment.ts> [--write] [--prefer incoming] [--json]build validates the catalog against the authoring schema and every emitted item against
the vendored interchange schema, exiting non-zero on either.
Authoring format
{
"name": "base",
"namespace": "@base", // bare `uses` names are qualified with this
"items": [
{
"name": "data-grid",
"kind": "block", // component | block | hook | lib | theme | file
"mantine": ">=9", // version GATE, checked — not an install directive
"provider": true, // requires MantineProvider
"npm": ["@mantine/core@^9"],
"uses": ["empty-state"], // bare = this registry; "@other/x" = cross-registry
"files": [
{ "path": "src/data-grid.tsx", "as": "component" },
{ "path": "src/use-data-grid.ts", "as": "hook" }
],
"themeFragment": "src/data-grid.theme.ts",
"docs": "Usage, source and attribution notes carried into the compiled item."
}
]
}Unknown fields are rejected rather than dropped, so the authoring format can't quietly drift toward the wire format.
docs is copied into the installable item document. Use it for human-facing usage, source and
attribution notes; it is not a substitute for shipping any license notice required with copied
source.
stylesApi is an optional author assertion for a component that genuinely exposes named selectors
through a public classNames/styles interface. Private CSS-module class names do not qualify, and
the kit carries the declaration without trying to infer or verify the component implementation:
{ "stylesApi": { "DataGrid": ["root", "header", "row"] } }props and usage are the same kind of author assertion, for documentation clients. props
documents the prop surface (keyed by exported component or hook name, each entry name/type
plus optional required/default/description); usage names a copy-ready example module
that is inlined at build time. The kit carries both verbatim — it never infers documentation
from source — and usage, like themeFragment, is deliberately not listed in files, so no
client installs it:
{
"props": { "DataGrid": [{ "name": "rows", "type": "DataGridRow[]", "required": true }] },
"usage": "src/data-grid.usage.tsx"
}mantine, provider, themeFragment, stylesApi, props and usage have no direct
wire-format equivalent, so
they compile into the installable item JSON under the open meta.mantine object. The registry index
contains only the discovery-safe requires and provider summary; stylesApi and the inlined theme
fragment remain item-detail metadata. Clients that understand these fields act on them; clients that
do not still install the files correctly.
themeFragment is deliberately not listed in files: an unaware client must not drop a
stray theme module into a project, and an aware one merges it instead.
Theme composition
The interchange format merges its own theme variables into a consumer's project. Mantine's
theme.ts has no equivalent, so a registry-shipped theme could only be overwritten
wholesale — one theme item per project, local edits lost on update.
manteen-kit merge-theme src/lib/theme.ts fragment.ts --write| Case | Behavior |
| --- | --- |
| Component missing from base | inserted, import added in the file's existing sort order |
| Component in both | .extend() objects merged, so defaultProps compose |
| Same leaf set by both | existing kept, conflict reported (--prefer incoming flips it) |
| Callback classNames/styles/vars | never merged — reported, since composing them changes runtime semantics |
| Entry that isn't X.extend({...}) | reported, left alone |
Comments and formatting survive; inserted nodes match the base file's indent and comma style. Idempotent, so it's safe to run on every install.
Programmatic API
import { compileRegistry, mergeThemeSource, validateCatalog } from "manteen-kit";
const { source, items, index, failures } = compileRegistry("./manteen.registry.json");
const { text, conflicts } = mergeThemeSource(baseSource, fragmentSource);Multiple registries
One toolchain, any number of catalogs — paths inside a catalog resolve against its own directory:
manteen-kit build registries/base/manteen.registry.json dist/base
manteen-kit build registries/product/manteen.registry.json dist/productAn item in @product can declare uses: ["@kit/callout", "@base/empty-state"], and a client
resolves across all of them in one install. fixtures/ contains exactly this arrangement and
the test suite exercises it.
Known limitation
Items are deduplicated by destination path, so two registries publishing an item of the
same name collide — the last one installed wins, silently. If @base/empty-state and
@house/empty-state have different prop signatures, install order decides which one your
project gets. Namespace item names, or don't ship overlapping names across registries you
expect to be installed together.
License
MIT
