digdesign 0.2.0 → 0.3.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 (2) hide show
  1. package/dist/index.js +376 -46
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -53,6 +53,60 @@ async function writeConfig(config, cwd = process.cwd()) {
53
53
  );
54
54
  }
55
55
 
56
+ // src/lib/errors.ts
57
+ var DigError = class extends Error {
58
+ constructor(message, options) {
59
+ super(message, options);
60
+ this.name = "DigError";
61
+ }
62
+ };
63
+
64
+ // src/lib/prompt.ts
65
+ import * as readline from "readline/promises";
66
+ import { stdin, stdout } from "process";
67
+ async function askLine(question) {
68
+ const rl = readline.createInterface({ input: stdin, output: stdout });
69
+ try {
70
+ return await rl.question(question);
71
+ } catch {
72
+ console.log();
73
+ return "";
74
+ } finally {
75
+ rl.close();
76
+ }
77
+ }
78
+ async function confirm(question, defaultValue) {
79
+ const suffix = defaultValue ? "(Y/n)" : "(y/N)";
80
+ const raw = await askLine(`${question} ${suffix} `);
81
+ const answer = raw.trim().toLowerCase();
82
+ if (!answer) return defaultValue;
83
+ return answer === "y" || answer === "yes";
84
+ }
85
+ async function ask(question, defaultValue) {
86
+ const raw = await askLine(`${question} (${defaultValue}) `);
87
+ return raw.trim() || defaultValue;
88
+ }
89
+ async function multiSelect(question, options) {
90
+ if (options.length === 0) return [];
91
+ const width = String(options.length).length;
92
+ for (const [index, option] of options.entries()) {
93
+ const number = String(index + 1).padStart(width);
94
+ console.log(` ${number}) ${option.label}`);
95
+ }
96
+ const raw = await askLine(`${question} `);
97
+ return parseSelection(raw, options);
98
+ }
99
+ function parseSelection(raw, options) {
100
+ const picked = /* @__PURE__ */ new Set();
101
+ for (const token of raw.split(/[\s,،]+/).filter(Boolean)) {
102
+ const index = Number(token) - 1;
103
+ if (Number.isInteger(index) && index >= 0 && index < options.length) {
104
+ picked.add(options[index].value);
105
+ }
106
+ }
107
+ return [...picked];
108
+ }
109
+
56
110
  // src/lib/hash.ts
57
111
  import { createHash } from "crypto";
