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.
- package/dist/commands/add.d.ts +34 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +284 -0
- package/dist/commands/diff.d.ts +6 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +100 -0
- package/dist/commands/init.d.ts +46 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +267 -0
- package/dist/commands/list.d.ts +8 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +73 -0
- package/dist/commands/status.d.ts +50 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +72 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +23 -0
- package/dist/commands/theme.d.ts +14 -0
- package/dist/commands/theme.d.ts.map +1 -0
- package/dist/commands/theme.js +94 -0
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +139 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/utils/registry-resolver.d.ts +4 -0
- package/dist/utils/registry-resolver.d.ts.map +1 -0
- package/dist/utils/registry-resolver.js +33 -0
- package/package.json +47 -0
|
@@ -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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
+
}
|