inai-react-components 0.1.7 → 1.4.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/README.md +80 -0
- package/dist/commands/add.d.ts +42 -12
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +413 -93
- package/dist/commands/diff.d.ts +6 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +90 -20
- package/dist/commands/init.d.ts +17 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +87 -64
- package/dist/commands/mcp.d.ts +123 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +289 -0
- package/dist/commands/migrate.d.ts +41 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +139 -0
- package/dist/commands/registries.d.ts +46 -0
- package/dist/commands/registries.d.ts.map +1 -0
- package/dist/commands/registries.js +152 -0
- package/dist/commands/remove.d.ts +11 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +171 -0
- package/dist/commands/schema.d.ts +16 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +32 -0
- package/dist/commands/status.d.ts +40 -4
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +149 -25
- package/dist/commands/theme.d.ts.map +1 -1
- package/dist/commands/theme.js +3 -37
- package/dist/commands/update.d.ts +18 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +197 -6
- package/dist/index.js +76 -5
- package/dist/schemas/registry-config.schema.json +62 -0
- package/dist/schemas/registry-item.schema.json +63 -0
- package/dist/schemas/registry.schema.json +15 -0
- package/dist/types/registry.d.ts +50 -0
- package/dist/types/registry.d.ts.map +1 -0
- package/dist/types/registry.js +10 -0
- package/dist/utils/auto-install.d.ts +11 -0
- package/dist/utils/auto-install.d.ts.map +1 -0
- package/dist/utils/auto-install.js +29 -0
- package/dist/utils/framework-detect.d.ts +20 -0
- package/dist/utils/framework-detect.d.ts.map +1 -0
- package/dist/utils/framework-detect.js +97 -0
- package/dist/utils/fuzzy-search.d.ts +16 -0
- package/dist/utils/fuzzy-search.d.ts.map +1 -0
- package/dist/utils/fuzzy-search.js +67 -0
- package/dist/utils/paths.d.ts +26 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +35 -0
- package/dist/utils/registry-config.d.ts +27 -0
- package/dist/utils/registry-config.d.ts.map +1 -0
- package/dist/utils/registry-config.js +94 -0
- package/dist/utils/registry-resolver.d.ts +26 -0
- package/dist/utils/registry-resolver.d.ts.map +1 -1
- package/dist/utils/registry-resolver.js +134 -10
- package/dist/utils/snapshot.d.ts +20 -0
- package/dist/utils/snapshot.d.ts.map +1 -0
- package/dist/utils/snapshot.js +32 -0
- package/dist/utils/themes.d.ts +17 -0
- package/dist/utils/themes.d.ts.map +1 -0
- package/dist/utils/themes.js +49 -0
- package/dist/utils/tsconfig-detect.d.ts +18 -0
- package/dist/utils/tsconfig-detect.d.ts.map +1 -0
- package/dist/utils/tsconfig-detect.js +121 -0
- package/package.json +9 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../src/commands/remove.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAgCD,wBAAsB,SAAS,CAC7B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAgIvB;AAED,wBAAsB,aAAa,CACjC,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
|
@@ -0,0 +1,171 @@
|
|
|
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 { resolveRegistryDir } from "../utils/registry-resolver.js";
|
|
8
|
+
import { deleteSnapshot } from "../utils/snapshot.js";
|
|
9
|
+
import { resolveLocalDir } from "../utils/paths.js";
|
|
10
|
+
/**
|
|
11
|
+
* Find components that depend on the given component name via
|
|
12
|
+
* `registryDependencies`.
|
|
13
|
+
*/
|
|
14
|
+
function findDependents(componentName, installedNames, registryPath) {
|
|
15
|
+
const registry = readRegistryJson(registryPath);
|
|
16
|
+
if (!registry)
|
|
17
|
+
return [];
|
|
18
|
+
const allItems = [
|
|
19
|
+
...registry.components,
|
|
20
|
+
...registry.blocks,
|
|
21
|
+
...registry.templates,
|
|
22
|
+
];
|
|
23
|
+
const dependents = [];
|
|
24
|
+
for (const item of allItems) {
|
|
25
|
+
if (!installedNames.includes(item.name))
|
|
26
|
+
continue;
|
|
27
|
+
if (item.name === componentName)
|
|
28
|
+
continue;
|
|
29
|
+
const deps = item.registryDependencies ?? [];
|
|
30
|
+
if (deps.includes(componentName)) {
|
|
31
|
+
dependents.push(item.name);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return dependents;
|
|
35
|
+
}
|
|
36
|
+
export async function runRemove(componentName, rootDir, options = {}) {
|
|
37
|
+
const componentsJson = readComponentsJson(rootDir);
|
|
38
|
+
if (!componentsJson) {
|
|
39
|
+
return {
|
|
40
|
+
removed: false,
|
|
41
|
+
message: "No components.json found. Run `inai-ui init` first to initialize your project.",
|
|
42
|
+
deletedFiles: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const installed = componentsJson.installedComponents ?? [];
|
|
46
|
+
const entry = installed.find((c) => c.name === componentName);
|
|
47
|
+
if (!entry) {
|
|
48
|
+
return {
|
|
49
|
+
removed: false,
|
|
50
|
+
message: `Component "${componentName}" is not installed.`,
|
|
51
|
+
deletedFiles: [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Check for dependents
|
|
55
|
+
const registryDir = resolveRegistryDir(rootDir);
|
|
56
|
+
const registryPath = path.join(registryDir, "registry.json");
|
|
57
|
+
const installedNames = installed.map((c) => c.name);
|
|
58
|
+
const dependents = findDependents(componentName, installedNames, registryPath);
|
|
59
|
+
if (dependents.length > 0 && !options.force) {
|
|
60
|
+
const depList = dependents.join(", ");
|
|
61
|
+
console.log(chalk.yellow(`\nWarning: The following installed components depend on "${componentName}": ${depList}`));
|
|
62
|
+
if (process.stdin.isTTY) {
|
|
63
|
+
const res = await prompts({
|
|
64
|
+
type: "confirm",
|
|
65
|
+
name: "proceed",
|
|
66
|
+
message: `Remove "${componentName}" anyway?`,
|
|
67
|
+
initial: false,
|
|
68
|
+
});
|
|
69
|
+
if (!res.proceed) {
|
|
70
|
+
return {
|
|
71
|
+
removed: false,
|
|
72
|
+
message: "Removal cancelled.",
|
|
73
|
+
deletedFiles: [],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return {
|
|
79
|
+
removed: false,
|
|
80
|
+
message: `Component "${componentName}" has dependents (${depList}). Use --force to remove anyway.`,
|
|
81
|
+
deletedFiles: [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Determine the files to delete
|
|
86
|
+
const registry = readRegistryJson(registryPath);
|
|
87
|
+
const allItems = registry
|
|
88
|
+
? [...registry.components, ...registry.blocks, ...registry.templates]
|
|
89
|
+
: [];
|
|
90
|
+
const regEntry = allItems.find((c) => c.name === componentName);
|
|
91
|
+
const deletedFiles = [];
|
|
92
|
+
if (regEntry) {
|
|
93
|
+
const type = regEntry.type.toLowerCase();
|
|
94
|
+
const isMultiFile = type === "block" || type === "template";
|
|
95
|
+
const aliases = componentsJson.aliases;
|
|
96
|
+
let baseTargetDir;
|
|
97
|
+
if (type === "block") {
|
|
98
|
+
baseTargetDir = resolveLocalDir(aliases.blocks, componentsJson);
|
|
99
|
+
}
|
|
100
|
+
else if (type === "template") {
|
|
101
|
+
if (aliases.templates) {
|
|
102
|
+
baseTargetDir = resolveLocalDir(aliases.templates, componentsJson);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const blocksDir = resolveLocalDir(aliases.blocks, componentsJson);
|
|
106
|
+
baseTargetDir = path.join(path.dirname(blocksDir), "templates");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
baseTargetDir = resolveLocalDir(aliases.components, componentsJson);
|
|
111
|
+
}
|
|
112
|
+
if (isMultiFile) {
|
|
113
|
+
// Delete the entire component directory
|
|
114
|
+
const componentDir = path.join(rootDir, baseTargetDir, componentName);
|
|
115
|
+
if (fs.existsSync(componentDir)) {
|
|
116
|
+
fs.rmSync(componentDir, { recursive: true, force: true });
|
|
117
|
+
deletedFiles.push(path.relative(rootDir, componentDir) + "/");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Delete individual files
|
|
122
|
+
for (const filePath of regEntry.files) {
|
|
123
|
+
const fileName = path.basename(filePath);
|
|
124
|
+
const localPath = path.join(rootDir, baseTargetDir, fileName);
|
|
125
|
+
if (fs.existsSync(localPath)) {
|
|
126
|
+
fs.unlinkSync(localPath);
|
|
127
|
+
deletedFiles.push(path.relative(rootDir, localPath));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Remove snapshot
|
|
133
|
+
deleteSnapshot(rootDir, componentName);
|
|
134
|
+
// Update components.json
|
|
135
|
+
componentsJson.installedComponents = installed.filter((c) => c.name !== componentName);
|
|
136
|
+
const componentsJsonPath = path.join(rootDir, "components.json");
|
|
137
|
+
fs.writeFileSync(componentsJsonPath, JSON.stringify(componentsJson, null, 2) + "\n");
|
|
138
|
+
return {
|
|
139
|
+
removed: true,
|
|
140
|
+
message: `Component "${componentName}" removed successfully.`,
|
|
141
|
+
deletedFiles,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export async function removeCommand(componentName, options) {
|
|
145
|
+
const spinner = ora(`Removing "${componentName}"...`).start();
|
|
146
|
+
try {
|
|
147
|
+
const rootDir = process.cwd();
|
|
148
|
+
spinner.stop();
|
|
149
|
+
const result = await runRemove(componentName, rootDir, {
|
|
150
|
+
force: options?.force,
|
|
151
|
+
});
|
|
152
|
+
if (!result.removed) {
|
|
153
|
+
console.log(chalk.yellow(result.message));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
console.log(chalk.green(result.message));
|
|
157
|
+
if (result.deletedFiles.length > 0) {
|
|
158
|
+
console.log(chalk.green("\nDeleted files:"));
|
|
159
|
+
for (const file of result.deletedFiles) {
|
|
160
|
+
console.log(chalk.dim(` - ${file}`));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log(chalk.dim("\nUpdated components.json"));
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
spinner.fail("Removal failed.");
|
|
167
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
168
|
+
console.error(chalk.red(`\nError: ${message}`));
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type SchemaKind = "item" | "registry" | "registry-config";
|
|
2
|
+
export interface SchemaOptions {
|
|
3
|
+
kind?: SchemaKind;
|
|
4
|
+
output?: "stdout" | string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Print or write the JSON Schema for the InAI registry.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* inai-ui schema # prints registry-item.schema.json
|
|
11
|
+
* inai-ui schema --kind registry # prints registry.schema.json
|
|
12
|
+
* inai-ui schema --kind registry-config # prints registry-config.schema.json (M3 federated registries)
|
|
13
|
+
* inai-ui schema --output ./my-schema.json
|
|
14
|
+
*/
|
|
15
|
+
export declare function runSchema(options?: SchemaOptions): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/commands/schema.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,iBAAiB,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB1E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
/**
|
|
8
|
+
* Print or write the JSON Schema for the InAI registry.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* inai-ui schema # prints registry-item.schema.json
|
|
12
|
+
* inai-ui schema --kind registry # prints registry.schema.json
|
|
13
|
+
* inai-ui schema --kind registry-config # prints registry-config.schema.json (M3 federated registries)
|
|
14
|
+
* inai-ui schema --output ./my-schema.json
|
|
15
|
+
*/
|
|
16
|
+
export async function runSchema(options = {}) {
|
|
17
|
+
const { kind = "item", output = "stdout" } = options;
|
|
18
|
+
const filename = kind === "registry"
|
|
19
|
+
? "registry.schema.json"
|
|
20
|
+
: kind === "registry-config"
|
|
21
|
+
? "registry-config.schema.json"
|
|
22
|
+
: "registry-item.schema.json";
|
|
23
|
+
// dist/commands/schema.js → ../schemas/<file>
|
|
24
|
+
const schemaPath = join(__dirname, "..", "schemas", filename);
|
|
25
|
+
const content = await readFile(schemaPath, "utf8");
|
|
26
|
+
if (output === "stdout") {
|
|
27
|
+
console.log(content);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
await writeFile(output, content, "utf8");
|
|
31
|
+
console.log(chalk.green(`✓ Schema written to ${output}`));
|
|
32
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import type { Framework } from "../utils/framework-detect.js";
|
|
1
2
|
export interface ComponentsJsonFile {
|
|
2
3
|
$schema: string;
|
|
3
4
|
style: string;
|
|
5
|
+
framework?: Framework;
|
|
6
|
+
registrySource?: string;
|
|
7
|
+
layout?: {
|
|
8
|
+
sourceRoot: string;
|
|
9
|
+
aliasPrefix: string;
|
|
10
|
+
};
|
|
4
11
|
tailwind: {
|
|
5
12
|
config: string;
|
|
6
13
|
css: string;
|
|
@@ -8,9 +15,9 @@ export interface ComponentsJsonFile {
|
|
|
8
15
|
aliases: {
|
|
9
16
|
components: string;
|
|
10
17
|
blocks: string;
|
|
18
|
+
templates?: string;
|
|
11
19
|
utils: string;
|
|
12
20
|
};
|
|
13
|
-
registrySource?: string;
|
|
14
21
|
theme: string;
|
|
15
22
|
font?: {
|
|
16
23
|
body: string;
|
|
@@ -46,10 +53,39 @@ export interface RegistryComponent {
|
|
|
46
53
|
tokenUsage: string[];
|
|
47
54
|
tanstackCompatibility: Record<string, boolean>;
|
|
48
55
|
fieldWrappers?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Names of other registry components this component depends on. Used by
|
|
58
|
+
* `inai-ui add` to install transitive deps automatically. Defaults to [].
|
|
59
|
+
*/
|
|
60
|
+
registryDependencies?: string[];
|
|
49
61
|
}
|
|
50
62
|
export declare function readComponentsJson(rootDir: string): ComponentsJsonFile | null;
|
|
51
63
|
export declare function readRegistryJson(registryPath: string): RegistryJson | null;
|
|
52
|
-
export
|
|
53
|
-
export
|
|
54
|
-
|
|
64
|
+
export type ComponentHealth = "HEALTHY" | "DRIFT" | "OUTDATED" | "MISSING";
|
|
65
|
+
export interface ComponentStatusEntry {
|
|
66
|
+
name: string;
|
|
67
|
+
installedVersion: string;
|
|
68
|
+
registryVersion: string;
|
|
69
|
+
status: ComponentHealth;
|
|
70
|
+
/** Files that drifted vs the snapshot (populated only for DRIFT). */
|
|
71
|
+
driftedFiles?: string[];
|
|
72
|
+
/** Files missing from disk (populated for MISSING). */
|
|
73
|
+
missingFiles?: string[];
|
|
74
|
+
}
|
|
75
|
+
export interface StatusReport {
|
|
76
|
+
registryVersion: string;
|
|
77
|
+
components: ComponentStatusEntry[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Inspect every installed component, comparing disk state against the
|
|
81
|
+
* saved snapshot and the registry version. This is the machine-readable
|
|
82
|
+
* side of the `status` command — `formatStatusTable` renders it.
|
|
83
|
+
*/
|
|
84
|
+
export declare function inspectStatus(rootDir: string, componentsJson: ComponentsJsonFile, registry: RegistryJson | null): StatusReport;
|
|
85
|
+
export declare function formatStatusTable(report: StatusReport): string;
|
|
86
|
+
export interface RunStatusOptions {
|
|
87
|
+
json?: boolean;
|
|
88
|
+
}
|
|
89
|
+
export declare function runStatus(rootDir: string, options?: RunStatusOptions): Promise<string>;
|
|
90
|
+
export declare function statusCommand(options?: RunStatusOptions): Promise<void>;
|
|
55
91
|
//# sourceMappingURL=status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/status.js
CHANGED
|
@@ -2,64 +2,188 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import ora from "ora";
|
|
5
|
+
import { loadSnapshot, sha256 } from "../utils/snapshot.js";
|
|
6
|
+
import { resolveLocalDir } from "../utils/paths.js";
|
|
5
7
|
export function readComponentsJson(rootDir) {
|
|
6
8
|
const filePath = path.join(rootDir, "components.json");
|
|
7
9
|
if (!fs.existsSync(filePath)) {
|
|
8
10
|
return null;
|
|
9
11
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
try {
|
|
13
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
12
19
|
}
|
|
13
20
|
export function readRegistryJson(registryPath) {
|
|
14
21
|
if (!fs.existsSync(registryPath)) {
|
|
15
22
|
return null;
|
|
16
23
|
}
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
try {
|
|
25
|
+
const raw = fs.readFileSync(registryPath, "utf-8");
|
|
26
|
+
return JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function padRight(str, len) {
|
|
33
|
+
if (str.length >= len)
|
|
34
|
+
return str;
|
|
35
|
+
return str + " ".repeat(len - str.length);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Inspect every installed component, comparing disk state against the
|
|
39
|
+
* saved snapshot and the registry version. This is the machine-readable
|
|
40
|
+
* side of the `status` command — `formatStatusTable` renders it.
|
|
41
|
+
*/
|
|
42
|
+
export function inspectStatus(rootDir, componentsJson, registry) {
|
|
43
|
+
const installed = componentsJson.installedComponents ?? [];
|
|
44
|
+
const registryVersion = registry?.version ?? "unknown";
|
|
45
|
+
const localComponentDir = resolveLocalDir(componentsJson.aliases.components, componentsJson);
|
|
46
|
+
const localBlocksDir = resolveLocalDir(componentsJson.aliases.blocks, componentsJson);
|
|
47
|
+
const allRegistryItems = registry
|
|
48
|
+
? [...registry.components, ...registry.blocks, ...registry.templates]
|
|
49
|
+
: [];
|
|
50
|
+
const registryByName = new Map(allRegistryItems.map((c) => [c.name, c]));
|
|
51
|
+
const components = installed.map((inst) => {
|
|
52
|
+
const snapshot = loadSnapshot(rootDir, inst.name);
|
|
53
|
+
const regItem = registryByName.get(inst.name);
|
|
54
|
+
const isMultiFile = regItem?.type === "block" || regItem?.type === "template";
|
|
55
|
+
// Compute expected local file paths from the snapshot (preferred) or
|
|
56
|
+
// fall back to registry files if the snapshot is missing.
|
|
57
|
+
const expectedFiles = [];
|
|
58
|
+
if (snapshot) {
|
|
59
|
+
for (const f of Object.values(snapshot.files)) {
|
|
60
|
+
expectedFiles.push(f.localPath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (regItem) {
|
|
64
|
+
const targetDirRaw = regItem.type === "block"
|
|
65
|
+
? path.join(localBlocksDir, regItem.name)
|
|
66
|
+
: regItem.type === "template"
|
|
67
|
+
? path.join(path.dirname(localBlocksDir), "templates", regItem.name)
|
|
68
|
+
: localComponentDir;
|
|
69
|
+
for (const rf of regItem.files) {
|
|
70
|
+
expectedFiles.push(path.join(targetDirRaw, path.basename(rf)));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const missingFiles = [];
|
|
74
|
+
const driftedFiles = [];
|
|
75
|
+
for (const rel of expectedFiles) {
|
|
76
|
+
const abs = path.join(rootDir, rel);
|
|
77
|
+
if (!fs.existsSync(abs)) {
|
|
78
|
+
missingFiles.push(rel);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (snapshot) {
|
|
82
|
+
const snap = snapshot.files[rel];
|
|
83
|
+
if (snap) {
|
|
84
|
+
const current = fs.readFileSync(abs, "utf-8");
|
|
85
|
+
if (sha256(current) !== snap.hash) {
|
|
86
|
+
driftedFiles.push(rel);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
let status;
|
|
92
|
+
if (missingFiles.length > 0 && missingFiles.length === expectedFiles.length) {
|
|
93
|
+
status = "MISSING";
|
|
94
|
+
}
|
|
95
|
+
else if (driftedFiles.length > 0 || missingFiles.length > 0) {
|
|
96
|
+
status = "DRIFT";
|
|
97
|
+
}
|
|
98
|
+
else if (registryVersion !== "unknown" &&
|
|
99
|
+
inst.version !== registryVersion) {
|
|
100
|
+
status = "OUTDATED";
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
status = "HEALTHY";
|
|
104
|
+
}
|
|
105
|
+
// Silence unused param warning when block dir isn't used.
|
|
106
|
+
void isMultiFile;
|
|
107
|
+
return {
|
|
108
|
+
name: inst.name,
|
|
109
|
+
installedVersion: inst.version,
|
|
110
|
+
registryVersion,
|
|
111
|
+
status,
|
|
112
|
+
driftedFiles: driftedFiles.length > 0 ? driftedFiles : undefined,
|
|
113
|
+
missingFiles: missingFiles.length > 0 ? missingFiles : undefined,
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
return { registryVersion, components };
|
|
19
117
|
}
|
|
20
|
-
export function formatStatusTable(
|
|
21
|
-
if (
|
|
118
|
+
export function formatStatusTable(report) {
|
|
119
|
+
if (report.components.length === 0) {
|
|
22
120
|
return chalk.yellow("No components installed yet. Run `inai-ui add` to get started.");
|
|
23
121
|
}
|
|
24
|
-
const header = `${chalk.bold("Installed Components")} (registry v${registryVersion})\n`;
|
|
122
|
+
const header = `${chalk.bold("Installed Components")} (registry v${report.registryVersion})\n`;
|
|
25
123
|
const separator = chalk.dim("─".repeat(60)) + "\n";
|
|
26
|
-
const columnHeader = chalk.bold(padRight("
|
|
27
|
-
chalk.bold(padRight("
|
|
28
|
-
chalk.bold("
|
|
124
|
+
const columnHeader = chalk.bold(padRight("NAME", 22)) +
|
|
125
|
+
chalk.bold(padRight("STATUS", 12)) +
|
|
126
|
+
chalk.bold("VERSION") +
|
|
29
127
|
"\n";
|
|
128
|
+
const colorize = (status, text) => {
|
|
129
|
+
switch (status) {
|
|
130
|
+
case "HEALTHY":
|
|
131
|
+
return chalk.green(text);
|
|
132
|
+
case "DRIFT":
|
|
133
|
+
return chalk.yellow(text);
|
|
134
|
+
case "OUTDATED":
|
|
135
|
+
return chalk.blue(text);
|
|
136
|
+
case "MISSING":
|
|
137
|
+
return chalk.red(text);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
30
140
|
let rows = "";
|
|
31
|
-
for (const comp of
|
|
141
|
+
for (const comp of report.components) {
|
|
142
|
+
let versionCell;
|
|
143
|
+
switch (comp.status) {
|
|
144
|
+
case "OUTDATED":
|
|
145
|
+
versionCell = `${comp.installedVersion} → ${comp.registryVersion}`;
|
|
146
|
+
break;
|
|
147
|
+
case "DRIFT":
|
|
148
|
+
versionCell = `${comp.installedVersion} (local modified)`;
|
|
149
|
+
break;
|
|
150
|
+
case "MISSING":
|
|
151
|
+
versionCell = `(file deleted)`;
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
versionCell = comp.installedVersion;
|
|
155
|
+
}
|
|
32
156
|
rows +=
|
|
33
|
-
chalk.cyan(padRight(comp.name,
|
|
34
|
-
padRight(comp.
|
|
35
|
-
chalk.dim(
|
|
157
|
+
chalk.cyan(padRight(comp.name, 22)) +
|
|
158
|
+
colorize(comp.status, padRight(comp.status, 12)) +
|
|
159
|
+
chalk.dim(versionCell) +
|
|
36
160
|
"\n";
|
|
37
161
|
}
|
|
38
162
|
return header + separator + columnHeader + separator + rows;
|
|
39
163
|
}
|
|
40
|
-
function
|
|
41
|
-
if (str.length >= len)
|
|
42
|
-
return str;
|
|
43
|
-
return str + " ".repeat(len - str.length);
|
|
44
|
-
}
|
|
45
|
-
export async function runStatus(rootDir) {
|
|
164
|
+
export async function runStatus(rootDir, options = {}) {
|
|
46
165
|
const componentsJson = readComponentsJson(rootDir);
|
|
47
166
|
if (!componentsJson) {
|
|
167
|
+
if (options.json) {
|
|
168
|
+
return JSON.stringify({ error: "No components.json found. Run `inai-ui init` first." }, null, 2);
|
|
169
|
+
}
|
|
48
170
|
return chalk.red("No components.json found. Run `inai-ui init` first to initialize your project.");
|
|
49
171
|
}
|
|
50
172
|
const { resolveRegistryDir } = await import("../utils/registry-resolver.js");
|
|
51
173
|
const registryDir = resolveRegistryDir(rootDir);
|
|
52
174
|
const registryPath = path.join(registryDir, "registry.json");
|
|
53
175
|
const registry = readRegistryJson(registryPath);
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
176
|
+
const report = inspectStatus(rootDir, componentsJson, registry);
|
|
177
|
+
if (options.json) {
|
|
178
|
+
return JSON.stringify(report, null, 2);
|
|
179
|
+
}
|
|
180
|
+
return formatStatusTable(report);
|
|
57
181
|
}
|
|
58
|
-
export async function statusCommand() {
|
|
182
|
+
export async function statusCommand(options = {}) {
|
|
59
183
|
const spinner = ora("Reading project configuration...").start();
|
|
60
184
|
try {
|
|
61
185
|
const rootDir = process.cwd();
|
|
62
|
-
const output = await runStatus(rootDir);
|
|
186
|
+
const output = await runStatus(rootDir, options);
|
|
63
187
|
spinner.stop();
|
|
64
188
|
console.log(output);
|
|
65
189
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/commands/theme.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/commands/theme.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgB3F;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDlE;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBnF"}
|
package/dist/commands/theme.js
CHANGED
|
@@ -2,42 +2,8 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import ora from "ora";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"monday",
|
|
8
|
-
"linear",
|
|
9
|
-
"notion",
|
|
10
|
-
"vercel",
|
|
11
|
-
"anthropic",
|
|
12
|
-
"shadcn-default",
|
|
13
|
-
"shadcn-blue",
|
|
14
|
-
"shadcn-green",
|
|
15
|
-
"shadcn-orange",
|
|
16
|
-
"shadcn-red",
|
|
17
|
-
"shadcn-rose",
|
|
18
|
-
"shadcn-violet",
|
|
19
|
-
"shadcn-yellow",
|
|
20
|
-
"shadcn-amber",
|
|
21
|
-
"shadcn-lime",
|
|
22
|
-
"shadcn-emerald",
|
|
23
|
-
"shadcn-teal",
|
|
24
|
-
"shadcn-cyan",
|
|
25
|
-
"shadcn-sky",
|
|
26
|
-
"shadcn-indigo",
|
|
27
|
-
"shadcn-purple",
|
|
28
|
-
"shadcn-fuchsia",
|
|
29
|
-
"shadcn-pink",
|
|
30
|
-
"cyberpunk",
|
|
31
|
-
"aurora",
|
|
32
|
-
"sunset",
|
|
33
|
-
"midnight",
|
|
34
|
-
"acid",
|
|
35
|
-
"sakura",
|
|
36
|
-
"ocean",
|
|
37
|
-
"cosmic",
|
|
38
|
-
"matcha",
|
|
39
|
-
"volcanic",
|
|
40
|
-
];
|
|
5
|
+
import { THEME_NAMES } from "../utils/themes.js";
|
|
6
|
+
const AVAILABLE_THEMES = THEME_NAMES;
|
|
41
7
|
export function resolveThemesDir(rootDir) {
|
|
42
8
|
return path.join(rootDir, "packages", "tokens", "src", "themes");
|
|
43
9
|
}
|
|
@@ -71,7 +37,7 @@ export async function runThemeCreate(options, rootDir) {
|
|
|
71
37
|
message: "Theme name is required.",
|
|
72
38
|
};
|
|
73
39
|
}
|
|
74
|
-
const validBases =
|
|
40
|
+
const validBases = AVAILABLE_THEMES;
|
|
75
41
|
if (!validBases.includes(base)) {
|
|
76
42
|
return {
|
|
77
43
|
success: false,
|
|
@@ -2,7 +2,24 @@ export interface UpdateResult {
|
|
|
2
2
|
updated: boolean;
|
|
3
3
|
message: string;
|
|
4
4
|
files: string[];
|
|
5
|
+
/** Files that merged cleanly (no conflict markers written). */
|
|
6
|
+
cleanMerges?: string[];
|
|
7
|
+
/** Files that were written with conflict markers the user must resolve. */
|
|
8
|
+
conflictedFiles?: string[];
|
|
5
9
|
}
|
|
10
|
+
export interface MergeOutcome {
|
|
11
|
+
merged: string;
|
|
12
|
+
hasConflicts: boolean;
|
|
13
|
+
conflictCount: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run a 3-way line merge between base (snapshot), mine (local) and theirs
|
|
17
|
+
* (registry). When a conflict is encountered we emit the standard git-style
|
|
18
|
+
* markers so the user can resolve them in their editor.
|
|
19
|
+
*/
|
|
20
|
+
export declare function threeWayMerge(mine: string, base: string, theirs: string): MergeOutcome;
|
|
6
21
|
export declare function runUpdate(componentName: string, rootDir: string, skipPrompts?: boolean): Promise<UpdateResult>;
|
|
7
|
-
export declare function updateCommand(componentName
|
|
22
|
+
export declare function updateCommand(componentName?: string, options?: {
|
|
23
|
+
all?: boolean;
|
|
24
|
+
}): Promise<void>;
|
|
8
25
|
//# sourceMappingURL=update.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,YAAY,CA4Cd;AAED,wBAAsB,SAAS,CAC7B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,WAAW,UAAQ,GAClB,OAAO,CAAC,YAAY,CAAC,CA+MvB;AAED,wBAAsB,aAAa,CACjC,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAiDf"}
|