digdesign 0.1.1 → 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.
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;
@@ -158,7 +243,8 @@ function resolveUrl(nameOrUrl, registryUrl) {
158
243
  async function fetchFromRegistry(url, subject) {
159
244
  let response;
160
245
  try {
161
- response = await fetch(url);
246
+ const token = process.env.DIG_TOKEN;
247
+ response = token ? await fetch(url, { headers: { Authorization: `Bearer ${token}` } }) : await fetch(url);
162
248
  } catch (cause) {
163
249
  throw new DigError(
164
250
  `\u0628\u0647 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0648\u0635\u0644 \u0646\u0634\u062F\u0645: ${url}
@@ -283,7 +369,10 @@ import path5 from "path";
283
369
  function toPosixPath(relativePath) {
284
370
  return relativePath.split(path5.sep).join("/");
285
371
  }
286
- 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
+ }
287
376
  const match = filePath.match(/(?:^|\/)(components|lib|hooks)\/(.*)$/);
288
377
  const cleaned = match ? `${match[1]}/${match[2]}` : filePath;
289
378
  if (cleaned.startsWith("components/ui/")) {
@@ -310,11 +399,82 @@ async function pathExists(target) {
310
399
  return false;
311
400
  }
312
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
+ }
313
426
  async function addCommand(components, options) {
314
427
  const cwd = process.cwd();
315
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;
316
431
  console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A \u0627\u0632 ${config.registryUrl} \u2026`);
317
- 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
+ }
318
478
  const dependencies = /* @__PURE__ */ new Set();
319
479
  const devDependencies = /* @__PURE__ */ new Set();
320
480
  let written = 0;
@@ -328,8 +488,10 @@ async function addCommand(components, options) {
328
488
  const writtenFiles = {};
329
489
  const writtenContents = {};
330
490
  for (const file of item.files) {
331
- const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases));
332
- 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)) {
333
495
  console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${path6.relative(cwd, target)}`);
334
496
  skipped++;
335
497
  continue;
@@ -383,6 +545,107 @@ ${written} \u0641\u0627\u06CC\u0644 \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\
383
545
  }
384
546
  }
385
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
+
386
649
  // src/lib/diff.ts
387
650
  import { readFile as readFile4 } from "fs/promises";
388
651
  import path7 from "path";
@@ -1290,7 +1553,7 @@ async function scanRepositoryForComponent(params) {
1290
1553
  }
1291
1554
 
1292
1555
  // src/lib/intelligence-export.ts
1293
- var INTELLIGENCE_EXPORT_SCHEMA_VERSION = "v2";
1556
+ var INTELLIGENCE_EXPORT_SCHEMA_VERSION = "v3";
1294
1557
  function buildIntelligenceExport(params) {
1295
1558
  const { registryIndexItems, installedComponents } = params;
1296
1559
  const healthReport = buildHealthReport({
@@ -1317,7 +1580,8 @@ function buildIntelligenceExport(params) {
1317
1580
  migrationPending: governance.impact.requiredMigration,
1318
1581
  dependencyImpact: governance.impact.affectedComponents.length,
1319
1582
  changeType: governance.changeType,
1320
- replacement: lifecycle.replacement
1583
+ replacement: lifecycle.replacement,
1584
+ affectedComponentNames: governance.impact.affectedComponents
1321
1585
  };
1322
1586
  });
1323
1587
  return {
@@ -1372,6 +1636,9 @@ function validateIntelligenceExport(data) {
1372
1636
  if (typeof entry.changeType !== "string") {
1373
1637
  errors.push(`components[${index}].changeType \u0628\u0627\u06CC\u062F \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.`);
1374
1638
  }
1639
+ if (entry.affectedComponentNames !== void 0 && (!Array.isArray(entry.affectedComponentNames) || !entry.affectedComponentNames.every((name) => typeof name === "string"))) {
1640
+ errors.push(`components[${index}].affectedComponentNames \u0628\u0627\u06CC\u062F \u0622\u0631\u0627\u06CC\u0647\u200C\u0627\u06CC \u0627\u0632 \u0631\u0634\u062A\u0647 \u0628\u0627\u0634\u062F.`);
1641
+ }
1375
1642
  });
1376
1643
  }
1377
1644
  return errors.length === 0 ? { valid: true } : { valid: false, errors };
@@ -1681,28 +1948,77 @@ async function impactCommand(name, options) {
1681
1948
  }
1682
1949
 
1683
1950
  // src/commands/init.ts
1951
+ import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
1684
1952
  import path10 from "path";
1685
-
1686
- // src/lib/prompt.ts
1687
- import * as readline from "readline/promises";
1688
- import { stdin, stdout } from "process";
1689
- async function confirm(question, defaultValue) {
1690
- const rl = readline.createInterface({ input: stdin, output: stdout });
1691
- const suffix = defaultValue ? "(Y/n)" : "(y/N)";
1692
- const raw = await rl.question(`${question} ${suffix} `);
1693
- rl.close();
1694
- const answer = raw.trim().toLowerCase();
1695
- if (!answer) return defaultValue;
1696
- 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
+ }
1697
1960
  }
1698
- async function ask(question, defaultValue) {
1699
- const rl = readline.createInterface({ input: stdin, output: stdout });
1700
- const raw = await rl.question(`${question} (${defaultValue}) `);
1701
- rl.close();
1702
- 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
+ );
1703
2021
  }
1704
-
1705
- // src/commands/init.ts
1706
2022
  async function initCommand(options) {
1707
2023
  const cwd = process.cwd();
1708
2024
  const base = defaultConfig();
@@ -1726,10 +2042,22 @@ async function initCommand(options) {
1726
2042
  };
1727
2043
  await writeConfig(config, cwd);
1728
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
+ }
1729
2057
  }
1730
2058
 
1731
2059
  // src/lib/migration-apply.ts
1732
- import { readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
2060
+ import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
1733
2061
  import path11 from "path";
1734
2062
 
1735
2063
  // src/lib/migration-strategy.ts
@@ -1807,7 +2135,7 @@ function looksBinary2(buffer) {
1807
2135
  async function readTextFileSafely2(target) {
1808
2136
  let buffer;
1809
2137
  try {
1810
- buffer = await readFile7(target);
2138
+ buffer = await readFile8(target);
1811
2139
  } catch (error) {
1812
2140
  if (error.code === "ENOENT") return void 0;
1813
2141
  throw error;
@@ -1919,12 +2247,12 @@ async function planOrApplyMigrations(params) {
1919
2247
  const written = [];
1920
2248
  try {
1921
2249
  for (const item of pending) {
1922
- await writeFile5(item.target, item.newContent, "utf8");
2250
+ await writeFile6(item.target, item.newContent, "utf8");
1923
2251
  written.push(item);
1924
2252
  }
1925
2253
  } catch (error) {
1926
2254
  for (const item of written) {
1927
- await writeFile5(item.target, item.originalContent, "utf8");
2255
+ await writeFile6(item.target, item.originalContent, "utf8");
1928
2256
  }
1929
2257
  return {
1930
2258
  name,
@@ -2535,7 +2863,7 @@ ${entry.migrationHint}`);
2535
2863
  }
