chore: add registry install flow
This commit is contained in:
@@ -14,9 +14,9 @@ The repo is still an internal/private monorepo, so Changesets is used conservati
|
||||
This repo now has two release-adjacent workflows:
|
||||
|
||||
- `Changeset Status` runs on pull requests and checks whether changes to releasable packages
|
||||
include a `.changeset/*.md` entry.
|
||||
include a `.changeset/*.md` entry, while also verifying that `registry/index.json` is current.
|
||||
- `Release Version PR` runs on `main` and opens or updates a version PR by running
|
||||
`pnpm changeset version`.
|
||||
`pnpm release:version`.
|
||||
|
||||
The PR check intentionally focuses on the releasable packages in scope today:
|
||||
|
||||
@@ -69,7 +69,7 @@ flow is enabled, treat Changesets as the source of truth for:
|
||||
- which changes deserve release notes
|
||||
- which internal dependency bumps should be coordinated
|
||||
|
||||
The current automation stops at version intent and version PR creation. It does not publish to a
|
||||
registry, create tags, or assume any package registry credentials exist yet.
|
||||
The current automation stops at version intent, registry metadata refresh, and version PR creation.
|
||||
It does not publish to npm, create tags, or assume any package registry credentials exist yet.
|
||||
|
||||
See `docs/releasing.md` for the expected workflow around creating and consuming changesets.
|
||||
|
||||
@@ -99,3 +99,6 @@ jobs:
|
||||
|
||||
- name: Validate pending changesets
|
||||
run: pnpm changeset:status --since "origin/${BASE_REF}"
|
||||
|
||||
- name: Validate registry metadata
|
||||
run: pnpm registry:check
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- name: Open or update version PR
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
version: pnpm changeset version
|
||||
version: pnpm release:version
|
||||
commit: "chore: version packages"
|
||||
title: "chore: version packages"
|
||||
env:
|
||||
|
||||
@@ -9,6 +9,7 @@ default styling with its own tokens, motion recipes, and component contract.
|
||||
- `packages/tokens`: theme tokens, motion tokens, and theme helpers
|
||||
- `packages/ui`: component source, variants, contracts, and tests
|
||||
- `apps/docs`: Storybook docs and usage reference
|
||||
- `registry`: generated registry metadata plus the source-copy install contract
|
||||
- `tests/e2e`: Playwright smoke coverage for high-value Storybook flows
|
||||
|
||||
## System principles
|
||||
@@ -50,6 +51,12 @@ Build Storybook:
|
||||
pnpm build:docs
|
||||
```
|
||||
|
||||
Build the registry metadata:
|
||||
|
||||
```bash
|
||||
pnpm registry:build
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
@@ -64,6 +71,12 @@ pnpm lint
|
||||
pnpm typecheck
|
||||
```
|
||||
|
||||
Install source-owned components into another project:
|
||||
|
||||
```bash
|
||||
pnpm registry:install --project ../acme-app button dialog
|
||||
```
|
||||
|
||||
## Workspace structure
|
||||
|
||||
```txt
|
||||
@@ -74,6 +87,7 @@ packages/
|
||||
ui/ Component source, variants, tests, and contracts
|
||||
tests/
|
||||
e2e/ Playwright smoke specs
|
||||
registry/ Generated item metadata for copy-in installs
|
||||
```
|
||||
|
||||
## How the component system is organized
|
||||
@@ -88,6 +102,17 @@ The system is layered:
|
||||
The current public component layer lives in `packages/ui/src/components`, with shared
|
||||
helpers in `packages/ui/src/lib`.
|
||||
|
||||
## Registry install flow
|
||||
|
||||
Cadence UI now ships a minimal internal registry flow for source-owned adoption.
|
||||
Consumers pin this repo to a reviewed commit or tag, then run the local installer to
|
||||
copy selected items into their own codebase.
|
||||
|
||||
- Registry metadata lives in `registry/index.json` and is generated by `pnpm registry:build`.
|
||||
- The installer copies components into `src/cadence-ui`, adds missing package dependencies,
|
||||
and writes `src/cadence-ui/.install-manifest.json` so upgrades can reuse the same item set.
|
||||
- Install and upgrade instructions live in [docs/registry.md](/Users/xd/project/cadence-ui/docs/registry.md).
|
||||
|
||||
## Docs and QA
|
||||
|
||||
Storybook is the main usage reference and review surface. Component stories are expected
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
# Registry Install and Upgrade
|
||||
|
||||
Cadence UI now supports a minimal internal registry flow for source-owned consumers.
|
||||
The goal is not package publishing yet. The goal is to make one reviewed repo state
|
||||
directly consumable by another app.
|
||||
|
||||
## What the registry contains
|
||||
|
||||
- `registry/config.json`: the authored registry format
|
||||
- `registry/index.json`: generated install metadata
|
||||
- `scripts/build-registry.mjs`: rebuilds and validates the generated index
|
||||
- `scripts/registry-install.mjs`: copies selected source files into a consumer project
|
||||
|
||||
`registry/index.json` is intentionally machine-readable. It records:
|
||||
|
||||
- installable item names
|
||||
- entrypoints and copied files
|
||||
- transitive source files required by each item
|
||||
- external package dependencies
|
||||
- the package versions that produced the registry snapshot
|
||||
|
||||
## Maintainer workflow
|
||||
|
||||
Whenever `packages/ui` or `packages/tokens` changes in a way that affects installable
|
||||
source, refresh the registry metadata:
|
||||
|
||||
```bash
|
||||
pnpm registry:build
|
||||
```
|
||||
|
||||
To verify that the generated file is current:
|
||||
|
||||
```bash
|
||||
pnpm registry:check
|
||||
```
|
||||
|
||||
The pull request workflow now runs `pnpm registry:check`, and the release version PR
|
||||
workflow refreshes the registry automatically via `pnpm release:version`.
|
||||
|
||||
## Consumer install flow
|
||||
|
||||
### 1. Pin the Cadence UI source
|
||||
|
||||
Consumers should install from a reviewed Cadence UI repo state, usually:
|
||||
|
||||
- the merge commit of a version PR, or
|
||||
- a maintainer-created tag that points at that versioned commit
|
||||
|
||||
The consumer does not need this repo as a runtime dependency. It only needs access to
|
||||
the checked-out source when running the installer.
|
||||
|
||||
### 2. Run the installer from the Cadence UI repo
|
||||
|
||||
From this repository checkout:
|
||||
|
||||
```bash
|
||||
pnpm registry:install --project ../acme-app button dialog
|
||||
```
|
||||
|
||||
The installer will:
|
||||
|
||||
- include `tokens` automatically when a component requires it
|
||||
- copy files into `src/cadence-ui`
|
||||
- preserve shared internal imports under `src/cadence-ui/components`, `src/cadence-ui/lib`,
|
||||
and `src/cadence-ui/tokens`
|
||||
- add any missing runtime dependencies to the consumer's `package.json`
|
||||
- write `src/cadence-ui/.install-manifest.json`
|
||||
|
||||
Useful flags:
|
||||
|
||||
- `--target-dir src/shared/cadence-ui`: customize the destination root
|
||||
- `--skip-package-json`: copy files without editing the consumer package manifest
|
||||
- `--dry-run`: preview copied files and package changes
|
||||
|
||||
## Consumer upgrade flow
|
||||
|
||||
Upgrades stay source-owned. There is no generated wrapper or hidden runtime.
|
||||
|
||||
1. Update the Cadence UI checkout to the new reviewed commit or tag.
|
||||
2. Re-run the installer against the same consumer project.
|
||||
|
||||
```bash
|
||||
pnpm registry:install --project ../acme-app
|
||||
```
|
||||
|
||||
If no item names are provided, the installer reuses the list stored in
|
||||
`src/cadence-ui/.install-manifest.json`.
|
||||
|
||||
After that:
|
||||
|
||||
- review the consumer diff
|
||||
- run the consumer package manager install if `package.json` changed
|
||||
- verify imports still point at `src/cadence-ui/tokens/styles.css`
|
||||
|
||||
## Expected consumer import points
|
||||
|
||||
At minimum, the consumer app should import:
|
||||
|
||||
- `src/cadence-ui/tokens/styles.css` from its global app stylesheet or entry module
|
||||
|
||||
If the consumer wants the theme helpers, they can also import from:
|
||||
|
||||
- `src/cadence-ui/tokens/index.ts`
|
||||
|
||||
## Current scope
|
||||
|
||||
This registry flow is intentionally minimal:
|
||||
|
||||
- it copies source files
|
||||
- it writes dependency intent into the consumer app
|
||||
- it supports reinstalling the same item set for upgrades
|
||||
|
||||
It does not yet:
|
||||
|
||||
- publish to npm
|
||||
- host a remote registry API
|
||||
- auto-tag or auto-publish releases
|
||||
- resolve consumer-specific codemods during upgrade
|
||||
+28
-14
@@ -9,6 +9,7 @@ The current goal is modest:
|
||||
- keep release notes attached to the changes that caused them
|
||||
- avoid inventing ad hoc version bumps when the component system evolves
|
||||
- make release intent enforceable in CI before package work merges
|
||||
- keep the generated registry metadata aligned with versioned source
|
||||
|
||||
## Current assumptions
|
||||
|
||||
@@ -68,6 +69,7 @@ At minimum, run:
|
||||
|
||||
```bash
|
||||
pnpm lint
|
||||
pnpm registry:check
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
```
|
||||
@@ -104,10 +106,10 @@ keep the dependency graph coherent without requiring manual package edits.
|
||||
|
||||
### 4. Version the packages
|
||||
|
||||
When it is time to cut a release manually, run the Changesets version step:
|
||||
When it is time to cut a release manually, run the repo version step:
|
||||
|
||||
```bash
|
||||
pnpm changeset version
|
||||
pnpm release:version
|
||||
```
|
||||
|
||||
That step is expected to:
|
||||
@@ -115,6 +117,7 @@ That step is expected to:
|
||||
- update package versions
|
||||
- update internal dependency ranges where needed
|
||||
- consume the pending changeset files
|
||||
- rebuild `registry/index.json` so the copy-in install metadata stays version-aligned
|
||||
|
||||
Review the resulting package diffs carefully before merging.
|
||||
|
||||
@@ -122,17 +125,19 @@ On `main`, the repository also runs a `Release Version PR` workflow that opens o
|
||||
version PR with the same command. That workflow is intentionally limited to version-file updates;
|
||||
it does not publish packages.
|
||||
|
||||
### 5. Publish or tag
|
||||
### 5. Distribute the versioned source
|
||||
|
||||
Publishing is not fully wired in this repo yet, so treat this step as pending infrastructure.
|
||||
Publishing is not fully wired in this repo yet, so the consumable artifact is the versioned
|
||||
repository state itself.
|
||||
|
||||
The intended future flow is:
|
||||
After the version PR merges:
|
||||
|
||||
```bash
|
||||
pnpm changeset publish
|
||||
```
|
||||
- keep the merge commit as a stable install point, or create a maintainer tag for it
|
||||
- treat the committed `registry/index.json` as the install contract for that repo state
|
||||
- point consumers at that commit or tag when they run `pnpm registry:install`
|
||||
|
||||
But until registry, auth, and CI behavior are explicit, do not assume publish is automated.
|
||||
See [docs/registry.md](/Users/xd/project/cadence-ui/docs/registry.md) for the consumer
|
||||
install and upgrade flow.
|
||||
|
||||
## CI workflows
|
||||
|
||||
@@ -153,16 +158,24 @@ pnpm changeset:status --since origin/main
|
||||
|
||||
This validates that pending changesets resolve cleanly against the base branch.
|
||||
|
||||
The workflow also runs:
|
||||
|
||||
```bash
|
||||
pnpm registry:check
|
||||
```
|
||||
|
||||
This prevents registry metadata drift from merging unnoticed.
|
||||
|
||||
### Version PR workflow
|
||||
|
||||
The `Release Version PR` workflow runs on pushes to `main` and on manual dispatch. It:
|
||||
|
||||
- installs dependencies
|
||||
- runs `pnpm changeset version`
|
||||
- runs `pnpm release:version`
|
||||
- opens or updates a version PR using `changesets/action`
|
||||
|
||||
This is a versioning skeleton, not a publish pipeline. It is safe to enable before registry
|
||||
credentials or publish commands exist.
|
||||
This is still a versioning skeleton, not a publish pipeline, but the version PR now refreshes
|
||||
the committed registry metadata that consumers install from.
|
||||
|
||||
## Future publish prerequisites
|
||||
|
||||
@@ -183,5 +196,6 @@ Before turning the version workflow into a publish workflow, the repo still need
|
||||
|
||||
## Main-thread follow-up still needed
|
||||
|
||||
This baseline now covers changeset intent checks in PRs and version PR creation on `main`.
|
||||
What remains is the publish decision and any registry-specific automation.
|
||||
This baseline now covers changeset intent checks in PRs, registry metadata validation, and
|
||||
version PR creation on `main`. What remains is the publish decision, tagging policy, and any
|
||||
registry-specific automation beyond source-copy installs.
|
||||
|
||||
+29
-1
@@ -7,14 +7,42 @@ import tseslint from "typescript-eslint";
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: [
|
||||
"**/.artifacts/**",
|
||||
"**/.tmp-home/**",
|
||||
"**/dist/**",
|
||||
"**/node_modules/**",
|
||||
"**/output/**",
|
||||
"**/playwright-report/**",
|
||||
"**/storybook-static/**",
|
||||
"**/coverage/**"
|
||||
"**/coverage/**",
|
||||
"**/test-results/**"
|
||||
]
|
||||
},
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
files: ["**/*.{js,mjs}"],
|
||||
languageOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
globals: {
|
||||
...globals.node
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ["**/*.cjs"],
|
||||
languageOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "commonjs",
|
||||
globals: {
|
||||
...globals.node
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-require-imports": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ["**/*.{ts,tsx,mts,cts}"],
|
||||
languageOptions: {
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"changeset:status": "changeset status --verbose",
|
||||
"dev:docs": "pnpm --dir apps/docs run storybook",
|
||||
"lint": "eslint .",
|
||||
"registry:build": "node ./scripts/build-registry.mjs",
|
||||
"registry:check": "node ./scripts/build-registry.mjs --check",
|
||||
"registry:install": "node ./scripts/registry-install.mjs",
|
||||
"release:version": "pnpm changeset version && pnpm registry:build",
|
||||
"test": "pnpm --filter @ai-ui/ui test",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:smoke": "playwright test tests/e2e/storybook-smoke.spec.ts",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Registry
|
||||
|
||||
This directory holds the internal Cadence UI registry contract.
|
||||
|
||||
- `config.json` is the maintained source of registry intent.
|
||||
- `index.json` is generated by `pnpm registry:build`.
|
||||
|
||||
The generated index is what the installer consumes. It records:
|
||||
|
||||
- installable item names
|
||||
- copied source files
|
||||
- required package dependencies
|
||||
- package versions for the registry snapshot
|
||||
|
||||
Consumers should not edit `index.json` by hand. Maintainers should regenerate it after
|
||||
source changes that affect installable components or tokens.
|
||||
|
||||
See [docs/registry.md](/Users/xd/project/cadence-ui/docs/registry.md) for the install
|
||||
and upgrade flow.
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"libraryName": "cadence-ui",
|
||||
"defaultTargetDir": "src/cadence-ui",
|
||||
"tokens": {
|
||||
"name": "tokens",
|
||||
"description": "Base CSS variables, motion tokens, and theme helpers.",
|
||||
"entrypoints": [
|
||||
"packages/tokens/src/styles.css",
|
||||
"packages/tokens/src/index.ts"
|
||||
],
|
||||
"targetSubdir": "tokens"
|
||||
},
|
||||
"ui": {
|
||||
"componentsDir": "packages/ui/src/components",
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"targetSubdir": "."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,913 @@
|
||||
{
|
||||
"generatedBy": "scripts/build-registry.mjs",
|
||||
"install": {
|
||||
"defaultTargetDir": "src/cadence-ui",
|
||||
"manifestFile": ".install-manifest.json"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"description": "Base CSS variables, motion tokens, and theme helpers.",
|
||||
"displayName": "Tokens",
|
||||
"entrypoints": [
|
||||
"packages/tokens/src/styles.css",
|
||||
"packages/tokens/src/index.ts"
|
||||
],
|
||||
"files": [
|
||||
"packages/tokens/src/base.css",
|
||||
"packages/tokens/src/index.ts",
|
||||
"packages/tokens/src/motion.css",
|
||||
"packages/tokens/src/styles.css",
|
||||
"packages/tokens/src/tokens.css"
|
||||
],
|
||||
"kind": "tokens",
|
||||
"name": "tokens",
|
||||
"packageDependencies": {},
|
||||
"requires": [],
|
||||
"sourcePackage": "@ai-ui/tokens",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui/tokens"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Alert component.",
|
||||
"displayName": "Alert",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/alert.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/alert.tsx",
|
||||
"packages/ui/src/components/alert.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "alert",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Avatar component.",
|
||||
"displayName": "Avatar",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/avatar.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/avatar.tsx",
|
||||
"packages/ui/src/components/avatar.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "avatar",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-avatar": "^1.1.11",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Badge component.",
|
||||
"displayName": "Badge",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/badge.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/badge.tsx",
|
||||
"packages/ui/src/components/badge.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "badge",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Button component.",
|
||||
"displayName": "Button",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/button.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/button.tsx",
|
||||
"packages/ui/src/components/button.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "button",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"motion": "^12.38.0",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Card component.",
|
||||
"displayName": "Card",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/card.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/card.tsx",
|
||||
"packages/ui/src/components/card.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "card",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Checkbox component.",
|
||||
"displayName": "Checkbox",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/checkbox.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/checkbox.tsx",
|
||||
"packages/ui/src/components/checkbox.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "checkbox",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Combobox component.",
|
||||
"displayName": "Combobox",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/combobox.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/combobox.tsx",
|
||||
"packages/ui/src/components/combobox.variants.ts",
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "combobox",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Command component.",
|
||||
"displayName": "Command",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/command.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/command.tsx",
|
||||
"packages/ui/src/components/command.variants.ts",
|
||||
"packages/ui/src/components/dialog.tsx",
|
||||
"packages/ui/src/components/dialog.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "command",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Data Table component.",
|
||||
"displayName": "Data Table",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/data-table.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/button.tsx",
|
||||
"packages/ui/src/components/button.variants.ts",
|
||||
"packages/ui/src/components/checkbox.tsx",
|
||||
"packages/ui/src/components/checkbox.variants.ts",
|
||||
"packages/ui/src/components/data-table.tsx",
|
||||
"packages/ui/src/components/data-table.variants.ts",
|
||||
"packages/ui/src/components/empty-state.tsx",
|
||||
"packages/ui/src/components/empty-state.variants.ts",
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/input.tsx",
|
||||
"packages/ui/src/components/input.variants.ts",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/components/select.tsx",
|
||||
"packages/ui/src/components/select.variants.ts",
|
||||
"packages/ui/src/components/skeleton.tsx",
|
||||
"packages/ui/src/components/spinner.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "data-table",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"motion": "^12.38.0",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Dialog component.",
|
||||
"displayName": "Dialog",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/dialog.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/dialog.tsx",
|
||||
"packages/ui/src/components/dialog.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "dialog",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Dropdown Menu component.",
|
||||
"displayName": "Dropdown Menu",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/dropdown-menu.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/dropdown-menu.tsx",
|
||||
"packages/ui/src/components/dropdown-menu.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "dropdown-menu",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Empty State component.",
|
||||
"displayName": "Empty State",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/empty-state.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/empty-state.tsx",
|
||||
"packages/ui/src/components/empty-state.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "empty-state",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Field component.",
|
||||
"displayName": "Field",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/field.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "field",
|
||||
"packageDependencies": {
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Form component.",
|
||||
"displayName": "Form",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/form.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/form.tsx",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "form",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"react-hook-form": "^7.71.2",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Input component.",
|
||||
"displayName": "Input",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/input.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/input.tsx",
|
||||
"packages/ui/src/components/input.variants.ts",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "input",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Label component.",
|
||||
"displayName": "Label",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/label.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "label",
|
||||
"packageDependencies": {
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Popover component.",
|
||||
"displayName": "Popover",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/popover.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/popover.tsx",
|
||||
"packages/ui/src/components/popover.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "popover",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Progress component.",
|
||||
"displayName": "Progress",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/progress.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/progress.tsx",
|
||||
"packages/ui/src/components/progress.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "progress",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-progress": "^1.1.8",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Radio Group component.",
|
||||
"displayName": "Radio Group",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/radio-group.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/radio-group.tsx",
|
||||
"packages/ui/src/components/radio-group.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "radio-group",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Select component.",
|
||||
"displayName": "Select",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/select.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/components/select.tsx",
|
||||
"packages/ui/src/components/select.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "select",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Separator component.",
|
||||
"displayName": "Separator",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/separator.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/separator.tsx",
|
||||
"packages/ui/src/components/separator.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "separator",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Sheet component.",
|
||||
"displayName": "Sheet",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/sheet.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/dialog.variants.ts",
|
||||
"packages/ui/src/components/sheet.tsx",
|
||||
"packages/ui/src/components/sheet.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "sheet",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Skeleton component.",
|
||||
"displayName": "Skeleton",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/skeleton.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/skeleton.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "skeleton",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Spinner component.",
|
||||
"displayName": "Spinner",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/spinner.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/spinner.tsx",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "spinner",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Switch component.",
|
||||
"displayName": "Switch",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/switch.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/switch.tsx",
|
||||
"packages/ui/src/components/switch.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "switch",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Tabs component.",
|
||||
"displayName": "Tabs",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/tabs.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/tabs.tsx",
|
||||
"packages/ui/src/components/tabs.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "tabs",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Textarea component.",
|
||||
"displayName": "Textarea",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/textarea.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/field.tsx",
|
||||
"packages/ui/src/components/label.tsx",
|
||||
"packages/ui/src/components/textarea.tsx",
|
||||
"packages/ui/src/components/textarea.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "textarea",
|
||||
"packageDependencies": {
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Toast component.",
|
||||
"displayName": "Toast",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/toast.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/toast.tsx",
|
||||
"packages/ui/src/components/toast.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts",
|
||||
"packages/ui/src/lib/motion.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "toast",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
},
|
||||
{
|
||||
"description": "Source-owned Tooltip component.",
|
||||
"displayName": "Tooltip",
|
||||
"entrypoints": [
|
||||
"packages/ui/src/components/tooltip.tsx"
|
||||
],
|
||||
"files": [
|
||||
"packages/ui/src/components/tooltip.tsx",
|
||||
"packages/ui/src/components/tooltip.variants.ts",
|
||||
"packages/ui/src/lib/cn.ts",
|
||||
"packages/ui/src/lib/contracts.ts",
|
||||
"packages/ui/src/lib/cva.ts"
|
||||
],
|
||||
"kind": "component",
|
||||
"name": "tooltip",
|
||||
"packageDependencies": {
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"react": "^18.3.1 || ^19.0.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"requires": [
|
||||
"tokens"
|
||||
],
|
||||
"sourcePackage": "@ai-ui/ui",
|
||||
"sourceVersion": "0.0.0",
|
||||
"targetDirectory": "src/cadence-ui"
|
||||
}
|
||||
],
|
||||
"library": {
|
||||
"name": "cadence-ui",
|
||||
"packageManager": "pnpm@10.25.0",
|
||||
"packages": {
|
||||
"@ai-ui/tokens": "0.0.0",
|
||||
"@ai-ui/ui": "0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { builtinModules } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import ts from "typescript";
|
||||
|
||||
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(scriptDir, "..");
|
||||
const registryDir = path.join(repoRoot, "registry");
|
||||
const registryConfigPath = path.join(registryDir, "config.json");
|
||||
const registryIndexPath = path.join(registryDir, "index.json");
|
||||
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const checkMode = args.has("--check");
|
||||
|
||||
const uiPackagePath = path.join(repoRoot, "packages", "ui", "package.json");
|
||||
const tokensPackagePath = path.join(repoRoot, "packages", "tokens", "package.json");
|
||||
const rootPackagePath = path.join(repoRoot, "package.json");
|
||||
|
||||
function toPosixPath(value) {
|
||||
return value.split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
|
||||
async function readJson(filePath) {
|
||||
const source = await fs.readFile(filePath, "utf8");
|
||||
return JSON.parse(source);
|
||||
}
|
||||
|
||||
function isBuiltinSpecifier(specifier) {
|
||||
return specifier.startsWith("node:") || builtinModules.includes(specifier);
|
||||
}
|
||||
|
||||
function normalizePackageName(specifier) {
|
||||
if (specifier.startsWith("@")) {
|
||||
const [scope, name] = specifier.split("/");
|
||||
return `${scope}/${name}`;
|
||||
}
|
||||
|
||||
return specifier.split("/")[0];
|
||||
}
|
||||
|
||||
function getDisplayName(name) {
|
||||
return name
|
||||
.split("-")
|
||||
.filter(Boolean)
|
||||
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function getScriptSpecifiers(sourceText) {
|
||||
const { importedFiles } = ts.preProcessFile(sourceText, true, true);
|
||||
return importedFiles.map((entry) => entry.fileName);
|
||||
}
|
||||
|
||||
function getCssSpecifiers(sourceText) {
|
||||
const specifiers = [];
|
||||
const pattern = /@import\s+["']([^"']+)["']/g;
|
||||
|
||||
for (const match of sourceText.matchAll(pattern)) {
|
||||
specifiers.push(match[1]);
|
||||
}
|
||||
|
||||
return specifiers;
|
||||
}
|
||||
|
||||
async function resolveLocalImport(fromFile, specifier) {
|
||||
const basePath = path.resolve(path.dirname(fromFile), specifier);
|
||||
const candidates = [
|
||||
basePath,
|
||||
`${basePath}.ts`,
|
||||
`${basePath}.tsx`,
|
||||
`${basePath}.js`,
|
||||
`${basePath}.mjs`,
|
||||
`${basePath}.cjs`,
|
||||
`${basePath}.css`,
|
||||
path.join(basePath, "index.ts"),
|
||||
path.join(basePath, "index.tsx"),
|
||||
path.join(basePath, "index.js")
|
||||
];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
const stats = await fs.stat(candidate);
|
||||
|
||||
if (stats.isFile()) {
|
||||
return candidate;
|
||||
}
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unable to resolve "${specifier}" from ${toPosixPath(path.relative(repoRoot, fromFile))}.`
|
||||
);
|
||||
}
|
||||
|
||||
async function collectSourceGraph(entrypoints) {
|
||||
const filesToVisit = [...entrypoints];
|
||||
const visited = new Set();
|
||||
const externalPackages = new Set();
|
||||
|
||||
while (filesToVisit.length > 0) {
|
||||
const currentFile = filesToVisit.pop();
|
||||
|
||||
if (!currentFile || visited.has(currentFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visited.add(currentFile);
|
||||
|
||||
const sourceText = await fs.readFile(currentFile, "utf8");
|
||||
const localSpecifiers = currentFile.endsWith(".css")
|
||||
? getCssSpecifiers(sourceText)
|
||||
: getScriptSpecifiers(sourceText);
|
||||
|
||||
for (const specifier of localSpecifiers) {
|
||||
if (specifier.startsWith(".") || specifier.startsWith("..")) {
|
||||
filesToVisit.push(await resolveLocalImport(currentFile, specifier));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBuiltinSpecifier(specifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
externalPackages.add(normalizePackageName(specifier));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
externalPackages: [...externalPackages].sort(),
|
||||
files: [...visited]
|
||||
.map((filePath) => toPosixPath(path.relative(repoRoot, filePath)))
|
||||
.sort()
|
||||
};
|
||||
}
|
||||
|
||||
async function discoverComponentEntrypoints(componentsDir) {
|
||||
const absoluteDir = path.join(repoRoot, componentsDir);
|
||||
const entries = await fs.readdir(absoluteDir, { withFileTypes: true });
|
||||
|
||||
return entries
|
||||
.filter((entry) => entry.isFile())
|
||||
.map((entry) => entry.name)
|
||||
.filter((fileName) => fileName.endsWith(".tsx"))
|
||||
.filter((fileName) => !fileName.endsWith(".test.tsx"))
|
||||
.sort()
|
||||
.map((fileName) => ({
|
||||
description: `Source-owned ${getDisplayName(fileName.replace(/\.tsx$/, ""))} component.`,
|
||||
entrypoints: [toPosixPath(path.join(componentsDir, fileName))],
|
||||
kind: "component",
|
||||
name: fileName.replace(/\.tsx$/, "")
|
||||
}));
|
||||
}
|
||||
|
||||
function createDependencyLookup(...packageJsonFiles) {
|
||||
const lookup = new Map();
|
||||
|
||||
for (const packageJson of packageJsonFiles) {
|
||||
for (const section of [
|
||||
packageJson.peerDependencies ?? {},
|
||||
packageJson.dependencies ?? {},
|
||||
packageJson.devDependencies ?? {}
|
||||
]) {
|
||||
for (const [name, range] of Object.entries(section)) {
|
||||
if (!lookup.has(name)) {
|
||||
lookup.set(name, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lookup;
|
||||
}
|
||||
|
||||
async function buildRegistryIndex() {
|
||||
const [registryConfig, rootPackage, uiPackage, tokensPackage] = await Promise.all([
|
||||
readJson(registryConfigPath),
|
||||
readJson(rootPackagePath),
|
||||
readJson(uiPackagePath),
|
||||
readJson(tokensPackagePath)
|
||||
]);
|
||||
|
||||
const dependencyLookup = createDependencyLookup(uiPackage, tokensPackage, rootPackage);
|
||||
|
||||
const itemDefinitions = [
|
||||
{
|
||||
...registryConfig.tokens,
|
||||
kind: "tokens",
|
||||
requires: [],
|
||||
sourcePackage: tokensPackage.name
|
||||
},
|
||||
...(await discoverComponentEntrypoints(registryConfig.ui.componentsDir)).map((item) => ({
|
||||
...item,
|
||||
requires: [...registryConfig.ui.requires],
|
||||
sourcePackage: uiPackage.name
|
||||
}))
|
||||
];
|
||||
|
||||
const items = [];
|
||||
|
||||
for (const definition of itemDefinitions) {
|
||||
const entrypoints = definition.entrypoints.map((entrypoint) =>
|
||||
path.join(repoRoot, ...entrypoint.split("/"))
|
||||
);
|
||||
const sourceGraph = await collectSourceGraph(entrypoints);
|
||||
const packageDependencies = {};
|
||||
|
||||
for (const dependencyName of sourceGraph.externalPackages) {
|
||||
const range = dependencyLookup.get(dependencyName);
|
||||
|
||||
if (!range) {
|
||||
throw new Error(
|
||||
`Missing dependency range for "${dependencyName}" while building registry item "${definition.name}".`
|
||||
);
|
||||
}
|
||||
|
||||
packageDependencies[dependencyName] = range;
|
||||
}
|
||||
|
||||
items.push({
|
||||
description: definition.description,
|
||||
displayName: getDisplayName(definition.name),
|
||||
entrypoints: definition.entrypoints,
|
||||
files: sourceGraph.files,
|
||||
kind: definition.kind,
|
||||
name: definition.name,
|
||||
packageDependencies,
|
||||
requires: definition.requires,
|
||||
sourcePackage: definition.sourcePackage,
|
||||
sourceVersion:
|
||||
definition.sourcePackage === uiPackage.name ? uiPackage.version : tokensPackage.version,
|
||||
targetDirectory:
|
||||
definition.kind === "tokens"
|
||||
? toPosixPath(
|
||||
path.posix.join(registryConfig.defaultTargetDir, registryConfig.tokens.targetSubdir)
|
||||
)
|
||||
: toPosixPath(
|
||||
path.posix.join(
|
||||
registryConfig.defaultTargetDir,
|
||||
registryConfig.ui.targetSubdir === "."
|
||||
? ""
|
||||
: registryConfig.ui.targetSubdir
|
||||
)
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
generatedBy: "scripts/build-registry.mjs",
|
||||
install: {
|
||||
defaultTargetDir: registryConfig.defaultTargetDir,
|
||||
manifestFile: ".install-manifest.json"
|
||||
},
|
||||
items,
|
||||
library: {
|
||||
name: registryConfig.libraryName,
|
||||
packageManager: rootPackage.packageManager,
|
||||
packages: {
|
||||
[tokensPackage.name]: tokensPackage.version,
|
||||
[uiPackage.name]: uiPackage.version
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const nextIndex = await buildRegistryIndex();
|
||||
const nextContent = `${JSON.stringify(nextIndex, null, 2)}\n`;
|
||||
|
||||
if (checkMode) {
|
||||
let currentContent = "";
|
||||
|
||||
try {
|
||||
currentContent = await fs.readFile(registryIndexPath, "utf8");
|
||||
} catch {
|
||||
currentContent = "";
|
||||
}
|
||||
|
||||
if (currentContent !== nextContent) {
|
||||
console.error("registry/index.json is out of date. Run `pnpm registry:build`.");
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("registry/index.json is up to date.");
|
||||
return;
|
||||
}
|
||||
|
||||
await fs.mkdir(registryDir, { recursive: true });
|
||||
await fs.writeFile(registryIndexPath, nextContent);
|
||||
console.log(`Wrote ${toPosixPath(path.relative(repoRoot, registryIndexPath))}.`);
|
||||
}
|
||||
|
||||
await main();
|
||||
@@ -0,0 +1,327 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(scriptDir, "..");
|
||||
const registryIndexPath = path.join(repoRoot, "registry", "index.json");
|
||||
|
||||
function toPosixPath(value) {
|
||||
return value.split(path.sep).join(path.posix.sep);
|
||||
}
|
||||
|
||||
async function readJson(filePath) {
|
||||
const source = await fs.readFile(filePath, "utf8");
|
||||
return JSON.parse(source);
|
||||
}
|
||||
|
||||
function sortObject(input) {
|
||||
return Object.fromEntries(Object.entries(input).sort(([left], [right]) => left.localeCompare(right)));
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = {
|
||||
dryRun: false,
|
||||
project: process.cwd(),
|
||||
skipPackageJson: false,
|
||||
targetDir: null
|
||||
};
|
||||
const items = [];
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const current = argv[index];
|
||||
|
||||
if (current === "--dry-run") {
|
||||
options.dryRun = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current === "--skip-package-json") {
|
||||
options.skipPackageJson = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current === "--project" || current === "--target-dir") {
|
||||
const next = argv[index + 1];
|
||||
|
||||
if (!next) {
|
||||
throw new Error(`Expected a value after ${current}.`);
|
||||
}
|
||||
|
||||
if (current === "--project") {
|
||||
options.project = next;
|
||||
} else {
|
||||
options.targetDir = next;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.startsWith("--")) {
|
||||
throw new Error(`Unknown option: ${current}`);
|
||||
}
|
||||
|
||||
items.push(current);
|
||||
}
|
||||
|
||||
return {
|
||||
items,
|
||||
options
|
||||
};
|
||||
}
|
||||
|
||||
function mapSourceToTarget(sourcePath, targetDir) {
|
||||
if (sourcePath.startsWith("packages/ui/src/")) {
|
||||
return toPosixPath(path.posix.join(targetDir, sourcePath.replace("packages/ui/src/", "")));
|
||||
}
|
||||
|
||||
if (sourcePath.startsWith("packages/tokens/src/")) {
|
||||
return toPosixPath(
|
||||
path.posix.join(targetDir, "tokens", sourcePath.replace("packages/tokens/src/", ""))
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported registry source path: ${sourcePath}`);
|
||||
}
|
||||
|
||||
function resolveItems(registryIndex, requestedItems) {
|
||||
const itemLookup = new Map(registryIndex.items.map((item) => [item.name, item]));
|
||||
const resolved = new Map();
|
||||
const stack = [...requestedItems];
|
||||
|
||||
while (stack.length > 0) {
|
||||
const name = stack.pop();
|
||||
const item = itemLookup.get(name);
|
||||
|
||||
if (!item) {
|
||||
const available = [...itemLookup.keys()].sort().join(", ");
|
||||
throw new Error(`Unknown registry item "${name}". Available items: ${available}.`);
|
||||
}
|
||||
|
||||
if (resolved.has(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
resolved.set(name, item);
|
||||
|
||||
for (const dependency of item.requires) {
|
||||
stack.push(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
return [...resolved.values()].sort((left, right) => left.name.localeCompare(right.name));
|
||||
}
|
||||
|
||||
async function readInstallManifest(manifestPath) {
|
||||
try {
|
||||
return await readJson(manifestPath);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function collectPackageDependencies(items) {
|
||||
const dependencies = {};
|
||||
|
||||
for (const item of items) {
|
||||
for (const [name, range] of Object.entries(item.packageDependencies)) {
|
||||
dependencies[name] = range;
|
||||
}
|
||||
}
|
||||
|
||||
return sortObject(dependencies);
|
||||
}
|
||||
|
||||
function upsertDependencySections(packageJson, dependencies) {
|
||||
const sections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
||||
const added = {};
|
||||
const conflicts = [];
|
||||
const nextPackageJson = JSON.parse(JSON.stringify(packageJson));
|
||||
nextPackageJson.dependencies = nextPackageJson.dependencies ?? {};
|
||||
|
||||
for (const [name, range] of Object.entries(dependencies)) {
|
||||
let existingSection = null;
|
||||
let existingRange = null;
|
||||
|
||||
for (const section of sections) {
|
||||
const value = nextPackageJson[section]?.[name];
|
||||
|
||||
if (typeof value === "string") {
|
||||
existingSection = section;
|
||||
existingRange = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingSection) {
|
||||
nextPackageJson.dependencies[name] = range;
|
||||
added[name] = range;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existingRange !== range) {
|
||||
conflicts.push({
|
||||
existingRange,
|
||||
name,
|
||||
requiredRange: range,
|
||||
section: existingSection
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
nextPackageJson.dependencies = sortObject(nextPackageJson.dependencies);
|
||||
|
||||
return {
|
||||
added,
|
||||
conflicts,
|
||||
packageJson: nextPackageJson
|
||||
};
|
||||
}
|
||||
|
||||
async function copyRegistryFiles({ dryRun, items, projectRoot, targetDir }) {
|
||||
const writtenFiles = [];
|
||||
const updatedFiles = [];
|
||||
const unchangedFiles = [];
|
||||
|
||||
for (const item of items) {
|
||||
for (const sourcePath of item.files) {
|
||||
const sourceFile = path.join(repoRoot, ...sourcePath.split("/"));
|
||||
const targetRelativePath = mapSourceToTarget(sourcePath, targetDir);
|
||||
const targetFile = path.join(projectRoot, ...targetRelativePath.split("/"));
|
||||
const content = await fs.readFile(sourceFile, "utf8");
|
||||
let existingContent = null;
|
||||
|
||||
try {
|
||||
existingContent = await fs.readFile(targetFile, "utf8");
|
||||
} catch {
|
||||
existingContent = null;
|
||||
}
|
||||
|
||||
if (existingContent === content) {
|
||||
unchangedFiles.push(targetRelativePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
await fs.mkdir(path.dirname(targetFile), { recursive: true });
|
||||
await fs.writeFile(targetFile, content);
|
||||
}
|
||||
|
||||
if (existingContent === null) {
|
||||
writtenFiles.push(targetRelativePath);
|
||||
} else {
|
||||
updatedFiles.push(targetRelativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
unchangedFiles,
|
||||
updatedFiles,
|
||||
writtenFiles
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { items: requestedItems, options } = parseArgs(process.argv.slice(2));
|
||||
const registryIndex = await readJson(registryIndexPath);
|
||||
const projectRoot = path.resolve(options.project);
|
||||
const targetDir = toPosixPath(options.targetDir ?? registryIndex.install.defaultTargetDir);
|
||||
const targetRoot = path.join(projectRoot, ...targetDir.split("/"));
|
||||
const manifestPath = path.join(targetRoot, registryIndex.install.manifestFile);
|
||||
|
||||
let itemNames = requestedItems;
|
||||
|
||||
if (itemNames.length === 0) {
|
||||
const manifest = await readInstallManifest(manifestPath);
|
||||
|
||||
if (!manifest?.items?.length) {
|
||||
throw new Error(
|
||||
`No registry items were provided and ${toPosixPath(
|
||||
path.relative(projectRoot, manifestPath)
|
||||
)} does not exist yet.`
|
||||
);
|
||||
}
|
||||
|
||||
itemNames = manifest.items;
|
||||
}
|
||||
|
||||
const resolvedItems = resolveItems(registryIndex, itemNames);
|
||||
const packageDependencies = collectPackageDependencies(resolvedItems);
|
||||
const copyResult = await copyRegistryFiles({
|
||||
dryRun: options.dryRun,
|
||||
items: resolvedItems,
|
||||
projectRoot,
|
||||
targetDir
|
||||
});
|
||||
|
||||
let dependencyResult = {
|
||||
added: {},
|
||||
conflicts: []
|
||||
};
|
||||
|
||||
if (!options.skipPackageJson) {
|
||||
const packageJsonPath = path.join(projectRoot, "package.json");
|
||||
const packageJson = await readJson(packageJsonPath);
|
||||
dependencyResult = upsertDependencySections(packageJson, packageDependencies);
|
||||
|
||||
if (!options.dryRun) {
|
||||
await fs.writeFile(
|
||||
packageJsonPath,
|
||||
`${JSON.stringify(dependencyResult.packageJson, null, 2)}\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const installManifest = {
|
||||
items: resolvedItems.map((item) => item.name),
|
||||
packageDependencies,
|
||||
registry: registryIndex.library.name,
|
||||
sourcePackages: registryIndex.library.packages,
|
||||
targetDir
|
||||
};
|
||||
|
||||
if (!options.dryRun) {
|
||||
await fs.mkdir(targetRoot, { recursive: true });
|
||||
await fs.writeFile(manifestPath, `${JSON.stringify(installManifest, null, 2)}\n`);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${options.dryRun ? "Planned" : "Installed"} registry items: ${resolvedItems
|
||||
.map((item) => item.name)
|
||||
.join(", ")}`
|
||||
);
|
||||
console.log(
|
||||
`Files written: ${copyResult.writtenFiles.length}, updated: ${copyResult.updatedFiles.length}, unchanged: ${copyResult.unchangedFiles.length}`
|
||||
);
|
||||
|
||||
if (!options.skipPackageJson) {
|
||||
const added = Object.keys(dependencyResult.added);
|
||||
console.log(
|
||||
added.length > 0
|
||||
? `Added package dependencies: ${added.join(", ")}`
|
||||
: "Added package dependencies: none"
|
||||
);
|
||||
}
|
||||
|
||||
if (dependencyResult.conflicts.length > 0) {
|
||||
console.log("Dependency version conflicts:");
|
||||
|
||||
for (const conflict of dependencyResult.conflicts) {
|
||||
console.log(
|
||||
`- ${conflict.name}: keeping ${conflict.section}=${conflict.existingRange}, registry expects ${conflict.requiredRange}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Target directory: ${targetDir}`);
|
||||
console.log(`Tokens import path: ${targetDir}/tokens/styles.css`);
|
||||
console.log(
|
||||
options.dryRun
|
||||
? "Dry run complete."
|
||||
: "Review the diff, then run your package manager install if package.json changed."
|
||||
);
|
||||
}
|
||||
|
||||
await main();
|
||||
Reference in New Issue
Block a user