lorre-blocks 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +337 -115
  3. package/package.json +14 -9
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # lorre-blocks
2
2
 
3
- CLI for [lorre-blocks](https://github.com/zukazine/lorre-blocks) — a shadcn-style
3
+ CLI for [lorre-blocks](https://github.com/odama-dev/lorre-blocks) — a shadcn-style
4
4
  component library. Instead of installing a package and importing from `node_modules`,
5
5
  this CLI **copies component source directly into your project**, so you own and can
6
6
  edit every line.
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
- import { promises as fs2 } from "fs";
8
- import path2 from "path";
7
+ import { promises as fs4 } from "fs";
8
+ import path4 from "path";
9
9
  import * as p from "@clack/prompts";
10
10
  import color from "picocolors";
11
11
 
@@ -13,7 +13,7 @@ import color from "picocolors";
13
13
  import { promises as fs } from "fs";
14
14
  import path from "path";
15
15
  var CONFIG_FILE = "components.json";
16
- var DEFAULT_REGISTRY = "http://localhost:3000";
16
+ var DEFAULT_REGISTRY = "https://lorre-blocks.vercel.app";
17
17
  var DEFAULT_ALIASES = {
18
18
  components: "@/components",
19
19
  ui: "@/components/ui",
@@ -48,104 +48,12 @@ async function writeConfig(cwd, config) {
48
48
  );
49
49
  }
50
50
 
51
- // src/commands/init.ts
52
- async function runInit(options) {
53
- const { cwd } = options;
54
- p.intro(color.bgCyan(color.black(" lorre-blocks init ")));
55
- if (await configExists(cwd)) {
56
- const overwrite = options.yes ? true : await p.confirm({
57
- message: "components.json already exists. Overwrite it?",
58
- initialValue: false
59
- });
60
- if (p.isCancel(overwrite) || !overwrite) {
61
- p.cancel("Init aborted; existing components.json kept.");
62
- return;
63
- }
64
- }
65
- const tsx = await fileExists(path2.join(cwd, "tsconfig.json"));
66
- const registry = options.registry ?? (options.yes ? DEFAULT_REGISTRY : await promptText(
67
- "Registry URL",
68
- DEFAULT_REGISTRY
69
- ));
70
- const config = {
71
- $schema: "https://lorre-blocks.dev/schema.json",
72
- registry,
73
- tsx,
74
- aliases: { ...DEFAULT_ALIASES }
75
- };
76
- await writeConfig(cwd, config);
77
- p.outro(
78
- `${color.green("\u2714")} Wrote components.json. Now run ${color.cyan(
79
- "lorre-blocks add button"
80
- )}.`
81
- );
82
- }
83
- async function promptText(message, initial) {
84
- const value = await p.text({ message, placeholder: initial, defaultValue: initial });
85
- if (p.isCancel(value)) {
86
- p.cancel("Init cancelled.");
87
- process.exit(0);
88
- }
89
- return value || initial;
90
- }
91
- async function fileExists(f) {
92
- try {
93
- await fs2.access(f);
94
- return true;
95
- } catch {
96
- return false;
97
- }
98
- }
99
-
100
- // src/commands/add.ts
101
- import { promises as fs5 } from "fs";
102
- import path5 from "path";
103
- import * as p2 from "@clack/prompts";
104
- import color2 from "picocolors";
105
-
106
- // src/utils/registry.ts
107
- async function fetchRegistryItem(registry, name) {
108
- const base = registry.replace(/\/$/, "");
109
- const url = `${base}/r/${name}.json`;
110
- let res;
111
- try {
112
- res = await fetch(url);
113
- } catch (err) {
114
- throw new Error(
115
- `Could not reach the registry at ${url}. Is it running / correct?
116
- ${err.message}`
117
- );
118
- }
119
- if (res.status === 404) {
120
- throw new Error(`Component "${name}" was not found in the registry (${url}).`);
121
- }
122
- if (!res.ok) {
123
- throw new Error(`Failed to fetch ${url} (HTTP ${res.status}).`);
124
- }
125
- return await res.json();
126
- }
127
- async function resolveTree(registry, names) {
128
- const resolved = /* @__PURE__ */ new Map();
129
- async function visit(name) {
130
- if (resolved.has(name)) return;
131
- const item = await fetchRegistryItem(registry, name);
132
- for (const dep of item.registryDependencies ?? []) {
133
- await visit(dep);
134
- }
135
- if (!resolved.has(name)) resolved.set(name, item);
136
- }
137
- for (const name of names) {
138
- await visit(name);
139
- }
140
- return [...resolved.values()];
141
- }
142
-
143
51
  // src/utils/package-manager.ts
144
- import { promises as fs3 } from "fs";
145
- import path3 from "path";
52
+ import { promises as fs2 } from "fs";
53
+ import path2 from "path";
146
54
  import { spawn } from "child_process";
147
55
  async function detectPackageManager(cwd) {
148
- const has = async (f) => fs3.access(path3.join(cwd, f)).then(() => true).catch(() => false);
56
+ const has = async (f) => fs2.access(path2.join(cwd, f)).then(() => true).catch(() => false);
149
57
  if (await has("pnpm-lock.yaml")) return "pnpm";
150
58
  if (await has("yarn.lock")) return "yarn";
151
59
  if (await has("bun.lockb")) return "bun";
@@ -170,7 +78,6 @@ async function installDependencies(cwd, pm, deps) {
170
78
  const child = spawn(pm, args, {
171
79
  cwd,
172
80
  stdio: "inherit",
173
- // On Windows, package managers are .cmd shims that need a shell.
174
81
  shell: process.platform === "win32"
175
82
  });
176
83
  child.on("error", reject);
@@ -182,33 +89,34 @@ async function installDependencies(cwd, pm, deps) {
182
89
  }
183
90
 
184
91
  // src/utils/paths.ts
185
- import { promises as fs4 } from "fs";
186
- import path4 from "path";
92
+ import { promises as fs3 } from "fs";
93
+ import path3 from "path";
187
94
  async function resolveBaseDir(cwd) {
188
95
  for (const file of ["tsconfig.json", "jsconfig.json"]) {
189
96
  try {
190
- const raw = await fs4.readFile(path4.join(cwd, file), "utf8");
97
+ const raw = await fs3.readFile(path3.join(cwd, file), "utf8");
191
98
  const json = JSON.parse(stripJsonComments(raw));
192
99
  const paths = json?.compilerOptions?.paths;
193
100
  const baseUrl = json?.compilerOptions?.baseUrl ?? ".";
194
101
  const target = paths?.["@/*"]?.[0];
195
102
  if (target) {
196
103
  const dir = target.replace(/\/\*$/, "");
197
- return path4.resolve(cwd, baseUrl, dir);
104
+ return path3.resolve(cwd, baseUrl, dir);
198
105
  }
199
106
  } catch {
107
+ continue;
200
108
  }
201
109
  }
202
110
  try {
203
- const srcStat = await fs4.stat(path4.join(cwd, "src"));
204
- if (srcStat.isDirectory()) return path4.join(cwd, "src");
111
+ const srcStat = await fs3.stat(path3.join(cwd, "src"));
112
+ if (srcStat.isDirectory()) return path3.join(cwd, "src");
205
113
  } catch {
206
114
  }
207
115
  return cwd;
208
116
  }
209
117
  function aliasToDir(alias, baseDir) {
210
118
  const rel = alias.replace(/^@\//, "");
211
- return path4.join(baseDir, rel);
119
+ return path3.join(baseDir, rel);
212
120
  }
213
121
  function targetDirForType(type, aliases, baseDir) {
214
122
  switch (type) {
@@ -232,10 +140,7 @@ function rewriteImports(content, config) {
232
140
  let result = content;
233
141
  for (const [from, to] of replacements) {
234
142
  if (from === to) continue;
235
- const pattern = new RegExp(
236
- `(["'\`])${escapeRegExp(from)}(?=["'\`/])`,
237
- "g"
238
- );
143
+ const pattern = new RegExp(`(["'\`])${escapeRegExp(from)}(?=["'\`/])`, "g");
239
144
  result = result.replace(pattern, `$1${to}`);
240
145
  }
241
146
  return result;
@@ -247,7 +152,211 @@ function stripJsonComments(input) {
247
152
  return input.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1").replace(/,(\s*[}\]])/g, "$1");
248
153
  }
249
154
 
155
+ // src/utils/registry.ts
156
+ async function fetchRegistryItem(registry, name) {
157
+ const base = registry.replace(/\/$/, "");
158
+ const url = `${base}/r/${name}.json`;
159
+ let res;
160
+ try {
161
+ res = await fetch(url);
162
+ } catch (err) {
163
+ throw new Error(
164
+ `Could not reach the registry at ${url}. Is it running / correct?
165
+ ${err.message}`
166
+ );
167
+ }
168
+ if (res.status === 404) {
169
+ throw new Error(`Component "${name}" was not found in the registry (${url}).`);
170
+ }
171
+ if (!res.ok) {
172
+ throw new Error(`Failed to fetch ${url} (HTTP ${res.status}).`);
173
+ }
174
+ return await res.json();
175
+ }
176
+ async function fetchIndex(registry) {
177
+ const base = registry.replace(/\/$/, "");
178
+ const url = `${base}/r/index.json`;
179
+ const res = await fetch(url);
180
+ if (!res.ok) throw new Error(`Failed to fetch ${url} (HTTP ${res.status}).`);
181
+ return await res.json();
182
+ }
183
+ async function fetchTheme(registry) {
184
+ const base = registry.replace(/\/$/, "");
185
+ const url = `${base}/r/theme.json`;
186
+ const res = await fetch(url);
187
+ if (!res.ok) throw new Error(`Failed to fetch ${url} (HTTP ${res.status}).`);
188
+ const data = await res.json();
189
+ return data.css;
190
+ }
191
+ async function resolveTree(registry, names) {
192
+ const resolved = /* @__PURE__ */ new Map();
193
+ async function visit(name) {
194
+ if (resolved.has(name)) return;
195
+ const item = await fetchRegistryItem(registry, name);
196
+ for (const dep of item.registryDependencies ?? []) {
197
+ await visit(dep);
198
+ }
199
+ if (!resolved.has(name)) resolved.set(name, item);
200
+ }
201
+ for (const name of names) {
202
+ await visit(name);
203
+ }
204
+ return [...resolved.values()];
205
+ }
206
+
207
+ // src/utils/css.ts
208
+ var THEME_START = "/* lorre-blocks theme start */";
209
+ var THEME_END = "/* lorre-blocks theme end */";
210
+ var TAILWIND_IMPORT = '@import "tailwindcss";';
211
+ function hasTheme(cssContent) {
212
+ return cssContent.includes(THEME_START);
213
+ }
214
+ function injectThemeBlock(cssContent, themeCss) {
215
+ const block = `${THEME_START}
216
+ ${themeCss.trim()}
217
+ ${THEME_END}`;
218
+ if (hasTheme(cssContent)) {
219
+ return cssContent.replace(
220
+ new RegExp(
221
+ `${escapeRegExp2(THEME_START)}[\\s\\S]*?${escapeRegExp2(THEME_END)}`
222
+ ),
223
+ block
224
+ );
225
+ }
226
+ const trimmed = cssContent.trimEnd();
227
+ if (trimmed.includes(TAILWIND_IMPORT)) {
228
+ const lines = trimmed.split("\n");
229
+ const idx = lines.findIndex((l) => l.trim() === TAILWIND_IMPORT);
230
+ lines.splice(idx + 1, 0, "", block);
231
+ return lines.join("\n") + "\n";
232
+ }
233
+ const prefix = trimmed.length > 0 ? `${trimmed}
234
+
235
+ ` : "";
236
+ return `${TAILWIND_IMPORT}
237
+
238
+ ${prefix}${block}
239
+ `;
240
+ }
241
+ function escapeRegExp2(s) {
242
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
243
+ }
244
+
245
+ // src/commands/init.ts
246
+ var BASE_DEPENDENCIES = [
247
+ "clsx",
248
+ "tailwind-merge",
249
+ "class-variance-authority",
250
+ "tw-animate-css"
251
+ ];
252
+ var GLOBAL_CSS_CANDIDATES = [
253
+ "src/app/globals.css",
254
+ "app/globals.css",
255
+ "src/index.css",
256
+ "src/styles/globals.css",
257
+ "styles/globals.css"
258
+ ];
259
+ async function runInit(options) {
260
+ const { cwd } = options;
261
+ p.intro(color.bgCyan(color.black(" lorre-blocks init ")));
262
+ if (await configExists(cwd)) {
263
+ const overwrite = options.yes ? true : await p.confirm({
264
+ message: "components.json already exists. Overwrite it?",
265
+ initialValue: false
266
+ });
267
+ if (p.isCancel(overwrite) || !overwrite) {
268
+ p.cancel("Init aborted; existing components.json kept.");
269
+ return;
270
+ }
271
+ }
272
+ const tsx = await fileExists(path4.join(cwd, "tsconfig.json"));
273
+ const registry = options.registry ?? (options.yes ? DEFAULT_REGISTRY : await promptText("Registry URL", DEFAULT_REGISTRY));
274
+ const config = {
275
+ $schema: "https://lorre-blocks.dev/schema.json",
276
+ registry,
277
+ tsx,
278
+ aliases: { ...DEFAULT_ALIASES }
279
+ };
280
+ await writeConfig(cwd, config);
281
+ p.log.success("Wrote components.json");
282
+ const pm = await detectPackageManager(cwd);
283
+ const depSpinner = p.spinner();
284
+ depSpinner.start(`Installing base dependencies with ${pm}`);
285
+ try {
286
+ await installDependencies(cwd, pm, BASE_DEPENDENCIES);
287
+ depSpinner.stop(`Installed: ${BASE_DEPENDENCIES.join(", ")}`);
288
+ } catch (err) {
289
+ depSpinner.stop("Dependency install failed.");
290
+ p.cancel(err.message);
291
+ process.exit(1);
292
+ }
293
+ const baseDir = await resolveBaseDir(cwd);
294
+ try {
295
+ const utils = await fetchRegistryItem(registry, "utils");
296
+ for (const file of utils.files) {
297
+ const dir = targetDirForType(file.type, config.aliases, baseDir);
298
+ await fs4.mkdir(dir, { recursive: true });
299
+ const dest = path4.join(dir, path4.basename(file.path));
300
+ await fs4.writeFile(dest, rewriteImports(file.content, config), "utf8");
301
+ p.log.success(`Wrote ${path4.relative(cwd, dest)}`);
302
+ }
303
+ } catch (err) {
304
+ p.log.warn(`Could not write utils: ${err.message}`);
305
+ }
306
+ try {
307
+ const themeCss = await fetchTheme(registry);
308
+ const cssPath = await resolveGlobalCss(cwd);
309
+ const existing = await readIfExists(cssPath);
310
+ const next = injectThemeBlock(existing ?? "", themeCss);
311
+ await fs4.mkdir(path4.dirname(cssPath), { recursive: true });
312
+ await fs4.writeFile(cssPath, next, "utf8");
313
+ p.log.success(
314
+ `${existing === null ? "Created" : "Updated"} ${path4.relative(cwd, cssPath)} with the theme`
315
+ );
316
+ } catch (err) {
317
+ p.log.warn(`Could not set up the theme: ${err.message}`);
318
+ }
319
+ p.outro(
320
+ `${color.green("\u2714")} Ready. Now run ${color.cyan("lorre-blocks add button")}.`
321
+ );
322
+ }
323
+ async function resolveGlobalCss(cwd) {
324
+ for (const candidate of GLOBAL_CSS_CANDIDATES) {
325
+ if (await fileExists(path4.join(cwd, candidate))) {
326
+ return path4.join(cwd, candidate);
327
+ }
328
+ }
329
+ return path4.join(cwd, GLOBAL_CSS_CANDIDATES[0]);
330
+ }
331
+ async function readIfExists(file) {
332
+ try {
333
+ return await fs4.readFile(file, "utf8");
334
+ } catch {
335
+ return null;
336
+ }
337
+ }
338
+ async function promptText(message, initial) {
339
+ const value = await p.text({ message, placeholder: initial, defaultValue: initial });
340
+ if (p.isCancel(value)) {
341
+ p.cancel("Init cancelled.");
342
+ process.exit(0);
343
+ }
344
+ return value || initial;
345
+ }
346
+ async function fileExists(f) {
347
+ try {
348
+ await fs4.access(f);
349
+ return true;
350
+ } catch {
351
+ return false;
352
+ }
353
+ }
354
+
250
355
  // src/commands/add.ts
356
+ import { promises as fs5 } from "fs";
357
+ import path5 from "path";
358
+ import * as p2 from "@clack/prompts";
359
+ import color2 from "picocolors";
251
360
  async function runAdd(options) {
252
361
  const { cwd, components } = options;
253
362
  p2.intro(color2.bgCyan(color2.black(" lorre-blocks add ")));
@@ -262,17 +371,17 @@ async function runAdd(options) {
262
371
  p2.cancel("Specify at least one component, e.g. `lorre-blocks add button`.");
263
372
  process.exit(1);
264
373
  }
265
- const spinner2 = p2.spinner();
266
- spinner2.start("Resolving components from the registry");
374
+ const spinner3 = p2.spinner();
375
+ spinner3.start("Resolving components from the registry");
267
376
  let tree;
268
377
  try {
269
378
  tree = await resolveTree(config.registry, components);
270
379
  } catch (err) {
271
- spinner2.stop("Failed to resolve components.");
380
+ spinner3.stop("Failed to resolve components.");
272
381
  p2.cancel(err.message);
273
382
  process.exit(1);
274
383
  }
275
- spinner2.stop(
384
+ spinner3.stop(
276
385
  `Resolved ${tree.length} item(s): ${tree.map((t) => t.name).join(", ")}`
277
386
  );
278
387
  const npmDeps = [...new Set(tree.flatMap((t) => t.dependencies ?? []))];
@@ -327,9 +436,116 @@ async function fileExists2(f) {
327
436
  }
328
437
  }
329
438
 
439
+ // src/commands/list.ts
440
+ import * as p3 from "@clack/prompts";
441
+ import color3 from "picocolors";
442
+ async function runList(options) {
443
+ const config = await readConfig(options.cwd);
444
+ const registry = options.registry ?? config?.registry ?? DEFAULT_REGISTRY;
445
+ p3.intro(color3.bgCyan(color3.black(" lorre-blocks list ")));
446
+ let items;
447
+ try {
448
+ items = await fetchIndex(registry);
449
+ } catch (err) {
450
+ p3.cancel(err.message);
451
+ process.exit(1);
452
+ }
453
+ const width = Math.max(...items.map((i) => i.name.length), 4);
454
+ const lines = items.map(
455
+ (i) => ` ${color3.green(i.name.padEnd(width))} ${color3.dim(i.description ?? "")}`
456
+ );
457
+ p3.log.message(`${items.length} component(s):
458
+ ${lines.join("\n")}`);
459
+ p3.outro(`Add one with ${color3.cyan("lorre-blocks add <name>")}.`);
460
+ }
461
+
462
+ // src/commands/diff.ts
463
+ import { promises as fs6 } from "fs";
464
+ import path6 from "path";
465
+ import * as p4 from "@clack/prompts";
466
+ import color4 from "picocolors";
467
+ import { createTwoFilesPatch } from "diff";
468
+ async function runDiff(options) {
469
+ const { cwd } = options;
470
+ p4.intro(color4.bgCyan(color4.black(" lorre-blocks diff ")));
471
+ const config = await readConfig(cwd);
472
+ if (!config) {
473
+ p4.cancel("No components.json found. Run `lorre-blocks init` first.");
474
+ process.exit(1);
475
+ }
476
+ const baseDir = await resolveBaseDir(cwd);
477
+ let names = options.components;
478
+ if (names.length === 0) {
479
+ const uiDir = targetDirForType("registry:ui", config.aliases, baseDir);
480
+ const present = await listComponentNames(uiDir);
481
+ const index = await fetchIndex(config.registry).catch(() => []);
482
+ const known = new Set(index.map((i) => i.name));
483
+ names = present.filter((n) => known.has(n));
484
+ if (names.length === 0) {
485
+ p4.cancel("No known components found in your project to diff.");
486
+ process.exit(0);
487
+ }
488
+ }
489
+ let changed = 0;
490
+ for (const name of names) {
491
+ const item = await fetchRegistryItem(config.registry, name);
492
+ for (const file of item.files) {
493
+ const dir = targetDirForType(file.type, config.aliases, baseDir);
494
+ const dest = path6.join(dir, path6.basename(file.path));
495
+ const local = await readIfExists2(dest);
496
+ const registryContent = rewriteImports(file.content, config);
497
+ if (local === null) {
498
+ p4.log.warn(`${name}: ${path6.relative(cwd, dest)} not added yet`);
499
+ continue;
500
+ }
501
+ if (local === registryContent) {
502
+ p4.log.success(`${name}: up to date`);
503
+ continue;
504
+ }
505
+ changed++;
506
+ const patch = createTwoFilesPatch(
507
+ "your version",
508
+ "registry",
509
+ local,
510
+ registryContent,
511
+ "",
512
+ ""
513
+ );
514
+ p4.log.message(`${color4.bold(name)} \u2014 ${path6.relative(cwd, dest)}
515
+ ${colorizePatch(patch)}`);
516
+ }
517
+ }
518
+ p4.outro(
519
+ changed === 0 ? `${color4.green("\u2714")} Everything matches the registry.` : `${changed} file(s) differ from the registry.`
520
+ );
521
+ }
522
+ function colorizePatch(patch) {
523
+ return patch.split("\n").map((line) => {
524
+ if (line.startsWith("+") && !line.startsWith("+++")) return color4.green(line);
525
+ if (line.startsWith("-") && !line.startsWith("---")) return color4.red(line);
526
+ if (line.startsWith("@@")) return color4.cyan(line);
527
+ return color4.dim(line);
528
+ }).join("\n");
529
+ }
530
+ async function listComponentNames(dir) {
531
+ try {
532
+ const entries = await fs6.readdir(dir);
533
+ return entries.filter((f) => /\.(tsx|jsx|ts|js)$/.test(f)).map((f) => f.replace(/\.(tsx|jsx|ts|js)$/, ""));
534
+ } catch {
535
+ return [];
536
+ }
537
+ }
538
+ async function readIfExists2(file) {
539
+ try {
540
+ return await fs6.readFile(file, "utf8");
541
+ } catch {
542
+ return null;
543
+ }
544
+ }
545
+
330
546
  // src/index.ts
331
547
  var program = new Command();
332
- program.name("lorre-blocks").description("Add lorre-blocks components to your project by copying their source in.").version("0.1.0");
548
+ program.name("lorre-blocks").description("Add lorre-blocks components to your project by copying their source in.").version("0.2.0");
333
549
  program.command("init").description("Create a components.json config in your project.").option("-c, --cwd <path>", "working directory", process.cwd()).option("-r, --registry <url>", "registry base URL").option("-y, --yes", "skip prompts and accept defaults", false).action(async (opts) => {
334
550
  await runInit({
335
551
  cwd: opts.cwd,
@@ -345,6 +561,12 @@ program.command("add").description("Add one or more components to your project."
345
561
  overwrite: opts.overwrite
346
562
  });
347
563
  });
564
+ program.command("list").description("List the components available in the registry.").option("-c, --cwd <path>", "working directory", process.cwd()).option("-r, --registry <url>", "registry base URL").action(async (opts) => {
565
+ await runList({ cwd: opts.cwd, registry: opts.registry });
566
+ });
567
+ program.command("diff").description("Show how your local components differ from the registry.").argument("[components...]", "component names; omit to diff all present").option("-c, --cwd <path>", "working directory", process.cwd()).action(async (components, opts) => {
568
+ await runDiff({ cwd: opts.cwd, components });
569
+ });
348
570
  program.parseAsync(process.argv).catch((err) => {
349
571
  console.error(err);
350
572
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lorre-blocks",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Add lorre-blocks components to your project by copying their source in.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,10 +9,6 @@
9
9
  "files": [
10
10
  "dist"
11
11
  ],
12
- "scripts": {
13
- "build": "tsup",
14
- "dev": "tsup --watch"
15
- },
16
12
  "keywords": [
17
13
  "cli",
18
14
  "components",
@@ -23,21 +19,30 @@
23
19
  "license": "MIT",
24
20
  "repository": {
25
21
  "type": "git",
26
- "url": "git+https://github.com/zukazine/lorre-blocks.git",
22
+ "url": "git+https://github.com/odama-dev/lorre-blocks.git",
27
23
  "directory": "packages/cli"
28
24
  },
29
- "homepage": "https://github.com/zukazine/lorre-blocks#readme",
25
+ "homepage": "https://github.com/odama-dev/lorre-blocks#readme",
30
26
  "publishConfig": {
31
27
  "access": "public"
32
28
  },
33
29
  "dependencies": {
34
30
  "@clack/prompts": "^0.9.1",
35
31
  "commander": "^13.0.0",
32
+ "diff": "^9.0.0",
36
33
  "picocolors": "^1.1.1"
37
34
  },
38
35
  "devDependencies": {
36
+ "@types/diff": "^7.0.0",
39
37
  "@types/node": "^22.10.5",
40
38
  "tsup": "^8.3.5",
41
- "typescript": "^5.7.2"
39
+ "typescript": "^5.7.2",
40
+ "vitest": "^4.1.10"
41
+ },
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "dev": "tsup --watch",
45
+ "test": "vitest run",
46
+ "typecheck": "tsc --noEmit"
42
47
  }
43
- }
48
+ }