2536
2864
 
2537
2865
  // src/lib/update.ts
2538
- 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";
2539
2867
  import path12 from "path";
2540
2868
  function safeFileIndexes(drift) {
2541
2869
  const indexes = [];
@@ -2564,14 +2892,14 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2564
2892
  newContent: registryFile.content,
2565
2893
  newHash: registryFile.hash ?? hashContent(registryFile.content),
2566
2894
  isNewFile,
2567
- originalContent: isNewFile ? void 0 : await readFile8(target, "utf8")
2895
+ originalContent: isNewFile ? void 0 : await readFile9(target, "utf8")
2568
2896
  });
2569
2897
  }
2570
2898
  const written = [];
2571
2899
  try {
2572
2900
  for (const item of pending) {
2573
- if (item.isNewFile) await mkdir4(path12.dirname(item.target), { recursive: true });
2574
- 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");
2575
2903
  written.push(item);
2576
2904
  }
2577
2905
  } catch (error) {
@@ -2579,7 +2907,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2579
2907
  if (item.isNewFile) {
2580
2908
  await rm2(item.target, { force: true });
2581
2909
  } else {
2582
- await writeFile6(item.target, item.originalContent, "utf8");
2910
+ await writeFile7(item.target, item.originalContent, "utf8");
2583
2911
  }
2584
2912
  }
2585
2913
  return { actions, writtenHashes: {}, rollbackError: error.message };
@@ -2867,11 +3195,18 @@ ${result.name}`);
2867
3195
  }
2868
3196
  }
2869
3197
 
3198
+ // src/lib/version.ts
3199
+ var CLI_VERSION = "0.3.0";
3200
+
2870
3201
  // src/index.ts
2871
3202
  var program = new Command();
2872
- 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");
2873
- 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);
2874
- 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);
2875
3210
  program.command("diff").description(
2876
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)"
2877
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.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "CLI رسمی برای نصب کامپوننت‌های دیزاین سیستم دیگ",
5
5
  "type": "module",
6
6
  "license": "MIT",