inai-react-components 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,139 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import chalk from "chalk";
4
+ import ora from "ora";
5
+ import prompts from "prompts";
6
+ import { readComponentsJson, readRegistryJson, } from "./status.js";
7
+ import { findComponentInRegistry, computeDiff } from "./diff.js";
8
+ import { resolveRegistryDir } from "../utils/registry-resolver.js";
9
+ export async function runUpdate(componentName, rootDir, skipPrompts = false) {
10
+ const componentsJson = readComponentsJson(rootDir);
11
+ if (!componentsJson) {
12
+ return {
13
+ updated: false,
14
+ message: "No components.json found. Run `inai-ui init` first to initialize your project.",
15
+ files: [],
16
+ };
17
+ }
18
+ const registryDir = resolveRegistryDir(rootDir);
19
+ const registryPath = path.join(registryDir, "registry.json");
20
+ const registryComponent = findComponentInRegistry(registryPath, componentName);
21
+ if (!registryComponent) {
22
+ return {
23
+ updated: false,
24
+ message: `Component "${componentName}" not found in registry.`,
25
+ files: [],
26
+ };
27
+ }
28
+ const registry = readRegistryJson(registryPath);
29
+ const registryVersion = registry?.version ?? "unknown";
30
+ const localComponentDir = componentsJson.aliases.components.replace(/^@\//, "src/");
31
+ const updatedFiles = [];
32
+ let needsUpdate = false;
33
+ for (const filePath of registryComponent.files) {
34
+ const registryFilePath = path.join(registryDir, filePath);
35
+ const fileName = path.basename(filePath);
36
+ const localFilePath = path.join(rootDir, localComponentDir, fileName);
37
+ if (!fs.existsSync(registryFilePath)) {
38
+ continue;
39
+ }
40
+ const registryContent = fs.readFileSync(registryFilePath, "utf-8");
41
+ if (!fs.existsSync(localFilePath)) {
42
+ // File does not exist locally -- install it
43
+ const dir = path.dirname(localFilePath);
44
+ fs.mkdirSync(dir, { recursive: true });
45
+ fs.writeFileSync(localFilePath, registryContent);
46
+ updatedFiles.push(fileName);
47
+ needsUpdate = true;
48
+ continue;
49
+ }
50
+ const localContent = fs.readFileSync(localFilePath, "utf-8");
51
+ if (localContent === registryContent) {
52
+ continue;
53
+ }
54
+ // There is a difference
55
+ needsUpdate = true;
56
+ if (!skipPrompts) {
57
+ const diff = computeDiff(localContent, registryContent);
58
+ console.log(chalk.bold(`\nChanges for ${fileName}:`));
59
+ console.log(chalk.dim("─".repeat(60)));
60
+ console.log(diff);
61
+ const response = await prompts({
62
+ type: "select",
63
+ name: "action",
64
+ message: `How would you like to handle ${fileName}?`,
65
+ choices: [
66
+ { title: "Overwrite with registry version", value: "overwrite" },
67
+ { title: "Keep local version", value: "keep" },
68
+ { title: "Skip this file", value: "skip" },
69
+ ],
70
+ initial: 0,
71
+ });
72
+ if (response.action === "overwrite") {
73
+ fs.writeFileSync(localFilePath, registryContent);
74
+ updatedFiles.push(fileName);
75
+ }
76
+ // "keep" and "skip" both leave the file as-is
77
+ }
78
+ else {
79
+ // Non-interactive mode: overwrite
80
+ fs.writeFileSync(localFilePath, registryContent);
81
+ updatedFiles.push(fileName);
82
+ }
83
+ }
84
+ if (!needsUpdate) {
85
+ return {
86
+ updated: false,
87
+ message: `Component "${componentName}" is already up to date.`,
88
+ files: [],
89
+ };
90
+ }
91
+ // Update installedComponents entry in components.json
92
+ const installed = componentsJson.installedComponents ?? [];
93
+ const existingIdx = installed.findIndex((c) => c.name === componentName);
94
+ const entry = {
95
+ name: componentName,
96
+ version: registryVersion,
97
+ installedAt: new Date().toISOString().split("T")[0],
98
+ };
99
+ if (existingIdx >= 0) {
100
+ installed[existingIdx] = entry;
101
+ }
102
+ else {
103
+ installed.push(entry);
104
+ }
105
+ componentsJson.installedComponents = installed;
106
+ const componentsJsonPath = path.join(rootDir, "components.json");
107
+ fs.writeFileSync(componentsJsonPath, JSON.stringify(componentsJson, null, 2) + "\n");
108
+ return {
109
+ updated: true,
110
+ message: `Component "${componentName}" updated to registry v${registryVersion}.`,
111
+ files: updatedFiles,
112
+ };
113
+ }
114
+ export async function updateCommand(componentName) {
115
+ const spinner = ora(`Checking "${componentName}" for updates...`).start();
116
+ try {
117
+ const rootDir = process.cwd();
118
+ spinner.stop();
119
+ const result = await runUpdate(componentName, rootDir);
120
+ if (result.updated) {
121
+ console.log(chalk.green(`\n${result.message}`));
122
+ if (result.files.length > 0) {
123
+ console.log(chalk.dim("\nUpdated files:"));
124
+ for (const file of result.files) {
125
+ console.log(chalk.dim(` - ${file}`));
126
+ }
127
+ }
128
+ }
129
+ else {
130
+ console.log(chalk.yellow(`\n${result.message}`));
131
+ }
132
+ }
133
+ catch (error) {
134
+ spinner.fail("Update failed.");
135
+ const message = error instanceof Error ? error.message : String(error);
136
+ console.error(chalk.red(`\nError: ${message}`));
137
+ process.exit(1);
138
+ }
139
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { addCommand } from "./commands/add.js";
4
+ import { initCommand } from "./commands/init.js";
5
+ import { statusCommand } from "./commands/status.js";
6
+ import { diffCommand } from "./commands/diff.js";
7
+ import { listCommand } from "./commands/list.js";
8
+ import { updateCommand } from "./commands/update.js";
9
+ import { syncCommand } from "./commands/sync.js";
10
+ import { themeCreateCommand } from "./commands/theme.js";
11
+ const program = new Command();
12
+ program
13
+ .name("inai-ui")
14
+ .description("CLI for InAI UI component library")
15
+ .version("0.1.0");
16
+ program
17
+ .command("init [repo-url]")
18
+ .description("Initialize InAI UI in your project. Optionally pass a Git repo URL to fetch components remotely.")
19
+ .action((repoUrl) => initCommand(repoUrl));
20
+ program
21
+ .command("add [component]")
22
+ .description("Add a component to your project. Without arguments, opens an interactive picker to select multiple components.")
23
+ .action((component) => addCommand(component));
24
+ program
25
+ .command("sync")
26
+ .description("Update the local registry cache from the remote repository")
27
+ .action(syncCommand);
28
+ program
29
+ .command("status")
30
+ .description("Show installed components and their versions")
31
+ .action(statusCommand);
32
+ program
33
+ .command("diff <component>")
34
+ .description("Show differences between local component and registry version")
35
+ .action((component) => diffCommand(component));
36
+ program
37
+ .command("list")
38
+ .description("List all available components from the registry")
39
+ .option("-c, --category <category>", "Filter by component category")
40
+ .action((options) => listCommand(options));
41
+ program
42
+ .command("update <component>")
43
+ .description("Update a component to the latest registry version")
44
+ .action((component) => updateCommand(component));
45
+ const themeCmd = program
46
+ .command("theme")
47
+ .description("Theme management commands");
48
+ themeCmd
49
+ .command("create")
50
+ .description("Create a custom theme based on an existing theme")
51
+ .requiredOption("-n, --name <name>", "Name for the new theme")
52
+ .requiredOption("-b, --base <base>", "Base theme to extend (monday, linear, notion, vercel)")
53
+ .action((options) => themeCreateCommand(options));
54
+ program.parse();
@@ -0,0 +1,4 @@
1
+ export declare function getCacheDir(): string;
2
+ export declare function cloneRegistry(repoUrl: string): string;
3
+ export declare function resolveRegistryDir(projectDir: string): string;
4
+ //# sourceMappingURL=registry-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/registry-resolver.ts"],"names":[],"mappings":"AASA,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWrD;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAS7D"}
@@ -0,0 +1,33 @@
1
+ import { execSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import { readComponentsJson } from "../commands/status.js";
6
+ const CACHE_BASE = path.join(os.homedir(), ".inai-ui");
7
+ const CACHE_DIR = path.join(CACHE_BASE, "registry");
8
+ export function getCacheDir() {
9
+ return CACHE_DIR;
10
+ }
11
+ export function cloneRegistry(repoUrl) {
12
+ if (fs.existsSync(path.join(CACHE_DIR, ".git"))) {
13
+ execSync("git pull --ff-only", { cwd: CACHE_DIR, stdio: "pipe" });
14
+ }
15
+ else {
16
+ fs.mkdirSync(CACHE_BASE, { recursive: true });
17
+ if (fs.existsSync(CACHE_DIR)) {
18
+ fs.rmSync(CACHE_DIR, { recursive: true });
19
+ }
20
+ execSync(`git clone "${repoUrl}" "${CACHE_DIR}"`, { stdio: "pipe" });
21
+ }
22
+ return CACHE_DIR;
23
+ }
24
+ export function resolveRegistryDir(projectDir) {
25
+ const componentsJson = readComponentsJson(projectDir);
26
+ if (componentsJson?.registrySource) {
27
+ if (!fs.existsSync(path.join(CACHE_DIR, ".git"))) {
28
+ cloneRegistry(componentsJson.registrySource);
29
+ }
30
+ return CACHE_DIR;
31
+ }
32
+ return projectDir;
33
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "inai-react-components",
3
+ "version": "0.1.0",
4
+ "description": "CLI for InAI UI component library — install components from a private registry into your React project",
5
+ "private": false,
6
+ "type": "module",
7
+ "bin": {
8
+ "inai-ui": "./dist/index.js"
9
+ },
10
+ "keywords": [
11
+ "react",
12
+ "components",
13
+ "ui",
14
+ "cli",
15
+ "tailwindcss",
16
+ "react-aria",
17
+ "design-system"
18
+ ],
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/InAI-Team/inai-components-react.git",
23
+ "directory": "packages/cli"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "dev": "tsc --watch",
31
+ "check-types": "tsc --noEmit",
32
+ "test": "vitest run"
33
+ },
34
+ "dependencies": {
35
+ "chalk": "^5.6.2",
36
+ "commander": "^14.0.3",
37
+ "fast-glob": "^3.3.3",
38
+ "ora": "^9.3.0",
39
+ "prompts": "^2.4.2"
40
+ },
41
+ "devDependencies": {
42
+ "@repo/typescript-config": "workspace:*",
43
+ "@types/node": "^22.15.3",
44
+ "typescript": "5.9.2",
45
+ "vitest": "^4.1.0"
46
+ }
47
+ }