Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.

Commit eb6272f

Browse files
committed
feat: implement diff command
1 parent 2088a37 commit eb6272f

1 file changed

Lines changed: 89 additions & 3 deletions

File tree

src/commands/diff.command.ts

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,95 @@
11
import { Command } from "@effect/cli"
22
import { Console, Effect } from "effect"
3-
import { componentNames, componentType } from "./add.command"
3+
import {
4+
HttpClient,
5+
HttpClientRequest,
6+
HttpClientResponse,
7+
} from "@effect/platform"
8+
import * as FS from "node:fs/promises"
9+
import * as Path from "node:path"
410

5-
export const diffCommand = Command.make("diff", { componentNames, componentType }, (config) =>
11+
const REMOTE_BASE =
12+
"https://raw.githubusercontent.com/irsyadadl/intentui/2.x/components/ui"
13+
14+
function simpleDiff(local: string, remote: string): string {
15+
const l = local.split(/\r?\n/)
16+
const r = remote.split(/\r?\n/)
17+
const max = Math.max(l.length, r.length)
18+
let out = ""
19+
for (let i = 0; i < max; i++) {
20+
const a = l[i]
21+
const b = r[i]
22+
if (a !== b) {
23+
if (a !== undefined) out += `- ${a}\n`
24+
if (b !== undefined) out += `+ ${b}\n`
25+
}
26+
}
27+
return out
28+
}
29+
30+
async function listFiles(dir: string): Promise<string[]> {
31+
const entries = await FS.readdir(dir, { withFileTypes: true })
32+
const files: string[] = []
33+
for (const entry of entries) {
34+
const res = Path.join(dir, entry.name)
35+
if (entry.isDirectory()) {
36+
files.push(...(await listFiles(res)))
37+
} else {
38+
files.push(res)
39+
}
40+
}
41+
return files
42+
}
43+
44+
export const diffCommand = Command.make("diff", {}, () =>
645
Effect.gen(function* () {
7-
yield* Console.log("Coming soon...")
46+
const client = yield* HttpClient.HttpClient
47+
const cwd = process.cwd()
48+
let config
49+
try {
50+
const jsonStr = yield* Effect.tryPromise(() => FS.readFile("components.json", "utf8"))
51+
config = JSON.parse(jsonStr)
52+
} catch {
53+
yield* Console.error("components.json not found or invalid")
54+
return
55+
}
56+
57+
const alias: string = config.aliases?.ui ?? "components/ui"
58+
const uiPath = Path.isAbsolute(alias.replace(/^@\//, ""))
59+
? alias.replace(/^@\//, "")
60+
: Path.join(cwd, alias.replace(/^@\//, ""))
61+
62+
let files: string[] = []
63+
try {
64+
files = await listFiles(uiPath)
65+
} catch {
66+
yield* Console.error(`Unable to read local components at ${uiPath}`)
67+
return
68+
}
69+
70+
const diffs: Array<{ file: string; diff: string }> = []
71+
for (const file of files) {
72+
const relative = Path.relative(uiPath, file).replace(/\\/g, "/")
73+
const remoteUrl = `${REMOTE_BASE}/${relative}`
74+
const localContent = yield* Effect.tryPromise(() => FS.readFile(file, "utf8"))
75+
const remoteContent = yield* HttpClientRequest.get(remoteUrl).pipe(
76+
client.execute,
77+
Effect.flatMap(HttpClientResponse.text),
78+
Effect.catchAll(() => Effect.succeed("")),
79+
)
80+
if (localContent !== remoteContent) {
81+
diffs.push({ file: relative, diff: simpleDiff(localContent, remoteContent) })
82+
}
83+
}
84+
85+
if (diffs.length === 0) {
86+
yield* Console.log("All components are up to date.")
87+
} else {
88+
for (const d of diffs) {
89+
yield* Console.log(`--- ${d.file} ---`)
90+
yield* Console.log(d.diff)
91+
}
92+
yield* Console.log("Some components differ from the registry. Run 'intentui add <component>' to sync.")
93+
}
894
}),
995
).pipe(Command.withDescription("Compares your local components with the registry versions."))

0 commit comments

Comments
 (0)