58
112
  function hashContent(content) {
@@ -125,31 +179,62 @@ function installArgs(pm, deps, dev) {
125
179
  const devFlag = dev ? pm === "npm" ? "--save-dev" : "-D" : null;
126
180
  return devFlag ? [verb, devFlag, ...deps] : [verb, ...deps];
127
181
  }
182
+ var PEER_CONFLICT_FLAG = {
183
+ npm: "--legacy-peer-deps",
184
+ pnpm: "--strict-peer-dependencies=false"
185
+ };
186
+ function installFailure(pm, args, code, stderr) {
187
+ const command = `${pm} ${args.join(" ")}`;
188
+ const lines = [`\u0646\u0635\u0628\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0634\u06A9\u0633\u062A \u062E\u0648\u0631\u062F (${command}\u060C \u06A9\u062F ${code ?? "?"}).`];
189
+ if (/ERESOLVE|peer dep|ERR_PNPM_PEER_DEP_ISSUES/i.test(stderr)) {
190
+ const flag = PEER_CONFLICT_FLAG[pm];
191
+ lines.push(
192
+ " \u0639\u0644\u062A: \u062A\u0639\u0627\u0631\u0636\u0650 peer dependency \u062F\u0631 \u062F\u0631\u062E\u062A\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u0650 \u062E\u0648\u062F\u0650 \u067E\u0631\u0648\u0698\u0647\u060C \u0646\u0647 \u062F\u0631 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u062F\u06CC\u06AF."
193
+ );
194
+ lines.push(" \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\u0646\u062F\u061B \u0641\u0642\u0637 \u0646\u0635\u0628\u0650 \u067E\u06A9\u06CC\u062C\u200C\u0647\u0627 \u0645\u0627\u0646\u062F\u0647.");
195
+ lines.push(
196
+ flag ? ` \u062F\u0633\u062A\u06CC \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F: ${command} ${flag}` : ` \u0627\u0648\u0644 \u062F\u0631\u062E\u062A\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC \u0631\u0627 \u062F\u0631\u0633\u062A \u06A9\u0646\u06CC\u062F\u060C \u0628\u0639\u062F: ${command}`
197
+ );
198
+ } else {
199
+ lines.push(" \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\u0646\u062F\u061B \u0641\u0642\u0637 \u0646\u0635\u0628\u0650 \u067E\u06A9\u06CC\u062C\u200C\u0647\u0627 \u0645\u0627\u0646\u062F\u0647.");
200
+ lines.push(` \u062E\u0631\u0648\u062C\u06CC\u0650 \u06A9\u0627\u0645\u0644\u0650 ${pm} \u0628\u0627\u0644\u0627\u062A\u0631 \u0686\u0627\u067E \u0634\u062F\u0647. \u0628\u0631\u0627\u06CC \u062A\u0644\u0627\u0634 \u062F\u0648\u0628\u0627\u0631\u0647: ${command}`);
201
+ }
202
+ lines.push(" \u0628\u0631\u0627\u06CC \u0631\u062F \u06A9\u0631\u062F\u0646\u0650 \u0646\u0635\u0628\u0650 \u062E\u0648\u062F\u06A9\u0627\u0631 \u0627\u0632 \u0627\u06CC\u0646 \u0628\u0647 \u0628\u0639\u062F: dig add \u2026 --no-install");
203
+ return new DigError(lines.join("\n"));
204
+ }
128
205
  function installDependencies(pm, deps, options = {}) {
129
206
  if (!deps.length) return Promise.resolve();
130
207
  const args = installArgs(pm, deps, Boolean(options.dev));
131
208
  return new Promise((resolve, reject) => {
132
209
  const child = spawn(pm, args, {
133
210
  cwd: options.cwd ?? process.cwd(),
134
- stdio: "inherit",
211
+ // stderr پایپ می‌شود تا هم مثل قبل بی‌کم‌وکاست به کاربر برسد، هم بتوانیم
212
+ // علتِ شکست را از رویش تشخیص بدهیم. stdout همچنان inherit است چون
213
+ // نوارِ پیشرفتِ پکیج‌منیجر باید زنده بماند.
214
+ stdio: ["inherit", "inherit", "pipe"],
135
215
  shell: process.platform === "win32"
136
216
  });
217
+ let stderr = "";
218
+ child.stderr?.on("data", (chunk) => {
219
+ const text = chunk.toString();
220
+ stderr += text;
221
+ process.stderr.write(text);
222
+ });
137
223
  child.on("exit", (code) => {
138
224
  if (code === 0) resolve();
139
- else reject(new Error(`${pm} ${args.join(" ")} \u0628\u0627 \u06A9\u062F ${code} \u0634\u06A9\u0633\u062A \u062E\u0648\u0631\u062F.`));
225
+ else reject(installFailure(pm, args, code, stderr));
226
+ });
227
+ child.on("error", (cause) => {
228
+ reject(
229
+ new DigError(
230
+ `\xAB${pm}\xBB \u0627\u062C\u0631\u0627 \u0646\u0634\u062F. \u0645\u0637\u0645\u0626\u0646 \u0634\u0648\u06CC\u062F \u0646\u0635\u0628 \u0627\u0633\u062A \u0648 \u062F\u0631 PATH \u0642\u0631\u0627\u0631 \u062F\u0627\u0631\u062F.`,
231
+ { cause }
232
+ )
233
+ );
140
234
  });
141
- child.on("error", reject);
142
235
  });
143
236
  }
144
237
 
145
- // src/lib/errors.ts
146
- var DigError = class extends Error {
147
- constructor(message, options) {
148
- super(message, options);
149
- this.name = "DigError";
150
- }
151
- };
152
-
153
238
  // src/lib/registry.ts
154
239
  function resolveUrl(nameOrUrl, registryUrl) {
155
240
  if (/^https?:\/\//.test(nameOrUrl)) return nameOrUrl;
@@ -284,7 +369,10 @@ import path5 from "path";
284
369
  function toPosixPath(relativePath) {
285
370
  return relativePath.split(path5.sep).join("/");
286
371
  }
287
- function resolveTargetPath(filePath, aliases) {
372
+ function resolveTargetPath(filePath, aliases, options = {}) {
373
+ if (/(?:^|\/)app\/globals\.css$/.test(filePath)) {
374
+ return options.tailwindCss ?? "src/app/globals.css";
375
+ }
288
376
  const match = filePath.match(/(?:^|\/)(components|lib|hooks)\/(.*)$/);
289
377
  const cleaned = match ? `${match[1]}/${match[2]}` : filePath;
290
378
  if (cleaned.startsWith("components/ui/")) {
@@ -311,11 +399,82 @@ async function pathExists(target) {
311
399
  return false;
312
400
  }
313
401
  }
402
+ async function pickComponents(registryUrl) {
403
+ if (!process.stdin.isTTY) {
404
+ throw new DigError(
405
+ "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0631\u0627 \u0628\u062F\u0647\u06CC\u062F: dig add button\n \u0628\u0631\u0627\u06CC \u062F\u06CC\u062F\u0646\u0650 \u0641\u0647\u0631\u0633\u062A: dig list"
406
+ );
407
+ }
408
+ const index = await fetchRegistryIndex(registryUrl);
409
+ const choices = index.items.filter((item) => item.type === "registry:ui").map((item) => ({
410
+ value: item.name,
411
+ label: item.title ? `${item.name} \u2014 ${item.title}` : item.name
412
+ }));
413
+ console.log(`
414
+ ${choices.length} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC:
415
+ `);
416
+ const picked = await multiSelect(
417
+ "\n\u0634\u0645\u0627\u0631\u0647\u200C\u0647\u0627 \u0631\u0627 \u0628\u0627 \u0641\u0627\u0635\u0644\u0647 \u0628\u062F\u0647\u06CC\u062F (\u062E\u0627\u0644\u06CC = \u0644\u063A\u0648):",
418
+ choices
419
+ );
420
+ if (picked.length === 0) {
421
+ console.log("\u0686\u06CC\u0632\u06CC \u0627\u0646\u062A\u062E\u0627\u0628 \u0646\u0634\u062F.");
422
+ return [];
423
+ }
424
+ return picked;
425
+ }
314
426
  async function addCommand(components, options) {
315
427
  const cwd = process.cwd();
316
428
  const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
429
+ const names = components.length ? components : await pickComponents(config.registryUrl);
430
+ if (names.length === 0) return;
317
431
  console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A \u0627\u0632 ${config.registryUrl} \u2026`);
318
- const items = await resolveRegistryTree(components, config.registryUrl);
432
+ const items = await resolveRegistryTree(names, config.registryUrl);
433
+ let overwrite = Boolean(options.overwrite);
434
+ if (!overwrite) {
435
+ const existing = [];
436
+ for (const item of items.values()) {
437
+ for (const file of item.files) {
438
+ const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases, {
439
+ tailwindCss: config.tailwind.css
440
+ }));
441
+ if (await pathExists(target)) existing.push(path6.relative(cwd, target));
442
+ }
443
+ }
444
+ if (existing.length) {
445
+ if (options.yes) {
446
+ overwrite = true;
447
+ } else if (process.stdin.isTTY) {
448
+ console.log(`
449
+ ${existing.length} \u0641\u0627\u06CC\u0644 \u0627\u0632 \u0642\u0628\u0644 \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F:`);
450
+ for (const file of existing.slice(0, 10)) console.log(` ${file}`);
451
+ if (existing.length > 10) console.log(` \u2026 \u0648 ${existing.length - 10} \u0641\u0627\u06CC\u0644 \u062F\u06CC\u06AF\u0631`);
452
+ overwrite = await confirm("\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0634\u0648\u0646\u062F\u061F", false);
453
+ }
454
+ }
455
+ }
456
+ if (options.dryRun) {
457
+ const npmDeps = /* @__PURE__ */ new Set();
458
+ console.log("\n\u067E\u06CC\u0634\u200C\u0646\u0645\u0627\u06CC\u0634 (\u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u0646\u0648\u0634\u062A\u0647 \u0646\u0645\u06CC\u200C\u0634\u0648\u062F):\n");
459
+ for (const item of items.values()) {
460
+ for (const dep of item.dependencies ?? []) npmDeps.add(dep);
461
+ for (const file of item.files) {
462
+ const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases, {
463
+ tailwindCss: config.tailwind.css
464
+ }));
465
+ const exists = await pathExists(target);
466
+ const mark = exists ? overwrite ? "~" : "-" : "+";
467
+ const note = exists ? overwrite ? " (\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC)" : " (\u0631\u062F \u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F)" : "";
468
+ console.log(`${mark} ${path6.relative(cwd, target)}${note}`);
469
+ }
470
+ }
471
+ console.log(`
472
+ ${items.size} \u0622\u06CC\u062A\u0645 \u0627\u0632 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC.`);
473
+ if (npmDeps.size) {
474
+ console.log(`\u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627\u06CC npm: ${[...npmDeps].join(" ")}`);
475
+ }
476
+ return;
477
+ }
319
478
  const dependencies = /* @__PURE__ */ new Set();
320
479
  const devDependencies = /* @__PURE__ */ new Set();
321
480
  let written = 0;
@@ -329,8 +488,10 @@ async function addCommand(components, options) {
329
488
  const writtenFiles = {};
330
489
  const writtenContents = {};
331
490
  for (const file of item.files) {
332
- const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases));
333
- if (!options.overwrite && await pathExists(target)) {
491
+ const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases, {
492
+ tailwindCss: config.tailwind.css
493
+ }));
494
+ if (!overwrite && await pathExists(target)) {
334
495
  console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${path6.relative(cwd, target)}`);
335
496
  skipped++;
336
497
  continue;
@@ -384,6 +545,107 @@ ${written} \u0641\u0627\u06CC\u0644 \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\
384
545
  }
385
546
  }
386
547
 
548
+ // src/lib/search.ts
549
+ var ARABIC_TO_PERSIAN = {
550
+ "\u064A": "\u06CC",
551
+ // ي → ی
552
+ "\u0649": "\u06CC",
553
+ // ى → ی
554
+ "\u0643": "\u06A9",
555
+ // ك → ک
556
+ "\u0629": "\u0647",
557
+ // ة → ه
558
+ "\u0623": "\u0627",
559
+ // أ → ا
560
+ "\u0625": "\u0627",
561
+ // إ → ا
562
+ "\u0622": "\u0627",
563
+ // آ → ا
564
+ "\u0624": "\u0648"
565
+ // ؤ → و
566
+ };
567
+ var DIACRITICS = /[ً-ْٰ]/g;
568
+ var INVISIBLE = /[​-‏؜]/g;
569
+ function toSearchKey(value) {
570
+ return value.replace(/[يىكةأإآؤ]/g, (ch) => ARABIC_TO_PERSIAN[ch]).replace(DIACRITICS, "").replace(INVISIBLE, "").toLowerCase().replace(/[\s‌]+/g, "");
571
+ }
572
+ function scoreItem(item, query) {
573
+ const q = toSearchKey(query);
574
+ if (!q) return 0;
575
+ const name = toSearchKey(item.name);
576
+ const title = toSearchKey(item.title ?? "");
577
+ const description = toSearchKey(item.description ?? "");
578
+ if (name === q || title === q) return 100;
579
+ if (name.startsWith(q) || title.startsWith(q)) return 80;
580
+ if (name.includes(q) || title.includes(q)) return 60;
581
+ if (description.includes(q)) return 30;
582
+ return 0;
583
+ }
584
+ function searchItems(items, query) {
585
+ return items.map((item) => ({ item, score: scoreItem(item, query) })).filter((entry) => entry.score > 0).sort(
586
+ (a, b) => b.score - a.score || a.item.name.localeCompare(b.item.name)
587
+ ).map((entry) => entry.item);
588
+ }
589
+
590
+ // src/commands/list.ts
591
+ var TYPE_LABEL = {
592
+ "registry:ui": "\u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A",
593
+ "registry:lib": "\u0627\u0628\u0632\u0627\u0631",
594
+ "registry:theme": "\u062A\u0645"
595
+ };
596
+ async function loadItems(cwd) {
597
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
598
+ const index = await fetchRegistryIndex(config.registryUrl);
599
+ return index.items;
600
+ }
601
+ function matchesType(item, type) {
602
+ if (!type) return true;
603
+ const wanted = type.startsWith("registry:") ? type : `registry:${type}`;
604
+ return item.type === wanted;
605
+ }
606
+ function printTable(items) {
607
+ if (items.length === 0) {
608
+ console.log("\u0686\u06CC\u0632\u06CC \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F.");
609
+ return;
610
+ }
611
+ const width = Math.max(...items.map((item) => item.name.length));
612
+ const labelWidth = Math.max(
613
+ ...items.map((item) => (TYPE_LABEL[item.type] ?? item.type).length)
614
+ );
615
+ for (const item of items) {
616
+ const label = (TYPE_LABEL[item.type] ?? item.type).padEnd(labelWidth);
617
+ const title = item.title ? ` \xB7 ${item.title}` : "";
618
+ console.log(`${item.name.padEnd(width)} ${label}${title}`);
619
+ }
620
+ console.log(`
621
+ ${items.length} \u0622\u06CC\u062A\u0645. \u0628\u0631\u0627\u06CC \u0646\u0635\u0628: dig add <\u0646\u0627\u0645>`);
622
+ }
623
+ async function listCommand(options) {
624
+ const items = (await loadItems(process.cwd())).filter(
625
+ (item) => matchesType(item, options.type)
626
+ );
627
+ if (options.json) {
628
+ console.log(JSON.stringify(items, null, 2));
629
+ return;
630
+ }
631
+ printTable(items);
632
+ }
633
+ async function searchCommand(query, options) {
634
+ const items = searchItems(await loadItems(process.cwd()), query);
635
+ if (options.json) {
636
+ console.log(JSON.stringify(items, null, 2));
637
+ return;
638
+ }
639
+ if (items.length === 0) {
640
+ console.log(
641
+ `\u0686\u06CC\u0632\u06CC \u0628\u0631\u0627\u06CC \xAB${query}\xBB \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F.
642
+ \u0641\u0647\u0631\u0633\u062A \u06A9\u0627\u0645\u0644: dig list`
643
+ );
644
+ return;
645
+ }
646
+ printTable(items);
647
+ }
648
+
387
649
  // src/lib/diff.ts
388
650
  import { readFile as readFile4 } from "fs/promises";
389
651
  import path7 from "path";
@@ -1686,28 +1948,77 @@ async function impactCommand(name, options) {
1686
1948
  }
1687
1949
 
1688
1950
  // src/commands/init.ts
1951
+ import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
1689
1952
  import path10 from "path";
1690
-
1691
- // src/lib/prompt.ts
1692
- import * as readline from "readline/promises";
1693
- import { stdin, stdout } from "process";
1694
- async function confirm(question, defaultValue) {
1695
- const rl = readline.createInterface({ input: stdin, output: stdout });
1696
- const suffix = defaultValue ? "(Y/n)" : "(y/N)";
1697
- const raw = await rl.question(`${question} ${suffix} `);
1698
- rl.close();
1699
- const answer = raw.trim().toLowerCase();
1700
- if (!answer) return defaultValue;
1701
- return answer === "y" || answer === "yes";
1953
+ var THEME_ITEM = "theme";
1954
+ async function readIfExists(target) {
1955
+ try {
1956
+ return await readFile7(target, "utf8");
1957
+ } catch {
1958
+ return null;
1959
+ }
1702
1960
  }
1703
- async function ask(question, defaultValue) {
1704
- const rl = readline.createInterface({ input: stdin, output: stdout });
1705
- const raw = await rl.question(`${question} (${defaultValue}) `);
1706
- rl.close();
1707
- return raw.trim() || defaultValue;
1961
+ async function writeTheme(config, cwd, options) {
1962
+ const item = await fetchRegistryItem(THEME_ITEM, config.registryUrl);
1963
+ const file = item.files[0];
1964
+ if (!file) {
1965
+ throw new DigError("\u0622\u06CC\u062A\u0645\u0650 \xABtheme\xBB \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0641\u0627\u06CC\u0644\u06CC \u0646\u062F\u0627\u0631\u062F.");
1966
+ }
1967
+ const target = path10.join(cwd, config.tailwind.css);
1968
+ const existing = await readIfExists(target);
1969
+ const isBoilerplate = existing === null || existing.split("\n").filter((line) => line.trim() && !line.trim().startsWith("@import")).length === 0;
1970
+ if (!isBoilerplate && !options.yes) {
1971
+ console.log(`
1972
+ ${config.tailwind.css} \u0627\u0632 \u0642\u0628\u0644 \u0645\u062D\u062A\u0648\u0627 \u062F\u0627\u0631\u062F.`);
1973
+ const overwrite = await confirm(
1974
+ "\u0628\u0627 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0634\u0648\u062F\u061F (\u0646\u0633\u062E\u0647\u0654 \u0641\u0639\u0644\u06CC \u06A9\u0646\u0627\u0631\u0634 \u0628\u0647\u200C\u0639\u0646\u0648\u0627\u0646 .backup \u0645\u06CC\u200C\u0645\u0627\u0646\u062F)",
1975
+ false
1976
+ );
1977
+ if (!overwrite) {
1978
+ console.log(
1979
+ `- \u062A\u0645 \u0646\u0648\u0634\u062A\u0647 \u0646\u0634\u062F. \u0628\u0631\u0627\u06CC \u0646\u0648\u0634\u062A\u0646\u0634 \u0628\u0639\u062F\u0627\u064B: dig add ${THEME_ITEM} --overwrite`
1980
+ );
1981
+ return "skipped";
1982
+ }
1983
+ }
1984
+ if (!isBoilerplate && existing !== null) {
1985
+ await writeFile5(`${target}.backup`, existing, "utf8");
1986
+ console.log(` \u0646\u0633\u062E\u0647\u0654 \u0642\u0628\u0644\u06CC \u062F\u0631 ${config.tailwind.css}.backup \u0646\u06AF\u0647 \u062F\u0627\u0634\u062A\u0647 \u0634\u062F.`);
1987
+ }
1988
+ await mkdir4(path10.dirname(target), { recursive: true });
1989
+ await writeFile5(target, file.content, "utf8");
1990
+ console.log(`\u2713 \u062A\u0645 \u062F\u0631 ${config.tailwind.css} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
1991
+ if (item.dependencies?.length) {
1992
+ const pm = await detectPackageManager(cwd);
1993
+ console.log(`
1994
+ \u0646\u0635\u0628 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627\u06CC \u062A\u0645 \u0628\u0627 ${pm} \u2026`);
1995
+ await installDependencies(pm, item.dependencies, { cwd });
1996
+ }
1997
+ return "written";
1998
+ }
1999
+ async function applyRtl(cwd) {
2000
+ for (const candidate of ["src/app/layout.tsx", "app/layout.tsx"]) {
2001
+ const target = path10.join(cwd, candidate);
2002
+ const source = await readIfExists(target);
2003
+ if (source === null) continue;
2004
+ if (/<html[^>]*\sdir=/.test(source)) {
2005
+ console.log(`- ${candidate} \u0627\u0632 \u0642\u0628\u0644 dir \u062F\u0627\u0631\u062F\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`);
2006
+ return;
2007
+ }
2008
+ const match = source.match(/<html\b([^>]*)>/);
2009
+ if (!match) break;
2010
+ const updated = source.replace(
2011
+ match[0],
2012
+ `<html${match[1]} dir="rtl">`.replace(/\s+dir="rtl">$/, ' dir="rtl">')
2013
+ );
2014
+ await writeFile5(target, updated, "utf8");
2015
+ console.log(`\u2713 dir="rtl" \u0628\u0647 ${candidate} \u0627\u0636\u0627\u0641\u0647 \u0634\u062F.`);
2016
+ return;
2017
+ }
2018
+ console.log(
2019
+ '- \u0686\u06CC\u062F\u0645\u0627\u0646\u0650 \u0631\u06CC\u0634\u0647 \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F. \u062E\u0648\u062F\u062A\u0627\u0646 dir="rtl" \u0631\u0627 \u0631\u0648\u06CC \u062A\u06AF <html> \u0628\u06AF\u0630\u0627\u0631\u06CC\u062F.'
2020
+ );
1708
2021
  }
1709
-
1710
- // src/commands/init.ts
1711
2022
  async function initCommand(options) {
1712
2023
  const cwd = process.cwd();
1713
2024
  const base = defaultConfig();
@@ -1731,10 +2042,22 @@ async function initCommand(options) {
1731
2042
  };
1732
2043
  await writeConfig(config, cwd);
1733
2044
  console.log(`\u2713 ${path10.join(cwd, "dig.json")} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
2045
+ const theme = await writeTheme(config, cwd, options);
2046
+ if (config.rtl) {
2047
+ await applyRtl(cwd);
2048
+ }
2049
+ console.log("\n\u0622\u0645\u0627\u062F\u0647 \u0627\u0633\u062A. \u0642\u062F\u0645 \u0628\u0639\u062F\u06CC:");
2050
+ console.log(" dig add button");
2051
+ if (theme === "skipped") {
2052
+ console.log(
2053
+ `
2054
+ \u26A0 \u0628\u062F\u0648\u0646 \u062A\u0645\u060C \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 \u0646\u0635\u0628 \u0645\u06CC\u200C\u0634\u0648\u0646\u062F \u0648\u0644\u06CC \u0628\u06CC\u200C\u0638\u0627\u0647\u0631 \u0645\u06CC\u200C\u0645\u0627\u0646\u0646\u062F (\u0631\u0646\u06AF\u060C \u0634\u0639\u0627\u0639 \u0648 \u0633\u0627\u06CC\u0647 \u0627\u0632 \u062A\u0648\u06A9\u0646 \u0645\u06CC\u200C\u0622\u06CC\u0646\u062F).`
2055
+ );
2056
+ }
1734
2057
  }
1735
2058
 
1736
2059
  // src/lib/migration-apply.ts
1737
- import { readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
2060
+ import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
1738
2061
  import path11 from "path";
1739
2062
 
1740
2063
  // src/lib/migration-strategy.ts
@@ -1812,7 +2135,7 @@ function looksBinary2(buffer) {
1812
2135
  async function readTextFileSafely2(target) {
1813
2136
  let buffer;
1814
2137
  try {
1815
- buffer = await readFile7(target);
2138
+ buffer = await readFile8(target);
1816
2139
  } catch (error) {
1817
2140
  if (error.code === "ENOENT") return void 0;
1818
2141
  throw error;
@@ -1924,12 +2247,12 @@ async function planOrApplyMigrations(params) {
1924
2247
  const written = [];
1925
2248
  try {
1926
2249
  for (const item of pending) {
1927
- await writeFile5(item.target, item.newContent, "utf8");
2250
+ await writeFile6(item.target, item.newContent, "utf8");
1928
2251
  written.push(item);
1929
2252
  }
1930
2253
  } catch (error) {
1931
2254
  for (const item of written) {
1932
- await writeFile5(item.target, item.originalContent, "utf8");
2255
+ await writeFile6(item.target, item.originalContent, "utf8");
1933
2256
  }
1934
2257
  return {
1935
2258
  name,
@@ -2540,7 +2863,7 @@ ${entry.migrationHint}`);
2540
2863
  }
2541
2864
 
2542
2865
  // src/lib/update.ts
2543
- import { mkdir as mkdir4, readFile as readFile8, rm as rm2, writeFile as writeFile6 } from "fs/promises";
2866
+ import { mkdir as mkdir5, readFile as readFile9, rm as rm2, writeFile as writeFile7 } from "fs/promises";
2544
2867
  import path12 from "path";
2545
2868
  function safeFileIndexes(drift) {
2546
2869
  const indexes = [];
@@ -2569,14 +2892,14 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2569
2892
  newContent: registryFile.content,
2570
2893
  newHash: registryFile.hash ?? hashContent(registryFile.content),
2571
2894
  isNewFile,
2572
- originalContent: isNewFile ? void 0 : await readFile8(target, "utf8")
2895
+ originalContent: isNewFile ? void 0 : await readFile9(target, "utf8")
2573
2896
  });
2574
2897
  }
2575
2898
  const written = [];
2576
2899
  try {
2577
2900
  for (const item of pending) {
2578
- if (item.isNewFile) await mkdir4(path12.dirname(item.target), { recursive: true });
2579
- await writeFile6(item.target, item.newContent, "utf8");
2901
+ if (item.isNewFile) await mkdir5(path12.dirname(item.target), { recursive: true });
2902
+ await writeFile7(item.target, item.newContent, "utf8");
2580
2903
  written.push(item);
2581
2904
  }
2582
2905
  } catch (error) {
@@ -2584,7 +2907,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2584
2907
  if (item.isNewFile) {
2585
2908
  await rm2(item.target, { force: true });
2586
2909
  } else {
2587
- await writeFile6(item.target, item.originalContent, "utf8");
2910
+ await writeFile7(item.target, item.originalContent, "utf8");
2588
2911
  }
2589
2912
  }
2590
2913
  return { actions, writtenHashes: {}, rollbackError: error.message };
@@ -2872,11 +3195,18 @@ ${result.name}`);
2872
3195
  }
2873
3196
  }
2874
3197
 
3198
+ // src/lib/version.ts
3199
+ var CLI_VERSION = "0.3.0";
3200
+
2875
3201
  // src/index.ts
2876
3202
  var program = new Command();
2877
- program.name("dig").description("CLI \u0631\u0633\u0645\u06CC \u062F\u06CC\u0632\u0627\u06CC\u0646 \u0633\u06CC\u0633\u062A\u0645 \u062F\u06CC\u06AF").version("0.1.1");
2878
- program.command("init").description("\u0631\u0627\u0647\u200C\u0627\u0646\u062F\u0627\u0632\u06CC \u062F\u06CC\u06AF \u062F\u0631 \u067E\u0631\u0648\u0698\u0647 (\u0633\u0627\u062E\u062A dig.json)").option("--yes", "\u067E\u0627\u0633\u062E \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0628\u0647 \u0647\u0645\u0647\u0654 \u0633\u0648\u0627\u0644\u0627\u062A", false).action(initCommand);
2879
- program.command("add").description("\u0627\u0641\u0632\u0648\u062F\u0646 \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0627\u0632 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button) \u06CC\u0627 \u0622\u062F\u0631\u0633 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC").option("--overwrite", "\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F", false).option("--no-install", "\u0641\u0642\u0637 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0631\u0627 \u0628\u0646\u0648\u06CC\u0633\u060C \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0631\u0627 \u0646\u0635\u0628 \u0646\u06A9\u0646").action(addCommand);
3203
+ program.name("dig").description("CLI \u0631\u0633\u0645\u06CC \u062F\u06CC\u0632\u0627\u06CC\u0646 \u0633\u06CC\u0633\u062A\u0645 \u062F\u06CC\u06AF").version(CLI_VERSION);
3204
+ program.command("init").description(
3205
+ '\u0631\u0627\u0647\u200C\u0627\u0646\u062F\u0627\u0632\u06CC \u062F\u06CC\u06AF \u062F\u0631 \u067E\u0631\u0648\u0698\u0647: dig.json\u060C \u062A\u0645 \u0648 \u062A\u0648\u06A9\u0646\u200C\u0647\u0627\u060C \u0648 dir="rtl"'
3206
+ ).option("--yes", "\u067E\u0627\u0633\u062E \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0628\u0647 \u0647\u0645\u0647\u0654 \u0633\u0648\u0627\u0644\u0627\u062A", false).action(initCommand);
3207
+ program.command("add").description("\u0627\u0641\u0632\u0648\u062F\u0646 \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0627\u0632 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").argument("[components...]", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button) \u06CC\u0627 \u0622\u062F\u0631\u0633 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC\u061B \u0628\u062F\u0648\u0646 \u0622\u0631\u06AF\u0648\u0645\u0627\u0646 \u0641\u0647\u0631\u0633\u062A \u0631\u0627 \u0646\u0634\u0627\u0646 \u0645\u06CC\u200C\u062F\u0647\u062F").option("--overwrite", "\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F", false).option("-y, --yes", "\u0628\u062F\u0648\u0646 \u067E\u0631\u0633\u0634\u060C \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u06A9\u0646", false).option("--dry-run", "\u0641\u0642\u0637 \u0646\u0634\u0627\u0646 \u0628\u062F\u0647 \u0686\u0647 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC\u06CC \u0646\u0648\u0634\u062A\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0686\u06CC\u0632\u06CC \u0646\u0646\u0648\u06CC\u0633", false).option("--no-install", "\u0641\u0642\u0637 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0631\u0627 \u0628\u0646\u0648\u06CC\u0633\u060C \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0631\u0627 \u0646\u0635\u0628 \u0646\u06A9\u0646").action(addCommand);
3208
+ program.command("list").description("\u0641\u0647\u0631\u0633\u062A \u0622\u06CC\u062A\u0645\u200C\u0647\u0627\u06CC \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").option("--type <type>", "\u0641\u06CC\u0644\u062A\u0631 \u0646\u0648\u0639: ui\u060C lib \u06CC\u0627 theme").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(listCommand);
3209
+ program.command("search").description("\u062C\u0633\u062A\u062C\u0648\u06CC \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC\u060C \u0628\u0627 \u0646\u0627\u0645 \u0641\u0627\u0631\u0633\u06CC \u06CC\u0627 \u0627\u0646\u06AF\u0644\u06CC\u0633\u06CC").argument("<query>", "\u0639\u0628\u0627\u0631\u062A \u062C\u0633\u062A\u062C\u0648").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(searchCommand);
2880
3210
  program.command("diff").description(
2881
3211
  "\u0645\u0642\u0627\u06CC\u0633\u0647\u0654 \u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC\u0650 \u0646\u0633\u062E\u0647\u0654 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u0628\u0627 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0648 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u062D\u0644\u06CC (\u0628\u062F\u0648\u0646 \u062A\u063A\u06CC\u06CC\u0631 \u0641\u0627\u06CC\u0644)"
2882
3212
  ).argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).option(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digdesign",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI رسمی برای نصب کامپوننت‌های دیزاین سیستم دیگ",
5
5
  "type": "module",
6
6
  "license": "MIT",