@metacells/mcellui-cli 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.js +567 -273
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -116,8 +116,8 @@ function validateConfig(config) {
|
|
|
116
116
|
return { success: true, data: result.data };
|
|
117
117
|
}
|
|
118
118
|
const errors = result.error.issues.map((issue) => {
|
|
119
|
-
const
|
|
120
|
-
return
|
|
119
|
+
const path12 = issue.path.join(".");
|
|
120
|
+
return path12 ? `${path12}: ${issue.message}` : issue.message;
|
|
121
121
|
});
|
|
122
122
|
return { success: false, errors };
|
|
123
123
|
}
|
|
@@ -212,27 +212,38 @@ async function detectProjectType(projectRoot) {
|
|
|
212
212
|
return "unknown";
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
+
var CONFIG_FILES = [
|
|
216
|
+
"mcellui.config.ts",
|
|
217
|
+
"mcellui.config.js",
|
|
218
|
+
"mcellui.config.json",
|
|
219
|
+
"nativeui.config.ts",
|
|
220
|
+
// Legacy
|
|
221
|
+
"nativeui.config.js",
|
|
222
|
+
// Legacy
|
|
223
|
+
"nativeui.config.json"
|
|
224
|
+
// Legacy
|
|
225
|
+
];
|
|
215
226
|
async function getConfig(projectRoot) {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
227
|
+
for (const fileName of CONFIG_FILES) {
|
|
228
|
+
const configPath = path.join(projectRoot, fileName);
|
|
229
|
+
if (await fs.pathExists(configPath)) {
|
|
230
|
+
if (fileName.endsWith(".ts")) {
|
|
231
|
+
return loadTsConfig(configPath);
|
|
232
|
+
} else if (fileName.endsWith(".js")) {
|
|
233
|
+
return loadJsConfig(configPath);
|
|
234
|
+
} else if (fileName.endsWith(".json")) {
|
|
235
|
+
try {
|
|
236
|
+
const rawConfig = await fs.readJson(configPath);
|
|
237
|
+
const validatedConfig = validateConfigOrThrow(rawConfig, configPath);
|
|
238
|
+
return resolveConfig(validatedConfig);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (error instanceof Error && error.message.includes("Invalid configuration")) {
|
|
241
|
+
console.error(chalk.red(error.message));
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
234
246
|
}
|
|
235
|
-
throw error;
|
|
236
247
|
}
|
|
237
248
|
}
|
|
238
249
|
return null;
|
|
@@ -294,7 +305,7 @@ async function detectPackageManager(projectRoot) {
|
|
|
294
305
|
}
|
|
295
306
|
|
|
296
307
|
// src/commands/init.ts
|
|
297
|
-
var initCommand = new Command().name("init").description("Initialize
|
|
308
|
+
var initCommand = new Command().name("init").description("Initialize mcellui in your project").option("-y, --yes", "Skip prompts and use defaults").option("--cwd <path>", "Working directory", process.cwd()).action(async (options) => {
|
|
298
309
|
const spinner = ora();
|
|
299
310
|
try {
|
|
300
311
|
const cwd = path2.resolve(options.cwd);
|
|
@@ -305,17 +316,24 @@ var initCommand = new Command().name("init").description("Initialize nativeui in
|
|
|
305
316
|
process.exit(1);
|
|
306
317
|
}
|
|
307
318
|
console.log();
|
|
308
|
-
console.log(chalk2.bold("Welcome to
|
|
319
|
+
console.log(chalk2.bold("Welcome to mcellui!"));
|
|
309
320
|
console.log(chalk2.dim("The copy-paste component library for Expo/React Native"));
|
|
310
321
|
console.log();
|
|
311
322
|
const projectType = await detectProjectType(projectRoot);
|
|
312
323
|
console.log(chalk2.dim(`Detected: ${projectType}`));
|
|
313
|
-
const configPath = path2.join(projectRoot, "
|
|
324
|
+
const configPath = path2.join(projectRoot, "mcellui.config.ts");
|
|
325
|
+
const legacyConfigPath = path2.join(projectRoot, "nativeui.config.ts");
|
|
314
326
|
if (await fs2.pathExists(configPath)) {
|
|
315
327
|
console.log(chalk2.yellow("Project already initialized."));
|
|
316
328
|
console.log(chalk2.dim(`Config found at: ${configPath}`));
|
|
317
329
|
return;
|
|
318
330
|
}
|
|
331
|
+
if (await fs2.pathExists(legacyConfigPath)) {
|
|
332
|
+
console.log(chalk2.yellow("Project already initialized with legacy config."));
|
|
333
|
+
console.log(chalk2.dim(`Config found at: ${legacyConfigPath}`));
|
|
334
|
+
console.log(chalk2.dim("Consider renaming to mcellui.config.ts"));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
319
337
|
let config = {
|
|
320
338
|
componentsPath: "./components/ui",
|
|
321
339
|
utilsPath: "./lib/utils",
|
|
@@ -454,14 +472,14 @@ export function cn(...inputs: StyleInput[]): Style {
|
|
|
454
472
|
);
|
|
455
473
|
spinner.succeed("Utilities installed");
|
|
456
474
|
console.log();
|
|
457
|
-
console.log(chalk2.green("Success!") + "
|
|
475
|
+
console.log(chalk2.green("Success!") + " mcellui initialized.");
|
|
458
476
|
console.log();
|
|
459
477
|
console.log("Next steps:");
|
|
460
478
|
console.log(chalk2.dim(" 1."), "Add your first component:");
|
|
461
|
-
console.log(chalk2.cyan(" npx
|
|
479
|
+
console.log(chalk2.cyan(" npx mcellui add button"));
|
|
462
480
|
console.log();
|
|
463
481
|
console.log(chalk2.dim(" 2."), "Browse available components:");
|
|
464
|
-
console.log(chalk2.cyan(" npx
|
|
482
|
+
console.log(chalk2.cyan(" npx mcellui list"));
|
|
465
483
|
console.log();
|
|
466
484
|
} catch (error) {
|
|
467
485
|
spinner.fail("Failed to initialize");
|
|
@@ -475,8 +493,8 @@ import { Command as Command2 } from "commander";
|
|
|
475
493
|
import chalk3 from "chalk";
|
|
476
494
|
import ora2 from "ora";
|
|
477
495
|
import prompts2 from "prompts";
|
|
478
|
-
import
|
|
479
|
-
import
|
|
496
|
+
import fs5 from "fs-extra";
|
|
497
|
+
import path5 from "path";
|
|
480
498
|
|
|
481
499
|
// src/utils/registry.ts
|
|
482
500
|
import fs3 from "fs-extra";
|
|
@@ -485,7 +503,8 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
485
503
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
486
504
|
var __dirname2 = path3.dirname(__filename3);
|
|
487
505
|
var DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/metacells-development/mcellui/main/packages/registry";
|
|
488
|
-
var
|
|
506
|
+
var hasEnvOverride = "MCELLUI_REGISTRY_URL" in process.env || "NATIVEUI_REGISTRY_URL" in process.env;
|
|
507
|
+
var REGISTRY_URL = hasEnvOverride ? process.env.MCELLUI_REGISTRY_URL || process.env.NATIVEUI_REGISTRY_URL || "" : DEFAULT_REGISTRY_URL;
|
|
489
508
|
function getLocalRegistryPath() {
|
|
490
509
|
return path3.resolve(__dirname2, "..", "..", "registry");
|
|
491
510
|
}
|
|
@@ -567,11 +586,172 @@ async function fetchRemoteComponent(item) {
|
|
|
567
586
|
};
|
|
568
587
|
}
|
|
569
588
|
|
|
589
|
+
// src/utils/dependencies.ts
|
|
590
|
+
function resolveDependencies(components, registry) {
|
|
591
|
+
const registryMap = /* @__PURE__ */ new Map();
|
|
592
|
+
for (const item of registry) {
|
|
593
|
+
registryMap.set(item.name, item);
|
|
594
|
+
}
|
|
595
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
596
|
+
const resolving = /* @__PURE__ */ new Set();
|
|
597
|
+
const resolvedOrder = [];
|
|
598
|
+
function resolve(name, path12) {
|
|
599
|
+
if (resolved.has(name)) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
if (resolving.has(name)) {
|
|
603
|
+
return [...path12, name];
|
|
604
|
+
}
|
|
605
|
+
const item = registryMap.get(name);
|
|
606
|
+
if (!item) {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
resolving.add(name);
|
|
610
|
+
const deps = item.registryDependencies || [];
|
|
611
|
+
for (const dep of deps) {
|
|
612
|
+
const circular = resolve(dep, [...path12, name]);
|
|
613
|
+
if (circular) {
|
|
614
|
+
return circular;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
resolving.delete(name);
|
|
618
|
+
resolved.add(name);
|
|
619
|
+
resolvedOrder.push(name);
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
for (const name of components) {
|
|
623
|
+
const circular = resolve(name, []);
|
|
624
|
+
if (circular) {
|
|
625
|
+
return {
|
|
626
|
+
resolved: [],
|
|
627
|
+
circular
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
resolved: resolvedOrder,
|
|
633
|
+
circular: null
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function formatCircularError(chain) {
|
|
637
|
+
return chain.join(" \u2192 ");
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/utils/installed.ts
|
|
641
|
+
import fs4 from "fs-extra";
|
|
642
|
+
import path4 from "path";
|
|
643
|
+
|
|
644
|
+
// src/utils/imports.ts
|
|
645
|
+
function transformToInstalled(code, config) {
|
|
646
|
+
if (!config) return code;
|
|
647
|
+
let result = code;
|
|
648
|
+
const utilsAlias = config.aliases?.utils || "@/lib/utils";
|
|
649
|
+
if (utilsAlias !== "@/lib/utils") {
|
|
650
|
+
result = result.replace(
|
|
651
|
+
/from ['"]@\/lib\/utils['"]/g,
|
|
652
|
+
`from '${utilsAlias}'`
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
function normalizeForComparison(content) {
|
|
658
|
+
return content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/utils/installed.ts
|
|
662
|
+
async function getInstalledFiles(componentsDir) {
|
|
663
|
+
if (!await fs4.pathExists(componentsDir)) {
|
|
664
|
+
return [];
|
|
665
|
+
}
|
|
666
|
+
const files = await fs4.readdir(componentsDir);
|
|
667
|
+
const componentFiles = [];
|
|
668
|
+
for (const file of files) {
|
|
669
|
+
if (file.endsWith(".tsx") || file.endsWith(".ts")) {
|
|
670
|
+
if (file === "index.ts" || file === "index.tsx") {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
componentFiles.push(path4.join(componentsDir, file));
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return componentFiles.sort();
|
|
677
|
+
}
|
|
678
|
+
async function getInstallStatus(installedFiles, registry, config) {
|
|
679
|
+
const results = [];
|
|
680
|
+
const registryMap = /* @__PURE__ */ new Map();
|
|
681
|
+
for (const item of registry) {
|
|
682
|
+
for (const file of item.files) {
|
|
683
|
+
const fileName = path4.basename(file);
|
|
684
|
+
registryMap.set(fileName, item);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
for (const localFile of installedFiles) {
|
|
688
|
+
const fileName = path4.basename(localFile);
|
|
689
|
+
const componentName = fileName.replace(/\.tsx?$/, "");
|
|
690
|
+
const registryItem = registryMap.get(fileName);
|
|
691
|
+
if (!registryItem) {
|
|
692
|
+
results.push({
|
|
693
|
+
name: componentName,
|
|
694
|
+
fileName,
|
|
695
|
+
status: "local-only",
|
|
696
|
+
localPath: localFile
|
|
697
|
+
});
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
const component = await fetchComponent(registryItem.name);
|
|
702
|
+
if (!component) {
|
|
703
|
+
results.push({
|
|
704
|
+
name: registryItem.name,
|
|
705
|
+
fileName,
|
|
706
|
+
status: "modified",
|
|
707
|
+
// Assume modified if we can't verify
|
|
708
|
+
localPath: localFile
|
|
709
|
+
});
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const registryFile = component.files.find((f) => f.name === fileName);
|
|
713
|
+
if (!registryFile) {
|
|
714
|
+
results.push({
|
|
715
|
+
name: registryItem.name,
|
|
716
|
+
fileName,
|
|
717
|
+
status: "modified",
|
|
718
|
+
localPath: localFile
|
|
719
|
+
});
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
const localContent = await fs4.readFile(localFile, "utf-8");
|
|
723
|
+
const normalizedLocal = normalizeForComparison(localContent);
|
|
724
|
+
const normalizedRegistry = normalizeForComparison(registryFile.content);
|
|
725
|
+
results.push({
|
|
726
|
+
name: registryItem.name,
|
|
727
|
+
fileName,
|
|
728
|
+
status: normalizedLocal === normalizedRegistry ? "identical" : "modified",
|
|
729
|
+
localPath: localFile
|
|
730
|
+
});
|
|
731
|
+
} catch {
|
|
732
|
+
results.push({
|
|
733
|
+
name: registryItem.name,
|
|
734
|
+
fileName,
|
|
735
|
+
status: "modified",
|
|
736
|
+
// Assume modified on error
|
|
737
|
+
localPath: localFile
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return results;
|
|
742
|
+
}
|
|
743
|
+
function getInstalledNames(installedFiles) {
|
|
744
|
+
return installedFiles.map((file) => {
|
|
745
|
+
const fileName = path4.basename(file);
|
|
746
|
+
return fileName.replace(/\.tsx?$/, "");
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
|
|
570
750
|
// src/commands/add.ts
|
|
571
751
|
var addCommand = new Command2().name("add").description("Add a component to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip confirmation").option("-o, --overwrite", "Overwrite existing files").option("--cwd <path>", "Working directory", process.cwd()).action(async (components, options) => {
|
|
572
752
|
const spinner = ora2();
|
|
573
753
|
try {
|
|
574
|
-
const cwd =
|
|
754
|
+
const cwd = path5.resolve(options.cwd);
|
|
575
755
|
const projectRoot = await getProjectRoot(cwd);
|
|
576
756
|
if (!projectRoot) {
|
|
577
757
|
console.log(chalk3.red("Could not find a valid project."));
|
|
@@ -585,8 +765,8 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
585
765
|
process.exit(1);
|
|
586
766
|
}
|
|
587
767
|
if (!components.length) {
|
|
588
|
-
const
|
|
589
|
-
if (!
|
|
768
|
+
const registry2 = await getRegistry();
|
|
769
|
+
if (!registry2.length) {
|
|
590
770
|
console.log(chalk3.red("No components found in registry."));
|
|
591
771
|
process.exit(1);
|
|
592
772
|
}
|
|
@@ -594,7 +774,7 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
594
774
|
type: "multiselect",
|
|
595
775
|
name: "selected",
|
|
596
776
|
message: "Which components would you like to add?",
|
|
597
|
-
choices:
|
|
777
|
+
choices: registry2.map((item) => ({
|
|
598
778
|
title: `${item.name} ${chalk3.dim(`(${item.status})`)}`,
|
|
599
779
|
value: item.name,
|
|
600
780
|
description: item.description
|
|
@@ -607,13 +787,51 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
607
787
|
}
|
|
608
788
|
components = selected;
|
|
609
789
|
}
|
|
790
|
+
spinner.start("Resolving dependencies...");
|
|
791
|
+
const registry = await getRegistry();
|
|
792
|
+
const { resolved, circular } = resolveDependencies(components, registry);
|
|
793
|
+
if (circular) {
|
|
794
|
+
spinner.fail("Circular dependency detected");
|
|
795
|
+
console.log(chalk3.red(` ${formatCircularError(circular)}`));
|
|
796
|
+
process.exit(1);
|
|
797
|
+
}
|
|
798
|
+
spinner.stop();
|
|
799
|
+
const componentsDir = path5.join(projectRoot, config.componentsPath);
|
|
800
|
+
const installedFiles = await getInstalledFiles(componentsDir);
|
|
801
|
+
const installedNames = new Set(getInstalledNames(installedFiles));
|
|
802
|
+
const toInstall = options.overwrite ? resolved : resolved.filter((name) => !installedNames.has(name));
|
|
803
|
+
if (toInstall.length === 0) {
|
|
804
|
+
console.log(chalk3.yellow("All components are already installed."));
|
|
805
|
+
console.log(chalk3.dim("Use --overwrite to reinstall."));
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const requested = new Set(components);
|
|
809
|
+
const dependencies = toInstall.filter((name) => !requested.has(name));
|
|
610
810
|
console.log();
|
|
611
811
|
console.log(chalk3.bold("Adding components:"));
|
|
612
|
-
|
|
812
|
+
for (const name of toInstall) {
|
|
813
|
+
if (requested.has(name)) {
|
|
814
|
+
console.log(chalk3.dim(` - ${name}`));
|
|
815
|
+
} else {
|
|
816
|
+
console.log(chalk3.dim(` - ${name} ${chalk3.cyan("(dependency)")}`));
|
|
817
|
+
}
|
|
818
|
+
}
|
|
613
819
|
console.log();
|
|
820
|
+
if (dependencies.length > 0 && !options.yes) {
|
|
821
|
+
const { confirm } = await prompts2({
|
|
822
|
+
type: "confirm",
|
|
823
|
+
name: "confirm",
|
|
824
|
+
message: `Add ${dependencies.length} additional dependencies?`,
|
|
825
|
+
initial: true
|
|
826
|
+
});
|
|
827
|
+
if (!confirm) {
|
|
828
|
+
console.log(chalk3.dim("Cancelled."));
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
614
832
|
const allDependencies = [];
|
|
615
833
|
const allDevDependencies = [];
|
|
616
|
-
for (const componentName of
|
|
834
|
+
for (const componentName of toInstall) {
|
|
617
835
|
spinner.start(`Fetching ${componentName}...`);
|
|
618
836
|
try {
|
|
619
837
|
const component = await fetchComponent(componentName);
|
|
@@ -621,16 +839,16 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
621
839
|
spinner.fail(`Component "${componentName}" not found`);
|
|
622
840
|
continue;
|
|
623
841
|
}
|
|
624
|
-
const targetDir =
|
|
842
|
+
const targetDir = path5.join(projectRoot, config.componentsPath);
|
|
625
843
|
for (const file of component.files) {
|
|
626
|
-
const targetPath =
|
|
627
|
-
if (await
|
|
844
|
+
const targetPath = path5.join(targetDir, file.name);
|
|
845
|
+
if (await fs5.pathExists(targetPath) && !options.overwrite) {
|
|
628
846
|
spinner.warn(`${file.name} already exists (use --overwrite)`);
|
|
629
847
|
continue;
|
|
630
848
|
}
|
|
631
|
-
await
|
|
632
|
-
const transformedContent =
|
|
633
|
-
await
|
|
849
|
+
await fs5.ensureDir(targetDir);
|
|
850
|
+
const transformedContent = transformToInstalled(file.content, config);
|
|
851
|
+
await fs5.writeFile(targetPath, transformedContent);
|
|
634
852
|
}
|
|
635
853
|
spinner.succeed(`Added ${componentName}`);
|
|
636
854
|
if (component.dependencies?.length) {
|
|
@@ -639,11 +857,6 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
639
857
|
if (component.devDependencies?.length) {
|
|
640
858
|
allDevDependencies.push(...component.devDependencies);
|
|
641
859
|
}
|
|
642
|
-
if (component.registryDependencies?.length) {
|
|
643
|
-
console.log(
|
|
644
|
-
chalk3.dim(` Requires: ${component.registryDependencies.join(", ")}`)
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
860
|
} catch (error) {
|
|
648
861
|
spinner.fail(`Failed to add ${componentName}`);
|
|
649
862
|
console.error(chalk3.dim(String(error)));
|
|
@@ -669,24 +882,20 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
669
882
|
process.exit(1);
|
|
670
883
|
}
|
|
671
884
|
});
|
|
672
|
-
function transformImports(code, config) {
|
|
673
|
-
let transformed = code;
|
|
674
|
-
const utilsAlias = config.aliases?.utils || "@/lib/utils";
|
|
675
|
-
if (utilsAlias === "@/lib/utils") {
|
|
676
|
-
return transformed;
|
|
677
|
-
}
|
|
678
|
-
transformed = transformed.replace(
|
|
679
|
-
/from ['"]@\/lib\/utils['"]/g,
|
|
680
|
-
`from '${utilsAlias}'`
|
|
681
|
-
);
|
|
682
|
-
return transformed;
|
|
683
|
-
}
|
|
684
885
|
|
|
685
886
|
// src/commands/list.ts
|
|
686
887
|
import { Command as Command3 } from "commander";
|
|
687
888
|
import chalk4 from "chalk";
|
|
688
889
|
import ora3 from "ora";
|
|
689
|
-
|
|
890
|
+
import path6 from "path";
|
|
891
|
+
var listCommand = new Command3().name("list").description("List available or installed components").option("-c, --category <category>", "Filter by category").option("-i, --installed", "Show installed components and their sync status").option("--cwd <path>", "Working directory", process.cwd()).action(async (options) => {
|
|
892
|
+
if (options.installed) {
|
|
893
|
+
await listInstalledComponents(options);
|
|
894
|
+
} else {
|
|
895
|
+
await listAvailableComponents(options);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
async function listAvailableComponents(options) {
|
|
690
899
|
const spinner = ora3("Fetching components...").start();
|
|
691
900
|
try {
|
|
692
901
|
const registry = await getRegistry();
|
|
@@ -714,27 +923,120 @@ var listCommand = new Command3().name("list").description("List available compon
|
|
|
714
923
|
}
|
|
715
924
|
console.log();
|
|
716
925
|
}
|
|
717
|
-
console.log(chalk4.dim("Add a component: npx
|
|
926
|
+
console.log(chalk4.dim("Add a component: npx mcellui add <component>"));
|
|
718
927
|
console.log();
|
|
719
928
|
} catch (error) {
|
|
720
929
|
spinner.fail("Failed to fetch components");
|
|
721
930
|
console.error(error);
|
|
722
931
|
process.exit(1);
|
|
723
932
|
}
|
|
724
|
-
}
|
|
933
|
+
}
|
|
934
|
+
async function listInstalledComponents(options) {
|
|
935
|
+
const spinner = ora3();
|
|
936
|
+
try {
|
|
937
|
+
const cwd = path6.resolve(options.cwd || process.cwd());
|
|
938
|
+
const projectRoot = await getProjectRoot(cwd);
|
|
939
|
+
if (!projectRoot) {
|
|
940
|
+
console.log(chalk4.red("Could not find a valid project."));
|
|
941
|
+
console.log(chalk4.dim("Run `npx mcellui init` first."));
|
|
942
|
+
process.exit(1);
|
|
943
|
+
}
|
|
944
|
+
const config = await getConfig(projectRoot);
|
|
945
|
+
if (!config) {
|
|
946
|
+
console.log(chalk4.red("Project not initialized."));
|
|
947
|
+
console.log(chalk4.dim("Run `npx mcellui init` first."));
|
|
948
|
+
process.exit(1);
|
|
949
|
+
}
|
|
950
|
+
spinner.start("Scanning installed components...");
|
|
951
|
+
const componentsDir = path6.join(projectRoot, config.componentsPath);
|
|
952
|
+
const installedFiles = await getInstalledFiles(componentsDir);
|
|
953
|
+
if (installedFiles.length === 0) {
|
|
954
|
+
spinner.info("No components installed yet.");
|
|
955
|
+
console.log(chalk4.dim("\nAdd components with: npx mcellui add <component>"));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
spinner.text = "Fetching registry...";
|
|
959
|
+
const registry = await getRegistry();
|
|
960
|
+
spinner.text = "Comparing components...";
|
|
961
|
+
const installed = await getInstallStatus(installedFiles, registry, config);
|
|
962
|
+
spinner.stop();
|
|
963
|
+
const identical = installed.filter((c) => c.status === "identical");
|
|
964
|
+
const modified = installed.filter((c) => c.status === "modified");
|
|
965
|
+
const localOnly = installed.filter((c) => c.status === "local-only");
|
|
966
|
+
const installedNames = new Set(installed.map((c) => c.name));
|
|
967
|
+
const notInstalled = registry.filter((item) => !installedNames.has(item.name));
|
|
968
|
+
console.log();
|
|
969
|
+
console.log(chalk4.bold(`Installed Components (${installed.length})`));
|
|
970
|
+
console.log();
|
|
971
|
+
const categories = /* @__PURE__ */ new Map();
|
|
972
|
+
for (const comp of installed) {
|
|
973
|
+
const registryItem = registry.find((r) => r.name === comp.name);
|
|
974
|
+
const category = registryItem?.category || "Custom";
|
|
975
|
+
if (!categories.has(category)) {
|
|
976
|
+
categories.set(category, []);
|
|
977
|
+
}
|
|
978
|
+
categories.get(category).push(comp);
|
|
979
|
+
}
|
|
980
|
+
for (const [category, components] of categories) {
|
|
981
|
+
console.log(chalk4.cyan.bold(category));
|
|
982
|
+
for (const comp of components) {
|
|
983
|
+
let statusIcon;
|
|
984
|
+
let statusText;
|
|
985
|
+
switch (comp.status) {
|
|
986
|
+
case "identical":
|
|
987
|
+
statusIcon = chalk4.green("\u2713");
|
|
988
|
+
statusText = chalk4.dim("(up to date)");
|
|
989
|
+
break;
|
|
990
|
+
case "modified":
|
|
991
|
+
statusIcon = chalk4.yellow("\u26A0");
|
|
992
|
+
statusText = chalk4.yellow("(modified locally)");
|
|
993
|
+
break;
|
|
994
|
+
case "local-only":
|
|
995
|
+
statusIcon = chalk4.blue("?");
|
|
996
|
+
statusText = chalk4.dim("(custom component)");
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
console.log(` ${statusIcon} ${chalk4.white(comp.name)} ${statusText}`);
|
|
1000
|
+
}
|
|
1001
|
+
console.log();
|
|
1002
|
+
}
|
|
1003
|
+
if (notInstalled.length > 0) {
|
|
1004
|
+
console.log(chalk4.dim("Not Installed"));
|
|
1005
|
+
const notInstalledNames = notInstalled.map((c) => c.name).slice(0, 10);
|
|
1006
|
+
const remaining = notInstalled.length - 10;
|
|
1007
|
+
console.log(chalk4.dim(` ${notInstalledNames.join(", ")}${remaining > 0 ? `, ... +${remaining} more` : ""}`));
|
|
1008
|
+
console.log();
|
|
1009
|
+
}
|
|
1010
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
1011
|
+
const parts = [];
|
|
1012
|
+
if (identical.length > 0) parts.push(chalk4.green(`${identical.length} up to date`));
|
|
1013
|
+
if (modified.length > 0) parts.push(chalk4.yellow(`${modified.length} modified`));
|
|
1014
|
+
if (localOnly.length > 0) parts.push(chalk4.blue(`${localOnly.length} custom`));
|
|
1015
|
+
console.log(parts.join(" \u2022 "));
|
|
1016
|
+
if (modified.length > 0) {
|
|
1017
|
+
console.log();
|
|
1018
|
+
console.log(chalk4.dim("Sync modified components:"));
|
|
1019
|
+
console.log(chalk4.cyan(` npx mcellui diff`));
|
|
1020
|
+
}
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
spinner.fail("Failed to list installed components");
|
|
1023
|
+
console.error(error);
|
|
1024
|
+
process.exit(1);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
725
1027
|
|
|
726
1028
|
// src/commands/doctor.ts
|
|
727
1029
|
import { Command as Command4 } from "commander";
|
|
728
1030
|
import chalk5 from "chalk";
|
|
729
|
-
import
|
|
730
|
-
import
|
|
1031
|
+
import fs6 from "fs-extra";
|
|
1032
|
+
import path7 from "path";
|
|
731
1033
|
var REQUIRED_PEER_DEPS = [
|
|
732
1034
|
{ name: "react-native-reanimated", minVersion: "3.0.0" },
|
|
733
1035
|
{ name: "react-native-gesture-handler", minVersion: "2.0.0" },
|
|
734
1036
|
{ name: "react-native-safe-area-context", minVersion: "4.0.0" }
|
|
735
1037
|
];
|
|
736
1038
|
var RECOMMENDED_DEPS = [
|
|
737
|
-
{ name: "@
|
|
1039
|
+
{ name: "@metacells/mcellui-core", reason: "Theme system and utilities" }
|
|
738
1040
|
];
|
|
739
1041
|
function parseVersion(version) {
|
|
740
1042
|
const cleaned = version.replace(/^[\^~>=<]+/, "").replace(/^workspace:\*?/, "");
|
|
@@ -779,7 +1081,7 @@ async function checkProjectType(projectRoot) {
|
|
|
779
1081
|
name: "Project Type",
|
|
780
1082
|
status: "fail",
|
|
781
1083
|
message: "Not an Expo or React Native project",
|
|
782
|
-
fix: "
|
|
1084
|
+
fix: "mcellui requires Expo or React Native"
|
|
783
1085
|
};
|
|
784
1086
|
}
|
|
785
1087
|
return {
|
|
@@ -790,34 +1092,50 @@ async function checkProjectType(projectRoot) {
|
|
|
790
1092
|
}
|
|
791
1093
|
async function checkInitialized(projectRoot) {
|
|
792
1094
|
const configFiles = [
|
|
1095
|
+
"mcellui.config.ts",
|
|
1096
|
+
"mcellui.config.js",
|
|
1097
|
+
"mcellui.config.json",
|
|
793
1098
|
"nativeui.config.ts",
|
|
1099
|
+
// Legacy
|
|
794
1100
|
"nativeui.config.js",
|
|
1101
|
+
// Legacy
|
|
795
1102
|
"nativeui.config.json"
|
|
1103
|
+
// Legacy
|
|
796
1104
|
];
|
|
797
1105
|
let foundConfig = null;
|
|
1106
|
+
let isLegacy = false;
|
|
798
1107
|
for (const file of configFiles) {
|
|
799
|
-
if (await
|
|
1108
|
+
if (await fs6.pathExists(path7.join(projectRoot, file))) {
|
|
800
1109
|
foundConfig = file;
|
|
1110
|
+
isLegacy = file.startsWith("nativeui.");
|
|
801
1111
|
break;
|
|
802
1112
|
}
|
|
803
1113
|
}
|
|
804
1114
|
if (!foundConfig) {
|
|
805
1115
|
return {
|
|
806
|
-
name: "
|
|
1116
|
+
name: "mcellui Config",
|
|
807
1117
|
status: "fail",
|
|
808
|
-
message: "
|
|
809
|
-
fix: "Run: npx
|
|
1118
|
+
message: "mcellui.config.ts not found",
|
|
1119
|
+
fix: "Run: npx mcellui init"
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
if (isLegacy) {
|
|
1123
|
+
return {
|
|
1124
|
+
name: "mcellui Config",
|
|
1125
|
+
status: "warn",
|
|
1126
|
+
message: `Found legacy config: ${foundConfig}`,
|
|
1127
|
+
fix: "Consider renaming to mcellui.config.ts"
|
|
810
1128
|
};
|
|
811
1129
|
}
|
|
812
1130
|
try {
|
|
813
|
-
const configPath =
|
|
814
|
-
const content = await
|
|
1131
|
+
const configPath = path7.join(projectRoot, foundConfig);
|
|
1132
|
+
const content = await fs6.readFile(configPath, "utf-8");
|
|
815
1133
|
if (foundConfig.endsWith(".ts") || foundConfig.endsWith(".js")) {
|
|
816
1134
|
const hasDefineConfig = content.includes("defineConfig");
|
|
817
1135
|
const hasExport = content.includes("export default");
|
|
818
1136
|
if (!hasExport) {
|
|
819
1137
|
return {
|
|
820
|
-
name: "
|
|
1138
|
+
name: "mcellui Config",
|
|
821
1139
|
status: "warn",
|
|
822
1140
|
message: "Config file missing default export",
|
|
823
1141
|
fix: "Add: export default defineConfig({ ... })"
|
|
@@ -825,7 +1143,7 @@ async function checkInitialized(projectRoot) {
|
|
|
825
1143
|
}
|
|
826
1144
|
if (!hasDefineConfig) {
|
|
827
1145
|
return {
|
|
828
|
-
name: "
|
|
1146
|
+
name: "mcellui Config",
|
|
829
1147
|
status: "warn",
|
|
830
1148
|
message: "Config not using defineConfig helper",
|
|
831
1149
|
fix: "Wrap config with: defineConfig({ ... })"
|
|
@@ -837,21 +1155,21 @@ async function checkInitialized(projectRoot) {
|
|
|
837
1155
|
JSON.parse(content);
|
|
838
1156
|
} catch {
|
|
839
1157
|
return {
|
|
840
|
-
name: "
|
|
1158
|
+
name: "mcellui Config",
|
|
841
1159
|
status: "fail",
|
|
842
1160
|
message: "Invalid JSON in config file",
|
|
843
|
-
fix: "Check JSON syntax in
|
|
1161
|
+
fix: "Check JSON syntax in mcellui.config.json"
|
|
844
1162
|
};
|
|
845
1163
|
}
|
|
846
1164
|
}
|
|
847
1165
|
return {
|
|
848
|
-
name: "
|
|
1166
|
+
name: "mcellui Config",
|
|
849
1167
|
status: "pass",
|
|
850
1168
|
message: `Found ${foundConfig}`
|
|
851
1169
|
};
|
|
852
1170
|
} catch (error) {
|
|
853
1171
|
return {
|
|
854
|
-
name: "
|
|
1172
|
+
name: "mcellui Config",
|
|
855
1173
|
status: "fail",
|
|
856
1174
|
message: "Could not read config file",
|
|
857
1175
|
fix: error instanceof Error ? error.message : "Check file permissions"
|
|
@@ -863,12 +1181,19 @@ async function checkPaths(projectRoot) {
|
|
|
863
1181
|
const defaultUtilsPath = "./lib/utils";
|
|
864
1182
|
let componentsPathValue = defaultComponentsPath;
|
|
865
1183
|
let utilsPathValue = defaultUtilsPath;
|
|
866
|
-
const configFiles = [
|
|
1184
|
+
const configFiles = [
|
|
1185
|
+
"mcellui.config.ts",
|
|
1186
|
+
"mcellui.config.js",
|
|
1187
|
+
"mcellui.config.json",
|
|
1188
|
+
"nativeui.config.ts",
|
|
1189
|
+
"nativeui.config.js",
|
|
1190
|
+
"nativeui.config.json"
|
|
1191
|
+
];
|
|
867
1192
|
for (const file of configFiles) {
|
|
868
|
-
const configPath =
|
|
869
|
-
if (await
|
|
1193
|
+
const configPath = path7.join(projectRoot, file);
|
|
1194
|
+
if (await fs6.pathExists(configPath)) {
|
|
870
1195
|
try {
|
|
871
|
-
const content = await
|
|
1196
|
+
const content = await fs6.readFile(configPath, "utf-8");
|
|
872
1197
|
const componentsMatch = content.match(/componentsPath:\s*['"]([^'"]+)['"]/);
|
|
873
1198
|
const utilsMatch = content.match(/utilsPath:\s*['"]([^'"]+)['"]/);
|
|
874
1199
|
if (componentsMatch) componentsPathValue = componentsMatch[1];
|
|
@@ -878,16 +1203,16 @@ async function checkPaths(projectRoot) {
|
|
|
878
1203
|
break;
|
|
879
1204
|
}
|
|
880
1205
|
}
|
|
881
|
-
const componentsPath =
|
|
882
|
-
const utilsPath =
|
|
883
|
-
const componentsExist = await
|
|
884
|
-
const utilsExist = await
|
|
1206
|
+
const componentsPath = path7.join(projectRoot, componentsPathValue);
|
|
1207
|
+
const utilsPath = path7.join(projectRoot, utilsPathValue);
|
|
1208
|
+
const componentsExist = await fs6.pathExists(componentsPath);
|
|
1209
|
+
const utilsExist = await fs6.pathExists(utilsPath);
|
|
885
1210
|
if (!componentsExist && !utilsExist) {
|
|
886
1211
|
return {
|
|
887
1212
|
name: "Component Paths",
|
|
888
1213
|
status: "warn",
|
|
889
1214
|
message: "Component and utils directories not created yet",
|
|
890
|
-
fix: `Add a component: npx
|
|
1215
|
+
fix: `Add a component: npx mcellui add button`
|
|
891
1216
|
};
|
|
892
1217
|
}
|
|
893
1218
|
if (!componentsExist) {
|
|
@@ -895,7 +1220,7 @@ async function checkPaths(projectRoot) {
|
|
|
895
1220
|
name: "Component Paths",
|
|
896
1221
|
status: "warn",
|
|
897
1222
|
message: `Components directory not found: ${componentsPathValue}`,
|
|
898
|
-
fix: "Add a component to create it: npx
|
|
1223
|
+
fix: "Add a component to create it: npx mcellui add button"
|
|
899
1224
|
};
|
|
900
1225
|
}
|
|
901
1226
|
return {
|
|
@@ -978,10 +1303,10 @@ async function checkBabelConfig(projectRoot) {
|
|
|
978
1303
|
let babelConfigPath = null;
|
|
979
1304
|
let babelContent = null;
|
|
980
1305
|
for (const configFile of babelConfigPaths) {
|
|
981
|
-
const fullPath =
|
|
982
|
-
if (await
|
|
1306
|
+
const fullPath = path7.join(projectRoot, configFile);
|
|
1307
|
+
if (await fs6.pathExists(fullPath)) {
|
|
983
1308
|
babelConfigPath = configFile;
|
|
984
|
-
babelContent = await
|
|
1309
|
+
babelContent = await fs6.readFile(fullPath, "utf-8");
|
|
985
1310
|
break;
|
|
986
1311
|
}
|
|
987
1312
|
}
|
|
@@ -1009,8 +1334,8 @@ async function checkBabelConfig(projectRoot) {
|
|
|
1009
1334
|
};
|
|
1010
1335
|
}
|
|
1011
1336
|
async function checkTypeScript(projectRoot) {
|
|
1012
|
-
const tsconfigPath =
|
|
1013
|
-
if (!await
|
|
1337
|
+
const tsconfigPath = path7.join(projectRoot, "tsconfig.json");
|
|
1338
|
+
if (!await fs6.pathExists(tsconfigPath)) {
|
|
1014
1339
|
return {
|
|
1015
1340
|
name: "TypeScript",
|
|
1016
1341
|
status: "warn",
|
|
@@ -1019,7 +1344,7 @@ async function checkTypeScript(projectRoot) {
|
|
|
1019
1344
|
};
|
|
1020
1345
|
}
|
|
1021
1346
|
try {
|
|
1022
|
-
const tsconfig = await
|
|
1347
|
+
const tsconfig = await fs6.readJson(tsconfigPath);
|
|
1023
1348
|
const hasPathAliases = tsconfig.compilerOptions?.paths;
|
|
1024
1349
|
if (!hasPathAliases) {
|
|
1025
1350
|
return {
|
|
@@ -1083,7 +1408,7 @@ async function checkExpoGo(projectRoot) {
|
|
|
1083
1408
|
}
|
|
1084
1409
|
function printReport(report) {
|
|
1085
1410
|
console.log();
|
|
1086
|
-
console.log(chalk5.bold("
|
|
1411
|
+
console.log(chalk5.bold("mcellui Doctor"));
|
|
1087
1412
|
console.log(chalk5.dim("Checking your project setup..."));
|
|
1088
1413
|
console.log();
|
|
1089
1414
|
console.log(chalk5.dim("Project:"), report.projectRoot);
|
|
@@ -1125,7 +1450,7 @@ function printReport(report) {
|
|
|
1125
1450
|
}
|
|
1126
1451
|
var doctorCommand = new Command4().name("doctor").description("Check project setup and diagnose common issues").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output results as JSON").action(async (options) => {
|
|
1127
1452
|
try {
|
|
1128
|
-
const cwd =
|
|
1453
|
+
const cwd = path7.resolve(options.cwd);
|
|
1129
1454
|
const projectRoot = await getProjectRoot(cwd);
|
|
1130
1455
|
if (!projectRoot) {
|
|
1131
1456
|
console.log(chalk5.red("Could not find a valid project."));
|
|
@@ -1177,31 +1502,31 @@ var doctorCommand = new Command4().name("doctor").description("Check project set
|
|
|
1177
1502
|
import { Command as Command5 } from "commander";
|
|
1178
1503
|
import chalk6 from "chalk";
|
|
1179
1504
|
import ora4 from "ora";
|
|
1180
|
-
import
|
|
1181
|
-
import
|
|
1182
|
-
import
|
|
1183
|
-
var diffCommand = new Command5().name("diff").description("
|
|
1505
|
+
import fs7 from "fs-extra";
|
|
1506
|
+
import path8 from "path";
|
|
1507
|
+
import * as Diff from "diff";
|
|
1508
|
+
var diffCommand = new Command5().name("diff").description("Compare locally installed components against the registry source").argument("[components...]", "Component names to diff (optional, diffs all if omitted)").option("--cwd <path>", "Working directory", process.cwd()).option("--list", "Only list components with differences").option("--json", "Output as JSON").action(async (components, options) => {
|
|
1184
1509
|
const spinner = ora4();
|
|
1185
1510
|
try {
|
|
1186
|
-
const cwd =
|
|
1511
|
+
const cwd = path8.resolve(options.cwd);
|
|
1187
1512
|
const projectRoot = await getProjectRoot(cwd);
|
|
1188
1513
|
if (!projectRoot) {
|
|
1189
1514
|
console.log(chalk6.red("Could not find a valid project."));
|
|
1190
|
-
console.log(chalk6.dim("Run `npx
|
|
1515
|
+
console.log(chalk6.dim("Run `npx mcellui init` first."));
|
|
1191
1516
|
process.exit(1);
|
|
1192
1517
|
}
|
|
1193
1518
|
const config = await getConfig(projectRoot);
|
|
1194
1519
|
if (!config) {
|
|
1195
1520
|
console.log(chalk6.red("Project not initialized."));
|
|
1196
|
-
console.log(chalk6.dim("Run `npx
|
|
1521
|
+
console.log(chalk6.dim("Run `npx mcellui init` first."));
|
|
1197
1522
|
process.exit(1);
|
|
1198
1523
|
}
|
|
1199
1524
|
spinner.start("Scanning installed components...");
|
|
1200
|
-
const componentsDir =
|
|
1201
|
-
const installedFiles = await
|
|
1525
|
+
const componentsDir = path8.join(projectRoot, config.componentsPath);
|
|
1526
|
+
const installedFiles = await getInstalledFiles(componentsDir);
|
|
1202
1527
|
if (installedFiles.length === 0) {
|
|
1203
1528
|
spinner.info("No components installed yet.");
|
|
1204
|
-
console.log(chalk6.dim("\nAdd components with: npx
|
|
1529
|
+
console.log(chalk6.dim("\nAdd components with: npx mcellui add <component>"));
|
|
1205
1530
|
return;
|
|
1206
1531
|
}
|
|
1207
1532
|
spinner.text = "Fetching registry...";
|
|
@@ -1209,18 +1534,34 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1209
1534
|
const registryMap = /* @__PURE__ */ new Map();
|
|
1210
1535
|
for (const item of registry) {
|
|
1211
1536
|
for (const file of item.files) {
|
|
1212
|
-
const fileName =
|
|
1537
|
+
const fileName = path8.basename(file);
|
|
1213
1538
|
registryMap.set(fileName, item);
|
|
1214
1539
|
}
|
|
1215
1540
|
}
|
|
1541
|
+
let filesToCompare = installedFiles;
|
|
1542
|
+
if (components.length > 0) {
|
|
1543
|
+
const componentFileNames = components.map((c) => {
|
|
1544
|
+
return c.endsWith(".tsx") ? c : `${c}.tsx`;
|
|
1545
|
+
});
|
|
1546
|
+
filesToCompare = installedFiles.filter((file) => {
|
|
1547
|
+
const fileName = path8.basename(file);
|
|
1548
|
+
return componentFileNames.includes(fileName);
|
|
1549
|
+
});
|
|
1550
|
+
if (filesToCompare.length === 0) {
|
|
1551
|
+
spinner.fail(`None of the specified components found: ${components.join(", ")}`);
|
|
1552
|
+
process.exit(1);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1216
1555
|
spinner.text = "Comparing components...";
|
|
1217
1556
|
const results = [];
|
|
1218
|
-
for (const localFile of
|
|
1219
|
-
const fileName =
|
|
1557
|
+
for (const localFile of filesToCompare) {
|
|
1558
|
+
const fileName = path8.basename(localFile);
|
|
1559
|
+
const componentName = fileName.replace(/\.tsx?$/, "");
|
|
1220
1560
|
const registryItem = registryMap.get(fileName);
|
|
1221
1561
|
if (!registryItem) {
|
|
1222
1562
|
results.push({
|
|
1223
|
-
name:
|
|
1563
|
+
name: componentName,
|
|
1564
|
+
fileName,
|
|
1224
1565
|
status: "local-only",
|
|
1225
1566
|
localFile
|
|
1226
1567
|
});
|
|
@@ -1231,6 +1572,7 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1231
1572
|
if (!component) {
|
|
1232
1573
|
results.push({
|
|
1233
1574
|
name: registryItem.name,
|
|
1575
|
+
fileName,
|
|
1234
1576
|
status: "error",
|
|
1235
1577
|
error: "Failed to fetch from registry"
|
|
1236
1578
|
});
|
|
@@ -1240,31 +1582,42 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1240
1582
|
if (!registryFile) {
|
|
1241
1583
|
results.push({
|
|
1242
1584
|
name: registryItem.name,
|
|
1585
|
+
fileName,
|
|
1243
1586
|
status: "error",
|
|
1244
1587
|
error: "File not found in registry"
|
|
1245
1588
|
});
|
|
1246
1589
|
continue;
|
|
1247
1590
|
}
|
|
1248
|
-
const localContent = await
|
|
1249
|
-
const
|
|
1250
|
-
const
|
|
1251
|
-
|
|
1252
|
-
if (localHash === registryHash) {
|
|
1591
|
+
const localContent = await fs7.readFile(localFile, "utf-8");
|
|
1592
|
+
const normalizedLocal = normalizeForComparison(localContent);
|
|
1593
|
+
const normalizedRegistry = normalizeForComparison(registryFile.content);
|
|
1594
|
+
if (normalizedLocal === normalizedRegistry) {
|
|
1253
1595
|
results.push({
|
|
1254
1596
|
name: registryItem.name,
|
|
1255
|
-
|
|
1597
|
+
fileName,
|
|
1598
|
+
status: "identical",
|
|
1256
1599
|
localFile
|
|
1257
1600
|
});
|
|
1258
1601
|
} else {
|
|
1602
|
+
const diffOutput = Diff.createPatch(
|
|
1603
|
+
fileName,
|
|
1604
|
+
normalizedRegistry,
|
|
1605
|
+
normalizedLocal,
|
|
1606
|
+
"registry",
|
|
1607
|
+
"local"
|
|
1608
|
+
);
|
|
1259
1609
|
results.push({
|
|
1260
1610
|
name: registryItem.name,
|
|
1261
|
-
|
|
1262
|
-
|
|
1611
|
+
fileName,
|
|
1612
|
+
status: "modified",
|
|
1613
|
+
localFile,
|
|
1614
|
+
diff: diffOutput
|
|
1263
1615
|
});
|
|
1264
1616
|
}
|
|
1265
1617
|
} catch (error) {
|
|
1266
1618
|
results.push({
|
|
1267
1619
|
name: registryItem.name,
|
|
1620
|
+
fileName,
|
|
1268
1621
|
status: "error",
|
|
1269
1622
|
error: String(error)
|
|
1270
1623
|
});
|
|
@@ -1273,100 +1626,71 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1273
1626
|
spinner.stop();
|
|
1274
1627
|
if (options.json) {
|
|
1275
1628
|
console.log(JSON.stringify(results, null, 2));
|
|
1276
|
-
|
|
1629
|
+
const hasChanges2 = results.some((r) => r.status === "modified");
|
|
1630
|
+
process.exit(hasChanges2 ? 1 : 0);
|
|
1277
1631
|
}
|
|
1278
|
-
printResults(results, options.
|
|
1632
|
+
printResults(results, options.list);
|
|
1633
|
+
const hasChanges = results.some((r) => r.status === "modified");
|
|
1634
|
+
process.exit(hasChanges ? 1 : 0);
|
|
1279
1635
|
} catch (error) {
|
|
1280
|
-
spinner.fail("Failed to
|
|
1636
|
+
spinner.fail("Failed to diff components");
|
|
1281
1637
|
console.error(error);
|
|
1282
1638
|
process.exit(1);
|
|
1283
1639
|
}
|
|
1284
1640
|
});
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
}
|
|
1289
|
-
const files = await fs6.readdir(componentsDir);
|
|
1290
|
-
const componentFiles = [];
|
|
1291
|
-
for (const file of files) {
|
|
1292
|
-
if (file.endsWith(".tsx") || file.endsWith(".ts")) {
|
|
1293
|
-
if (file === "index.ts" || file === "index.tsx") {
|
|
1294
|
-
continue;
|
|
1295
|
-
}
|
|
1296
|
-
componentFiles.push(path6.join(componentsDir, file));
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
return componentFiles;
|
|
1300
|
-
}
|
|
1301
|
-
function hashContent(content) {
|
|
1302
|
-
const normalized = content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
|
|
1303
|
-
return crypto.createHash("md5").update(normalized).digest("hex");
|
|
1304
|
-
}
|
|
1305
|
-
function transformImports2(code, config) {
|
|
1306
|
-
let transformed = code;
|
|
1307
|
-
const utilsAlias = config.aliases?.utils || "@/lib/utils";
|
|
1308
|
-
if (utilsAlias === "@/lib/utils") {
|
|
1309
|
-
return transformed;
|
|
1310
|
-
}
|
|
1311
|
-
transformed = transformed.replace(
|
|
1312
|
-
/from ['"]@\/lib\/utils['"]/g,
|
|
1313
|
-
`from '${utilsAlias}'`
|
|
1314
|
-
);
|
|
1315
|
-
return transformed;
|
|
1316
|
-
}
|
|
1317
|
-
function printResults(results, verbose) {
|
|
1318
|
-
const upToDate = results.filter((r) => r.status === "up-to-date");
|
|
1319
|
-
const changed = results.filter((r) => r.status === "changed");
|
|
1641
|
+
function printResults(results, listOnly) {
|
|
1642
|
+
const identical = results.filter((r) => r.status === "identical");
|
|
1643
|
+
const modified = results.filter((r) => r.status === "modified");
|
|
1320
1644
|
const localOnly = results.filter((r) => r.status === "local-only");
|
|
1321
1645
|
const errors = results.filter((r) => r.status === "error");
|
|
1322
1646
|
console.log();
|
|
1323
|
-
console.log(chalk6.bold("
|
|
1324
|
-
console.log(chalk6.dim("\u2500".repeat(40)));
|
|
1325
|
-
console.log();
|
|
1326
|
-
const total = results.length;
|
|
1327
|
-
console.log(`Found ${chalk6.bold(total)} installed component${total !== 1 ? "s" : ""}`);
|
|
1647
|
+
console.log(chalk6.bold("Comparing components..."));
|
|
1328
1648
|
console.log();
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
console.log(chalk6.dim(` ${names}`));
|
|
1649
|
+
for (const result of results) {
|
|
1650
|
+
switch (result.status) {
|
|
1651
|
+
case "identical":
|
|
1652
|
+
console.log(`${chalk6.green("\u2713")} ${result.fileName} ${chalk6.dim("(identical)")}`);
|
|
1653
|
+
break;
|
|
1654
|
+
case "modified":
|
|
1655
|
+
console.log(`${chalk6.red("\u2717")} ${result.fileName} ${chalk6.yellow("(modified)")}`);
|
|
1656
|
+
if (!listOnly && result.diff) {
|
|
1657
|
+
printColoredDiff(result.diff);
|
|
1658
|
+
}
|
|
1659
|
+
break;
|
|
1660
|
+
case "local-only":
|
|
1661
|
+
console.log(`${chalk6.yellow("\u26A0")} ${result.fileName} ${chalk6.dim("(not in registry)")}`);
|
|
1662
|
+
break;
|
|
1663
|
+
case "error":
|
|
1664
|
+
console.log(`${chalk6.red("!")} ${result.fileName} ${chalk6.red(`(error: ${result.error})`)}`);
|
|
1665
|
+
break;
|
|
1347
1666
|
}
|
|
1348
|
-
console.log();
|
|
1349
1667
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1668
|
+
console.log();
|
|
1669
|
+
const parts = [];
|
|
1670
|
+
if (identical.length > 0) parts.push(`${identical.length} identical`);
|
|
1671
|
+
if (modified.length > 0) parts.push(`${modified.length} modified`);
|
|
1672
|
+
if (localOnly.length > 0) parts.push(`${localOnly.length} custom`);
|
|
1673
|
+
if (errors.length > 0) parts.push(`${errors.length} errors`);
|
|
1674
|
+
console.log(chalk6.dim(`Summary: ${parts.join(", ")}`));
|
|
1675
|
+
if (modified.length > 0) {
|
|
1355
1676
|
console.log();
|
|
1677
|
+
console.log(chalk6.dim("Update modified components with:"));
|
|
1678
|
+
console.log(chalk6.cyan(` npx mcellui add ${modified.map((m) => m.name).join(" ")} --overwrite`));
|
|
1356
1679
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1680
|
+
}
|
|
1681
|
+
function printColoredDiff(diffOutput) {
|
|
1682
|
+
const lines = diffOutput.split("\n");
|
|
1683
|
+
const contentLines = lines.slice(4);
|
|
1684
|
+
for (const line of contentLines) {
|
|
1685
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
1686
|
+
console.log(chalk6.green(` ${line}`));
|
|
1687
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
1688
|
+
console.log(chalk6.red(` ${line}`));
|
|
1689
|
+
} else if (line.startsWith("@@")) {
|
|
1690
|
+
console.log(chalk6.cyan(` ${line}`));
|
|
1691
|
+
} else if (line.trim()) {
|
|
1692
|
+
console.log(chalk6.dim(` ${line}`));
|
|
1361
1693
|
}
|
|
1362
|
-
console.log();
|
|
1363
|
-
}
|
|
1364
|
-
if (changed.length === 0 && errors.length === 0) {
|
|
1365
|
-
console.log(chalk6.green("All registry components are up to date!"));
|
|
1366
|
-
} else if (changed.length > 0) {
|
|
1367
|
-
console.log(
|
|
1368
|
-
chalk6.yellow(`Run ${chalk6.cyan("npx nativeui add " + changed.map((c) => c.name).join(" ") + " --overwrite")} to update`)
|
|
1369
|
-
);
|
|
1370
1694
|
}
|
|
1371
1695
|
}
|
|
1372
1696
|
|
|
@@ -1375,13 +1699,12 @@ import { Command as Command6 } from "commander";
|
|
|
1375
1699
|
import chalk7 from "chalk";
|
|
1376
1700
|
import ora5 from "ora";
|
|
1377
1701
|
import prompts3 from "prompts";
|
|
1378
|
-
import
|
|
1379
|
-
import
|
|
1380
|
-
import crypto2 from "crypto";
|
|
1702
|
+
import fs8 from "fs-extra";
|
|
1703
|
+
import path9 from "path";
|
|
1381
1704
|
var updateCommand = new Command6().name("update").description("Update installed components to latest registry versions").argument("[components...]", "Specific components to update (default: all outdated)").option("-y, --yes", "Skip confirmation prompt").option("--all", "Update all components (including up-to-date)").option("--dry-run", "Show what would be updated without making changes").option("--cwd <path>", "Working directory", process.cwd()).action(async (components, options) => {
|
|
1382
1705
|
const spinner = ora5();
|
|
1383
1706
|
try {
|
|
1384
|
-
const cwd =
|
|
1707
|
+
const cwd = path9.resolve(options.cwd);
|
|
1385
1708
|
const projectRoot = await getProjectRoot(cwd);
|
|
1386
1709
|
if (!projectRoot) {
|
|
1387
1710
|
console.log(chalk7.red("Could not find a valid project."));
|
|
@@ -1395,7 +1718,7 @@ var updateCommand = new Command6().name("update").description("Update installed
|
|
|
1395
1718
|
process.exit(1);
|
|
1396
1719
|
}
|
|
1397
1720
|
spinner.start("Checking for updates...");
|
|
1398
|
-
const componentsDir =
|
|
1721
|
+
const componentsDir = path9.join(projectRoot, config.componentsPath);
|
|
1399
1722
|
const diffs = await getComponentDiffs(componentsDir, config);
|
|
1400
1723
|
if (diffs.length === 0) {
|
|
1401
1724
|
spinner.info("No components installed yet.");
|
|
@@ -1457,12 +1780,12 @@ var updateCommand = new Command6().name("update").description("Update installed
|
|
|
1457
1780
|
failCount++;
|
|
1458
1781
|
continue;
|
|
1459
1782
|
}
|
|
1460
|
-
const targetDir =
|
|
1783
|
+
const targetDir = path9.join(projectRoot, config.componentsPath);
|
|
1461
1784
|
for (const file of component.files) {
|
|
1462
|
-
const targetPath =
|
|
1463
|
-
await
|
|
1464
|
-
const transformedContent =
|
|
1465
|
-
await
|
|
1785
|
+
const targetPath = path9.join(targetDir, file.name);
|
|
1786
|
+
await fs8.ensureDir(targetDir);
|
|
1787
|
+
const transformedContent = transformToInstalled(file.content, config);
|
|
1788
|
+
await fs8.writeFile(targetPath, transformedContent);
|
|
1466
1789
|
}
|
|
1467
1790
|
spinner.succeed(`Updated ${comp.name}`);
|
|
1468
1791
|
successCount++;
|
|
@@ -1503,18 +1826,18 @@ var updateCommand = new Command6().name("update").description("Update installed
|
|
|
1503
1826
|
}
|
|
1504
1827
|
});
|
|
1505
1828
|
async function getComponentDiffs(componentsDir, config) {
|
|
1506
|
-
if (!await
|
|
1829
|
+
if (!await fs8.pathExists(componentsDir)) {
|
|
1507
1830
|
return [];
|
|
1508
1831
|
}
|
|
1509
1832
|
const registry = await getRegistry();
|
|
1510
1833
|
const registryMap = /* @__PURE__ */ new Map();
|
|
1511
1834
|
for (const item of registry) {
|
|
1512
1835
|
for (const file of item.files) {
|
|
1513
|
-
const fileName =
|
|
1836
|
+
const fileName = path9.basename(file);
|
|
1514
1837
|
registryMap.set(fileName, item);
|
|
1515
1838
|
}
|
|
1516
1839
|
}
|
|
1517
|
-
const files = await
|
|
1840
|
+
const files = await fs8.readdir(componentsDir);
|
|
1518
1841
|
const results = [];
|
|
1519
1842
|
for (const file of files) {
|
|
1520
1843
|
if (!file.endsWith(".tsx") && !file.endsWith(".ts")) {
|
|
@@ -1527,7 +1850,7 @@ async function getComponentDiffs(componentsDir, config) {
|
|
|
1527
1850
|
if (!registryItem) {
|
|
1528
1851
|
continue;
|
|
1529
1852
|
}
|
|
1530
|
-
const localFile =
|
|
1853
|
+
const localFile = path9.join(componentsDir, file);
|
|
1531
1854
|
const hasUpdate = await checkForUpdate(localFile, registryItem, config);
|
|
1532
1855
|
results.push({
|
|
1533
1856
|
name: registryItem.name,
|
|
@@ -1541,46 +1864,29 @@ async function checkForUpdate(localFile, registryItem, config) {
|
|
|
1541
1864
|
try {
|
|
1542
1865
|
const component = await fetchComponent(registryItem.name);
|
|
1543
1866
|
if (!component) return false;
|
|
1544
|
-
const fileName =
|
|
1867
|
+
const fileName = path9.basename(localFile);
|
|
1545
1868
|
const registryFile = component.files.find((f) => f.name === fileName);
|
|
1546
1869
|
if (!registryFile) return false;
|
|
1547
|
-
const localContent = await
|
|
1548
|
-
const
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
return localHash !== registryHash;
|
|
1870
|
+
const localContent = await fs8.readFile(localFile, "utf-8");
|
|
1871
|
+
const normalizedLocal = normalizeForComparison(localContent);
|
|
1872
|
+
const normalizedRegistry = normalizeForComparison(registryFile.content);
|
|
1873
|
+
return normalizedLocal !== normalizedRegistry;
|
|
1552
1874
|
} catch {
|
|
1553
1875
|
return false;
|
|
1554
1876
|
}
|
|
1555
1877
|
}
|
|
1556
|
-
function hashContent2(content) {
|
|
1557
|
-
const normalized = content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
|
|
1558
|
-
return crypto2.createHash("md5").update(normalized).digest("hex");
|
|
1559
|
-
}
|
|
1560
|
-
function transformImports3(code, config) {
|
|
1561
|
-
let transformed = code;
|
|
1562
|
-
const utilsAlias = config.aliases?.utils || "@/lib/utils";
|
|
1563
|
-
if (utilsAlias === "@/lib/utils") {
|
|
1564
|
-
return transformed;
|
|
1565
|
-
}
|
|
1566
|
-
transformed = transformed.replace(
|
|
1567
|
-
/from ['"]@\/lib\/utils['"]/g,
|
|
1568
|
-
`from '${utilsAlias}'`
|
|
1569
|
-
);
|
|
1570
|
-
return transformed;
|
|
1571
|
-
}
|
|
1572
1878
|
|
|
1573
1879
|
// src/commands/create.ts
|
|
1574
1880
|
import { Command as Command7 } from "commander";
|
|
1575
1881
|
import chalk8 from "chalk";
|
|
1576
1882
|
import ora6 from "ora";
|
|
1577
1883
|
import prompts4 from "prompts";
|
|
1578
|
-
import
|
|
1579
|
-
import
|
|
1884
|
+
import fs9 from "fs-extra";
|
|
1885
|
+
import path10 from "path";
|
|
1580
1886
|
var createCommand = new Command7().name("create").description("Scaffold a new custom component").argument("<name>", "Component name (e.g., my-button, ProfileCard)").option("-t, --template <type>", "Component template: basic, animated, pressable, input", "basic").option("--forward-ref", "Include forwardRef pattern").option("-y, --yes", "Skip confirmation").option("--cwd <path>", "Working directory", process.cwd()).action(async (name, options) => {
|
|
1581
1887
|
const spinner = ora6();
|
|
1582
1888
|
try {
|
|
1583
|
-
const cwd =
|
|
1889
|
+
const cwd = path10.resolve(options.cwd);
|
|
1584
1890
|
const projectRoot = await getProjectRoot(cwd);
|
|
1585
1891
|
if (!projectRoot) {
|
|
1586
1892
|
console.log(chalk8.red("Could not find a valid project."));
|
|
@@ -1595,9 +1901,9 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
|
|
|
1595
1901
|
}
|
|
1596
1902
|
const componentName = toPascalCase(name);
|
|
1597
1903
|
const fileName = toKebabCase(name) + ".tsx";
|
|
1598
|
-
const targetDir =
|
|
1599
|
-
const targetPath =
|
|
1600
|
-
if (await
|
|
1904
|
+
const targetDir = path10.join(projectRoot, config.componentsPath);
|
|
1905
|
+
const targetPath = path10.join(targetDir, fileName);
|
|
1906
|
+
if (await fs9.pathExists(targetPath)) {
|
|
1601
1907
|
console.log(chalk8.red(`Component already exists: ${fileName}`));
|
|
1602
1908
|
console.log(chalk8.dim(`Path: ${targetPath}`));
|
|
1603
1909
|
process.exit(1);
|
|
@@ -1624,8 +1930,8 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
|
|
|
1624
1930
|
}
|
|
1625
1931
|
spinner.start("Creating component...");
|
|
1626
1932
|
const code = generateComponent(componentName, options.template, options.forwardRef);
|
|
1627
|
-
await
|
|
1628
|
-
await
|
|
1933
|
+
await fs9.ensureDir(targetDir);
|
|
1934
|
+
await fs9.writeFile(targetPath, code);
|
|
1629
1935
|
spinner.succeed(`Created ${fileName}`);
|
|
1630
1936
|
console.log();
|
|
1631
1937
|
console.log(chalk8.bold("Next steps:"));
|
|
@@ -1658,7 +1964,7 @@ function generateBasicComponent(name, forwardRef) {
|
|
|
1658
1964
|
if (forwardRef) {
|
|
1659
1965
|
return `import React, { forwardRef } from 'react';
|
|
1660
1966
|
import { View, Text, StyleSheet, ViewProps } from 'react-native';
|
|
1661
|
-
import { useTheme } from '@
|
|
1967
|
+
import { useTheme } from '@metacells/mcellui-core';
|
|
1662
1968
|
|
|
1663
1969
|
export interface ${name}Props extends ViewProps {
|
|
1664
1970
|
/**
|
|
@@ -1718,7 +2024,7 @@ const styles = StyleSheet.create({
|
|
|
1718
2024
|
}
|
|
1719
2025
|
return `import React from 'react';
|
|
1720
2026
|
import { View, Text, StyleSheet, ViewProps } from 'react-native';
|
|
1721
|
-
import { useTheme } from '@
|
|
2027
|
+
import { useTheme } from '@metacells/mcellui-core';
|
|
1722
2028
|
|
|
1723
2029
|
export interface ${name}Props extends ViewProps {
|
|
1724
2030
|
/**
|
|
@@ -1785,7 +2091,7 @@ import Animated, {
|
|
|
1785
2091
|
useSharedValue,
|
|
1786
2092
|
withSpring,
|
|
1787
2093
|
} from 'react-native-reanimated';
|
|
1788
|
-
import { useTheme } from '@
|
|
2094
|
+
import { useTheme } from '@metacells/mcellui-core';
|
|
1789
2095
|
|
|
1790
2096
|
export interface ${name}Props extends ViewProps {
|
|
1791
2097
|
/**
|
|
@@ -1854,7 +2160,7 @@ import Animated, {
|
|
|
1854
2160
|
useSharedValue,
|
|
1855
2161
|
withTiming,
|
|
1856
2162
|
} from 'react-native-reanimated';
|
|
1857
|
-
import { useTheme } from '@
|
|
2163
|
+
import { useTheme } from '@metacells/mcellui-core';
|
|
1858
2164
|
|
|
1859
2165
|
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
1860
2166
|
|
|
@@ -1947,7 +2253,7 @@ import Animated, {
|
|
|
1947
2253
|
useSharedValue,
|
|
1948
2254
|
withTiming,
|
|
1949
2255
|
} from 'react-native-reanimated';
|
|
1950
|
-
import { useTheme } from '@
|
|
2256
|
+
import { useTheme } from '@metacells/mcellui-core';
|
|
1951
2257
|
|
|
1952
2258
|
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
1953
2259
|
|
|
@@ -2037,7 +2343,7 @@ import Animated, {
|
|
|
2037
2343
|
useSharedValue,
|
|
2038
2344
|
withTiming,
|
|
2039
2345
|
} from 'react-native-reanimated';
|
|
2040
|
-
import { useTheme } from '@
|
|
2346
|
+
import { useTheme } from '@metacells/mcellui-core';
|
|
2041
2347
|
|
|
2042
2348
|
export interface ${name}Props extends TextInputProps {
|
|
2043
2349
|
/**
|
|
@@ -2162,7 +2468,7 @@ import Animated, {
|
|
|
2162
2468
|
useSharedValue,
|
|
2163
2469
|
withTiming,
|
|
2164
2470
|
} from 'react-native-reanimated';
|
|
2165
|
-
import { useTheme } from '@
|
|
2471
|
+
import { useTheme } from '@metacells/mcellui-core';
|
|
2166
2472
|
|
|
2167
2473
|
export interface ${name}Props extends TextInputProps {
|
|
2168
2474
|
/**
|
|
@@ -2281,8 +2587,8 @@ import { Command as Command8 } from "commander";
|
|
|
2281
2587
|
import chalk9 from "chalk";
|
|
2282
2588
|
import prompts5 from "prompts";
|
|
2283
2589
|
import ora7 from "ora";
|
|
2284
|
-
import
|
|
2285
|
-
import
|
|
2590
|
+
import fs10 from "fs-extra";
|
|
2591
|
+
import path11 from "path";
|
|
2286
2592
|
function groupByCategory(items) {
|
|
2287
2593
|
const groups = {};
|
|
2288
2594
|
for (const item of items) {
|
|
@@ -2339,21 +2645,9 @@ function formatComponentChoice(item, installed) {
|
|
|
2339
2645
|
disabled: isInstalled
|
|
2340
2646
|
};
|
|
2341
2647
|
}
|
|
2342
|
-
function transformImports4(code, config) {
|
|
2343
|
-
let transformed = code;
|
|
2344
|
-
const utilsAlias = config.aliases?.utils || "@/lib/utils";
|
|
2345
|
-
if (utilsAlias === "@/lib/utils") {
|
|
2346
|
-
return transformed;
|
|
2347
|
-
}
|
|
2348
|
-
transformed = transformed.replace(
|
|
2349
|
-
/from ['"]@\/lib\/utils['"]/g,
|
|
2350
|
-
`from '${utilsAlias}'`
|
|
2351
|
-
);
|
|
2352
|
-
return transformed;
|
|
2353
|
-
}
|
|
2354
2648
|
var pickCommand = new Command8().name("pick").description("Interactively browse and select components to add").option("--all", "Show all components without category selection").option("-o, --overwrite", "Overwrite existing files").option("--cwd <path>", "Working directory", process.cwd()).action(async (options) => {
|
|
2355
2649
|
console.log(chalk9.bold("\n\u{1F3A8} nativeui Component Picker\n"));
|
|
2356
|
-
const cwd =
|
|
2650
|
+
const cwd = path11.resolve(options.cwd);
|
|
2357
2651
|
const projectRoot = await getProjectRoot(cwd);
|
|
2358
2652
|
if (!projectRoot) {
|
|
2359
2653
|
console.log(chalk9.red("Could not find a valid project."));
|
|
@@ -2372,10 +2666,10 @@ var pickCommand = new Command8().name("pick").description("Interactively browse
|
|
|
2372
2666
|
return;
|
|
2373
2667
|
}
|
|
2374
2668
|
spinner.succeed(`Loaded ${registry.length} components`);
|
|
2375
|
-
const componentsDir =
|
|
2669
|
+
const componentsDir = path11.join(projectRoot, config.componentsPath);
|
|
2376
2670
|
const installed = /* @__PURE__ */ new Set();
|
|
2377
|
-
if (
|
|
2378
|
-
const files =
|
|
2671
|
+
if (fs10.existsSync(componentsDir)) {
|
|
2672
|
+
const files = fs10.readdirSync(componentsDir);
|
|
2379
2673
|
for (const file of files) {
|
|
2380
2674
|
if (file.endsWith(".tsx")) {
|
|
2381
2675
|
installed.add(file.replace(".tsx", ""));
|
|
@@ -2458,16 +2752,16 @@ ${installed.size} already installed, ${availableCount} available
|
|
|
2458
2752
|
installSpinner.fail(`Component "${name}" not found`);
|
|
2459
2753
|
continue;
|
|
2460
2754
|
}
|
|
2461
|
-
const targetDir =
|
|
2755
|
+
const targetDir = path11.join(projectRoot, config.componentsPath);
|
|
2462
2756
|
for (const file of component.files) {
|
|
2463
|
-
const targetPath =
|
|
2464
|
-
if (await
|
|
2757
|
+
const targetPath = path11.join(targetDir, file.name);
|
|
2758
|
+
if (await fs10.pathExists(targetPath) && !options.overwrite) {
|
|
2465
2759
|
installSpinner.warn(`${name}: ${file.name} already exists (use --overwrite)`);
|
|
2466
2760
|
continue;
|
|
2467
2761
|
}
|
|
2468
|
-
await
|
|
2469
|
-
const transformedContent =
|
|
2470
|
-
await
|
|
2762
|
+
await fs10.ensureDir(targetDir);
|
|
2763
|
+
const transformedContent = transformToInstalled(file.content, config);
|
|
2764
|
+
await fs10.writeFile(targetPath, transformedContent);
|
|
2471
2765
|
}
|
|
2472
2766
|
installSpinner.succeed(`Installed ${chalk9.green(name)}`);
|
|
2473
2767
|
successCount++;
|
|
@@ -2513,7 +2807,7 @@ ${installed.size} already installed, ${availableCount} available
|
|
|
2513
2807
|
|
|
2514
2808
|
// src/index.ts
|
|
2515
2809
|
var program = new Command9();
|
|
2516
|
-
program.name("
|
|
2810
|
+
program.name("mcellui").description("Add beautiful UI components to your Expo/React Native project").version("0.1.4");
|
|
2517
2811
|
program.addCommand(initCommand);
|
|
2518
2812
|
program.addCommand(addCommand);
|
|
2519
2813
|
program.addCommand(listCommand);
|