@metacells/mcellui-cli 0.1.2 → 0.1.4
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 +481 -184
- package/package.json +5 -3
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
|
}
|
|
@@ -475,8 +475,8 @@ import { Command as Command2 } from "commander";
|
|
|
475
475
|
import chalk3 from "chalk";
|
|
476
476
|
import ora2 from "ora";
|
|
477
477
|
import prompts2 from "prompts";
|
|
478
|
-
import
|
|
479
|
-
import
|
|
478
|
+
import fs5 from "fs-extra";
|
|
479
|
+
import path5 from "path";
|
|
480
480
|
|
|
481
481
|
// src/utils/registry.ts
|
|
482
482
|
import fs3 from "fs-extra";
|
|
@@ -484,7 +484,7 @@ import path3 from "path";
|
|
|
484
484
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
485
485
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
486
486
|
var __dirname2 = path3.dirname(__filename3);
|
|
487
|
-
var DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/metacells/mcellui/main/packages/registry";
|
|
487
|
+
var DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/metacells-development/mcellui/main/packages/registry";
|
|
488
488
|
var REGISTRY_URL = process.env.MCELLUI_REGISTRY_URL || process.env.NATIVEUI_REGISTRY_URL || DEFAULT_REGISTRY_URL;
|
|
489
489
|
function getLocalRegistryPath() {
|
|
490
490
|
return path3.resolve(__dirname2, "..", "..", "registry");
|
|
@@ -567,11 +567,173 @@ async function fetchRemoteComponent(item) {
|
|
|
567
567
|
};
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
+
// src/utils/dependencies.ts
|
|
571
|
+
function resolveDependencies(components, registry) {
|
|
572
|
+
const registryMap = /* @__PURE__ */ new Map();
|
|
573
|
+
for (const item of registry) {
|
|
574
|
+
registryMap.set(item.name, item);
|
|
575
|
+
}
|
|
576
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
577
|
+
const resolving = /* @__PURE__ */ new Set();
|
|
578
|
+
const resolvedOrder = [];
|
|
579
|
+
function resolve(name, path12) {
|
|
580
|
+
if (resolved.has(name)) {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
if (resolving.has(name)) {
|
|
584
|
+
return [...path12, name];
|
|
585
|
+
}
|
|
586
|
+
const item = registryMap.get(name);
|
|
587
|
+
if (!item) {
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
resolving.add(name);
|
|
591
|
+
const deps = item.registryDependencies || [];
|
|
592
|
+
for (const dep of deps) {
|
|
593
|
+
const circular = resolve(dep, [...path12, name]);
|
|
594
|
+
if (circular) {
|
|
595
|
+
return circular;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
resolving.delete(name);
|
|
599
|
+
resolved.add(name);
|
|
600
|
+
resolvedOrder.push(name);
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
for (const name of components) {
|
|
604
|
+
const circular = resolve(name, []);
|
|
605
|
+
if (circular) {
|
|
606
|
+
return {
|
|
607
|
+
resolved: [],
|
|
608
|
+
circular
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return {
|
|
613
|
+
resolved: resolvedOrder,
|
|
614
|
+
circular: null
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
function formatCircularError(chain) {
|
|
618
|
+
return chain.join(" \u2192 ");
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// src/utils/installed.ts
|
|
622
|
+
import fs4 from "fs-extra";
|
|
623
|
+
import path4 from "path";
|
|
624
|
+
async function getInstalledFiles(componentsDir) {
|
|
625
|
+
if (!await fs4.pathExists(componentsDir)) {
|
|
626
|
+
return [];
|
|
627
|
+
}
|
|
628
|
+
const files = await fs4.readdir(componentsDir);
|
|
629
|
+
const componentFiles = [];
|
|
630
|
+
for (const file of files) {
|
|
631
|
+
if (file.endsWith(".tsx") || file.endsWith(".ts")) {
|
|
632
|
+
if (file === "index.ts" || file === "index.tsx") {
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
componentFiles.push(path4.join(componentsDir, file));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return componentFiles.sort();
|
|
639
|
+
}
|
|
640
|
+
function normalizeContent(content) {
|
|
641
|
+
return content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
|
|
642
|
+
}
|
|
643
|
+
function transformImports(code, config) {
|
|
644
|
+
let transformed = code;
|
|
645
|
+
transformed = transformed.replace(
|
|
646
|
+
/from ['"]@nativeui\/core['"]/g,
|
|
647
|
+
`from '@metacells/mcellui-core'`
|
|
648
|
+
);
|
|
649
|
+
const utilsAlias = config.aliases?.utils || "@/lib/utils";
|
|
650
|
+
if (utilsAlias !== "@/lib/utils") {
|
|
651
|
+
transformed = transformed.replace(
|
|
652
|
+
/from ['"]@\/lib\/utils['"]/g,
|
|
653
|
+
`from '${utilsAlias}'`
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
return transformed;
|
|
657
|
+
}
|
|
658
|
+
async function getInstallStatus(installedFiles, registry, config) {
|
|
659
|
+
const results = [];
|
|
660
|
+
const registryMap = /* @__PURE__ */ new Map();
|
|
661
|
+
for (const item of registry) {
|
|
662
|
+
for (const file of item.files) {
|
|
663
|
+
const fileName = path4.basename(file);
|
|
664
|
+
registryMap.set(fileName, item);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
for (const localFile of installedFiles) {
|
|
668
|
+
const fileName = path4.basename(localFile);
|
|
669
|
+
const componentName = fileName.replace(/\.tsx?$/, "");
|
|
670
|
+
const registryItem = registryMap.get(fileName);
|
|
671
|
+
if (!registryItem) {
|
|
672
|
+
results.push({
|
|
673
|
+
name: componentName,
|
|
674
|
+
fileName,
|
|
675
|
+
status: "local-only",
|
|
676
|
+
localPath: localFile
|
|
677
|
+
});
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
try {
|
|
681
|
+
const component = await fetchComponent(registryItem.name);
|
|
682
|
+
if (!component) {
|
|
683
|
+
results.push({
|
|
684
|
+
name: registryItem.name,
|
|
685
|
+
fileName,
|
|
686
|
+
status: "modified",
|
|
687
|
+
// Assume modified if we can't verify
|
|
688
|
+
localPath: localFile
|
|
689
|
+
});
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
const registryFile = component.files.find((f) => f.name === fileName);
|
|
693
|
+
if (!registryFile) {
|
|
694
|
+
results.push({
|
|
695
|
+
name: registryItem.name,
|
|
696
|
+
fileName,
|
|
697
|
+
status: "modified",
|
|
698
|
+
localPath: localFile
|
|
699
|
+
});
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
const localContent = await fs4.readFile(localFile, "utf-8");
|
|
703
|
+
const normalizedLocal = normalizeContent(localContent);
|
|
704
|
+
const normalizedRegistry = normalizeContent(
|
|
705
|
+
transformImports(registryFile.content, config)
|
|
706
|
+
);
|
|
707
|
+
results.push({
|
|
708
|
+
name: registryItem.name,
|
|
709
|
+
fileName,
|
|
710
|
+
status: normalizedLocal === normalizedRegistry ? "identical" : "modified",
|
|
711
|
+
localPath: localFile
|
|
712
|
+
});
|
|
713
|
+
} catch {
|
|
714
|
+
results.push({
|
|
715
|
+
name: registryItem.name,
|
|
716
|
+
fileName,
|
|
717
|
+
status: "modified",
|
|
718
|
+
// Assume modified on error
|
|
719
|
+
localPath: localFile
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return results;
|
|
724
|
+
}
|
|
725
|
+
function getInstalledNames(installedFiles) {
|
|
726
|
+
return installedFiles.map((file) => {
|
|
727
|
+
const fileName = path4.basename(file);
|
|
728
|
+
return fileName.replace(/\.tsx?$/, "");
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
|
|
570
732
|
// src/commands/add.ts
|
|
571
733
|
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
734
|
const spinner = ora2();
|
|
573
735
|
try {
|
|
574
|
-
const cwd =
|
|
736
|
+
const cwd = path5.resolve(options.cwd);
|
|
575
737
|
const projectRoot = await getProjectRoot(cwd);
|
|
576
738
|
if (!projectRoot) {
|
|
577
739
|
console.log(chalk3.red("Could not find a valid project."));
|
|
@@ -585,8 +747,8 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
585
747
|
process.exit(1);
|
|
586
748
|
}
|
|
587
749
|
if (!components.length) {
|
|
588
|
-
const
|
|
589
|
-
if (!
|
|
750
|
+
const registry2 = await getRegistry();
|
|
751
|
+
if (!registry2.length) {
|
|
590
752
|
console.log(chalk3.red("No components found in registry."));
|
|
591
753
|
process.exit(1);
|
|
592
754
|
}
|
|
@@ -594,7 +756,7 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
594
756
|
type: "multiselect",
|
|
595
757
|
name: "selected",
|
|
596
758
|
message: "Which components would you like to add?",
|
|
597
|
-
choices:
|
|
759
|
+
choices: registry2.map((item) => ({
|
|
598
760
|
title: `${item.name} ${chalk3.dim(`(${item.status})`)}`,
|
|
599
761
|
value: item.name,
|
|
600
762
|
description: item.description
|
|
@@ -607,13 +769,51 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
607
769
|
}
|
|
608
770
|
components = selected;
|
|
609
771
|
}
|
|
772
|
+
spinner.start("Resolving dependencies...");
|
|
773
|
+
const registry = await getRegistry();
|
|
774
|
+
const { resolved, circular } = resolveDependencies(components, registry);
|
|
775
|
+
if (circular) {
|
|
776
|
+
spinner.fail("Circular dependency detected");
|
|
777
|
+
console.log(chalk3.red(` ${formatCircularError(circular)}`));
|
|
778
|
+
process.exit(1);
|
|
779
|
+
}
|
|
780
|
+
spinner.stop();
|
|
781
|
+
const componentsDir = path5.join(projectRoot, config.componentsPath);
|
|
782
|
+
const installedFiles = await getInstalledFiles(componentsDir);
|
|
783
|
+
const installedNames = new Set(getInstalledNames(installedFiles));
|
|
784
|
+
const toInstall = options.overwrite ? resolved : resolved.filter((name) => !installedNames.has(name));
|
|
785
|
+
if (toInstall.length === 0) {
|
|
786
|
+
console.log(chalk3.yellow("All components are already installed."));
|
|
787
|
+
console.log(chalk3.dim("Use --overwrite to reinstall."));
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const requested = new Set(components);
|
|
791
|
+
const dependencies = toInstall.filter((name) => !requested.has(name));
|
|
610
792
|
console.log();
|
|
611
793
|
console.log(chalk3.bold("Adding components:"));
|
|
612
|
-
|
|
794
|
+
for (const name of toInstall) {
|
|
795
|
+
if (requested.has(name)) {
|
|
796
|
+
console.log(chalk3.dim(` - ${name}`));
|
|
797
|
+
} else {
|
|
798
|
+
console.log(chalk3.dim(` - ${name} ${chalk3.cyan("(dependency)")}`));
|
|
799
|
+
}
|
|
800
|
+
}
|
|
613
801
|
console.log();
|
|
802
|
+
if (dependencies.length > 0 && !options.yes) {
|
|
803
|
+
const { confirm } = await prompts2({
|
|
804
|
+
type: "confirm",
|
|
805
|
+
name: "confirm",
|
|
806
|
+
message: `Add ${dependencies.length} additional dependencies?`,
|
|
807
|
+
initial: true
|
|
808
|
+
});
|
|
809
|
+
if (!confirm) {
|
|
810
|
+
console.log(chalk3.dim("Cancelled."));
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
614
814
|
const allDependencies = [];
|
|
615
815
|
const allDevDependencies = [];
|
|
616
|
-
for (const componentName of
|
|
816
|
+
for (const componentName of toInstall) {
|
|
617
817
|
spinner.start(`Fetching ${componentName}...`);
|
|
618
818
|
try {
|
|
619
819
|
const component = await fetchComponent(componentName);
|
|
@@ -621,16 +821,16 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
621
821
|
spinner.fail(`Component "${componentName}" not found`);
|
|
622
822
|
continue;
|
|
623
823
|
}
|
|
624
|
-
const targetDir =
|
|
824
|
+
const targetDir = path5.join(projectRoot, config.componentsPath);
|
|
625
825
|
for (const file of component.files) {
|
|
626
|
-
const targetPath =
|
|
627
|
-
if (await
|
|
826
|
+
const targetPath = path5.join(targetDir, file.name);
|
|
827
|
+
if (await fs5.pathExists(targetPath) && !options.overwrite) {
|
|
628
828
|
spinner.warn(`${file.name} already exists (use --overwrite)`);
|
|
629
829
|
continue;
|
|
630
830
|
}
|
|
631
|
-
await
|
|
632
|
-
const transformedContent =
|
|
633
|
-
await
|
|
831
|
+
await fs5.ensureDir(targetDir);
|
|
832
|
+
const transformedContent = transformImports2(file.content, config);
|
|
833
|
+
await fs5.writeFile(targetPath, transformedContent);
|
|
634
834
|
}
|
|
635
835
|
spinner.succeed(`Added ${componentName}`);
|
|
636
836
|
if (component.dependencies?.length) {
|
|
@@ -639,11 +839,6 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
639
839
|
if (component.devDependencies?.length) {
|
|
640
840
|
allDevDependencies.push(...component.devDependencies);
|
|
641
841
|
}
|
|
642
|
-
if (component.registryDependencies?.length) {
|
|
643
|
-
console.log(
|
|
644
|
-
chalk3.dim(` Requires: ${component.registryDependencies.join(", ")}`)
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
842
|
} catch (error) {
|
|
648
843
|
spinner.fail(`Failed to add ${componentName}`);
|
|
649
844
|
console.error(chalk3.dim(String(error)));
|
|
@@ -669,7 +864,7 @@ var addCommand = new Command2().name("add").description("Add a component to your
|
|
|
669
864
|
process.exit(1);
|
|
670
865
|
}
|
|
671
866
|
});
|
|
672
|
-
function
|
|
867
|
+
function transformImports2(code, config) {
|
|
673
868
|
let transformed = code;
|
|
674
869
|
const utilsAlias = config.aliases?.utils || "@/lib/utils";
|
|
675
870
|
if (utilsAlias === "@/lib/utils") {
|
|
@@ -686,7 +881,15 @@ function transformImports(code, config) {
|
|
|
686
881
|
import { Command as Command3 } from "commander";
|
|
687
882
|
import chalk4 from "chalk";
|
|
688
883
|
import ora3 from "ora";
|
|
689
|
-
|
|
884
|
+
import path6 from "path";
|
|
885
|
+
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) => {
|
|
886
|
+
if (options.installed) {
|
|
887
|
+
await listInstalledComponents(options);
|
|
888
|
+
} else {
|
|
889
|
+
await listAvailableComponents(options);
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
async function listAvailableComponents(options) {
|
|
690
893
|
const spinner = ora3("Fetching components...").start();
|
|
691
894
|
try {
|
|
692
895
|
const registry = await getRegistry();
|
|
@@ -714,20 +917,113 @@ var listCommand = new Command3().name("list").description("List available compon
|
|
|
714
917
|
}
|
|
715
918
|
console.log();
|
|
716
919
|
}
|
|
717
|
-
console.log(chalk4.dim("Add a component: npx
|
|
920
|
+
console.log(chalk4.dim("Add a component: npx mcellui add <component>"));
|
|
718
921
|
console.log();
|
|
719
922
|
} catch (error) {
|
|
720
923
|
spinner.fail("Failed to fetch components");
|
|
721
924
|
console.error(error);
|
|
722
925
|
process.exit(1);
|
|
723
926
|
}
|
|
724
|
-
}
|
|
927
|
+
}
|
|
928
|
+
async function listInstalledComponents(options) {
|
|
929
|
+
const spinner = ora3();
|
|
930
|
+
try {
|
|
931
|
+
const cwd = path6.resolve(options.cwd || process.cwd());
|
|
932
|
+
const projectRoot = await getProjectRoot(cwd);
|
|
933
|
+
if (!projectRoot) {
|
|
934
|
+
console.log(chalk4.red("Could not find a valid project."));
|
|
935
|
+
console.log(chalk4.dim("Run `npx mcellui init` first."));
|
|
936
|
+
process.exit(1);
|
|
937
|
+
}
|
|
938
|
+
const config = await getConfig(projectRoot);
|
|
939
|
+
if (!config) {
|
|
940
|
+
console.log(chalk4.red("Project not initialized."));
|
|
941
|
+
console.log(chalk4.dim("Run `npx mcellui init` first."));
|
|
942
|
+
process.exit(1);
|
|
943
|
+
}
|
|
944
|
+
spinner.start("Scanning installed components...");
|
|
945
|
+
const componentsDir = path6.join(projectRoot, config.componentsPath);
|
|
946
|
+
const installedFiles = await getInstalledFiles(componentsDir);
|
|
947
|
+
if (installedFiles.length === 0) {
|
|
948
|
+
spinner.info("No components installed yet.");
|
|
949
|
+
console.log(chalk4.dim("\nAdd components with: npx mcellui add <component>"));
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
spinner.text = "Fetching registry...";
|
|
953
|
+
const registry = await getRegistry();
|
|
954
|
+
spinner.text = "Comparing components...";
|
|
955
|
+
const installed = await getInstallStatus(installedFiles, registry, config);
|
|
956
|
+
spinner.stop();
|
|
957
|
+
const identical = installed.filter((c) => c.status === "identical");
|
|
958
|
+
const modified = installed.filter((c) => c.status === "modified");
|
|
959
|
+
const localOnly = installed.filter((c) => c.status === "local-only");
|
|
960
|
+
const installedNames = new Set(installed.map((c) => c.name));
|
|
961
|
+
const notInstalled = registry.filter((item) => !installedNames.has(item.name));
|
|
962
|
+
console.log();
|
|
963
|
+
console.log(chalk4.bold(`Installed Components (${installed.length})`));
|
|
964
|
+
console.log();
|
|
965
|
+
const categories = /* @__PURE__ */ new Map();
|
|
966
|
+
for (const comp of installed) {
|
|
967
|
+
const registryItem = registry.find((r) => r.name === comp.name);
|
|
968
|
+
const category = registryItem?.category || "Custom";
|
|
969
|
+
if (!categories.has(category)) {
|
|
970
|
+
categories.set(category, []);
|
|
971
|
+
}
|
|
972
|
+
categories.get(category).push(comp);
|
|
973
|
+
}
|
|
974
|
+
for (const [category, components] of categories) {
|
|
975
|
+
console.log(chalk4.cyan.bold(category));
|
|
976
|
+
for (const comp of components) {
|
|
977
|
+
let statusIcon;
|
|
978
|
+
let statusText;
|
|
979
|
+
switch (comp.status) {
|
|
980
|
+
case "identical":
|
|
981
|
+
statusIcon = chalk4.green("\u2713");
|
|
982
|
+
statusText = chalk4.dim("(up to date)");
|
|
983
|
+
break;
|
|
984
|
+
case "modified":
|
|
985
|
+
statusIcon = chalk4.yellow("\u26A0");
|
|
986
|
+
statusText = chalk4.yellow("(modified locally)");
|
|
987
|
+
break;
|
|
988
|
+
case "local-only":
|
|
989
|
+
statusIcon = chalk4.blue("?");
|
|
990
|
+
statusText = chalk4.dim("(custom component)");
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
993
|
+
console.log(` ${statusIcon} ${chalk4.white(comp.name)} ${statusText}`);
|
|
994
|
+
}
|
|
995
|
+
console.log();
|
|
996
|
+
}
|
|
997
|
+
if (notInstalled.length > 0) {
|
|
998
|
+
console.log(chalk4.dim("Not Installed"));
|
|
999
|
+
const notInstalledNames = notInstalled.map((c) => c.name).slice(0, 10);
|
|
1000
|
+
const remaining = notInstalled.length - 10;
|
|
1001
|
+
console.log(chalk4.dim(` ${notInstalledNames.join(", ")}${remaining > 0 ? `, ... +${remaining} more` : ""}`));
|
|
1002
|
+
console.log();
|
|
1003
|
+
}
|
|
1004
|
+
console.log(chalk4.dim("\u2500".repeat(50)));
|
|
1005
|
+
const parts = [];
|
|
1006
|
+
if (identical.length > 0) parts.push(chalk4.green(`${identical.length} up to date`));
|
|
1007
|
+
if (modified.length > 0) parts.push(chalk4.yellow(`${modified.length} modified`));
|
|
1008
|
+
if (localOnly.length > 0) parts.push(chalk4.blue(`${localOnly.length} custom`));
|
|
1009
|
+
console.log(parts.join(" \u2022 "));
|
|
1010
|
+
if (modified.length > 0) {
|
|
1011
|
+
console.log();
|
|
1012
|
+
console.log(chalk4.dim("Sync modified components:"));
|
|
1013
|
+
console.log(chalk4.cyan(` npx mcellui diff`));
|
|
1014
|
+
}
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
spinner.fail("Failed to list installed components");
|
|
1017
|
+
console.error(error);
|
|
1018
|
+
process.exit(1);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
725
1021
|
|
|
726
1022
|
// src/commands/doctor.ts
|
|
727
1023
|
import { Command as Command4 } from "commander";
|
|
728
1024
|
import chalk5 from "chalk";
|
|
729
|
-
import
|
|
730
|
-
import
|
|
1025
|
+
import fs6 from "fs-extra";
|
|
1026
|
+
import path7 from "path";
|
|
731
1027
|
var REQUIRED_PEER_DEPS = [
|
|
732
1028
|
{ name: "react-native-reanimated", minVersion: "3.0.0" },
|
|
733
1029
|
{ name: "react-native-gesture-handler", minVersion: "2.0.0" },
|
|
@@ -796,7 +1092,7 @@ async function checkInitialized(projectRoot) {
|
|
|
796
1092
|
];
|
|
797
1093
|
let foundConfig = null;
|
|
798
1094
|
for (const file of configFiles) {
|
|
799
|
-
if (await
|
|
1095
|
+
if (await fs6.pathExists(path7.join(projectRoot, file))) {
|
|
800
1096
|
foundConfig = file;
|
|
801
1097
|
break;
|
|
802
1098
|
}
|
|
@@ -810,8 +1106,8 @@ async function checkInitialized(projectRoot) {
|
|
|
810
1106
|
};
|
|
811
1107
|
}
|
|
812
1108
|
try {
|
|
813
|
-
const configPath =
|
|
814
|
-
const content = await
|
|
1109
|
+
const configPath = path7.join(projectRoot, foundConfig);
|
|
1110
|
+
const content = await fs6.readFile(configPath, "utf-8");
|
|
815
1111
|
if (foundConfig.endsWith(".ts") || foundConfig.endsWith(".js")) {
|
|
816
1112
|
const hasDefineConfig = content.includes("defineConfig");
|
|
817
1113
|
const hasExport = content.includes("export default");
|
|
@@ -865,10 +1161,10 @@ async function checkPaths(projectRoot) {
|
|
|
865
1161
|
let utilsPathValue = defaultUtilsPath;
|
|
866
1162
|
const configFiles = ["nativeui.config.ts", "nativeui.config.js", "nativeui.config.json"];
|
|
867
1163
|
for (const file of configFiles) {
|
|
868
|
-
const configPath =
|
|
869
|
-
if (await
|
|
1164
|
+
const configPath = path7.join(projectRoot, file);
|
|
1165
|
+
if (await fs6.pathExists(configPath)) {
|
|
870
1166
|
try {
|
|
871
|
-
const content = await
|
|
1167
|
+
const content = await fs6.readFile(configPath, "utf-8");
|
|
872
1168
|
const componentsMatch = content.match(/componentsPath:\s*['"]([^'"]+)['"]/);
|
|
873
1169
|
const utilsMatch = content.match(/utilsPath:\s*['"]([^'"]+)['"]/);
|
|
874
1170
|
if (componentsMatch) componentsPathValue = componentsMatch[1];
|
|
@@ -878,10 +1174,10 @@ async function checkPaths(projectRoot) {
|
|
|
878
1174
|
break;
|
|
879
1175
|
}
|
|
880
1176
|
}
|
|
881
|
-
const componentsPath =
|
|
882
|
-
const utilsPath =
|
|
883
|
-
const componentsExist = await
|
|
884
|
-
const utilsExist = await
|
|
1177
|
+
const componentsPath = path7.join(projectRoot, componentsPathValue);
|
|
1178
|
+
const utilsPath = path7.join(projectRoot, utilsPathValue);
|
|
1179
|
+
const componentsExist = await fs6.pathExists(componentsPath);
|
|
1180
|
+
const utilsExist = await fs6.pathExists(utilsPath);
|
|
885
1181
|
if (!componentsExist && !utilsExist) {
|
|
886
1182
|
return {
|
|
887
1183
|
name: "Component Paths",
|
|
@@ -978,10 +1274,10 @@ async function checkBabelConfig(projectRoot) {
|
|
|
978
1274
|
let babelConfigPath = null;
|
|
979
1275
|
let babelContent = null;
|
|
980
1276
|
for (const configFile of babelConfigPaths) {
|
|
981
|
-
const fullPath =
|
|
982
|
-
if (await
|
|
1277
|
+
const fullPath = path7.join(projectRoot, configFile);
|
|
1278
|
+
if (await fs6.pathExists(fullPath)) {
|
|
983
1279
|
babelConfigPath = configFile;
|
|
984
|
-
babelContent = await
|
|
1280
|
+
babelContent = await fs6.readFile(fullPath, "utf-8");
|
|
985
1281
|
break;
|
|
986
1282
|
}
|
|
987
1283
|
}
|
|
@@ -1009,8 +1305,8 @@ async function checkBabelConfig(projectRoot) {
|
|
|
1009
1305
|
};
|
|
1010
1306
|
}
|
|
1011
1307
|
async function checkTypeScript(projectRoot) {
|
|
1012
|
-
const tsconfigPath =
|
|
1013
|
-
if (!await
|
|
1308
|
+
const tsconfigPath = path7.join(projectRoot, "tsconfig.json");
|
|
1309
|
+
if (!await fs6.pathExists(tsconfigPath)) {
|
|
1014
1310
|
return {
|
|
1015
1311
|
name: "TypeScript",
|
|
1016
1312
|
status: "warn",
|
|
@@ -1019,7 +1315,7 @@ async function checkTypeScript(projectRoot) {
|
|
|
1019
1315
|
};
|
|
1020
1316
|
}
|
|
1021
1317
|
try {
|
|
1022
|
-
const tsconfig = await
|
|
1318
|
+
const tsconfig = await fs6.readJson(tsconfigPath);
|
|
1023
1319
|
const hasPathAliases = tsconfig.compilerOptions?.paths;
|
|
1024
1320
|
if (!hasPathAliases) {
|
|
1025
1321
|
return {
|
|
@@ -1125,7 +1421,7 @@ function printReport(report) {
|
|
|
1125
1421
|
}
|
|
1126
1422
|
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
1423
|
try {
|
|
1128
|
-
const cwd =
|
|
1424
|
+
const cwd = path7.resolve(options.cwd);
|
|
1129
1425
|
const projectRoot = await getProjectRoot(cwd);
|
|
1130
1426
|
if (!projectRoot) {
|
|
1131
1427
|
console.log(chalk5.red("Could not find a valid project."));
|
|
@@ -1177,31 +1473,31 @@ var doctorCommand = new Command4().name("doctor").description("Check project set
|
|
|
1177
1473
|
import { Command as Command5 } from "commander";
|
|
1178
1474
|
import chalk6 from "chalk";
|
|
1179
1475
|
import ora4 from "ora";
|
|
1180
|
-
import
|
|
1181
|
-
import
|
|
1182
|
-
import
|
|
1183
|
-
var diffCommand = new Command5().name("diff").description("
|
|
1476
|
+
import fs7 from "fs-extra";
|
|
1477
|
+
import path8 from "path";
|
|
1478
|
+
import * as Diff from "diff";
|
|
1479
|
+
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
1480
|
const spinner = ora4();
|
|
1185
1481
|
try {
|
|
1186
|
-
const cwd =
|
|
1482
|
+
const cwd = path8.resolve(options.cwd);
|
|
1187
1483
|
const projectRoot = await getProjectRoot(cwd);
|
|
1188
1484
|
if (!projectRoot) {
|
|
1189
1485
|
console.log(chalk6.red("Could not find a valid project."));
|
|
1190
|
-
console.log(chalk6.dim("Run `npx
|
|
1486
|
+
console.log(chalk6.dim("Run `npx mcellui init` first."));
|
|
1191
1487
|
process.exit(1);
|
|
1192
1488
|
}
|
|
1193
1489
|
const config = await getConfig(projectRoot);
|
|
1194
1490
|
if (!config) {
|
|
1195
1491
|
console.log(chalk6.red("Project not initialized."));
|
|
1196
|
-
console.log(chalk6.dim("Run `npx
|
|
1492
|
+
console.log(chalk6.dim("Run `npx mcellui init` first."));
|
|
1197
1493
|
process.exit(1);
|
|
1198
1494
|
}
|
|
1199
1495
|
spinner.start("Scanning installed components...");
|
|
1200
|
-
const componentsDir =
|
|
1201
|
-
const installedFiles = await
|
|
1496
|
+
const componentsDir = path8.join(projectRoot, config.componentsPath);
|
|
1497
|
+
const installedFiles = await getInstalledFiles(componentsDir);
|
|
1202
1498
|
if (installedFiles.length === 0) {
|
|
1203
1499
|
spinner.info("No components installed yet.");
|
|
1204
|
-
console.log(chalk6.dim("\nAdd components with: npx
|
|
1500
|
+
console.log(chalk6.dim("\nAdd components with: npx mcellui add <component>"));
|
|
1205
1501
|
return;
|
|
1206
1502
|
}
|
|
1207
1503
|
spinner.text = "Fetching registry...";
|
|
@@ -1209,18 +1505,34 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1209
1505
|
const registryMap = /* @__PURE__ */ new Map();
|
|
1210
1506
|
for (const item of registry) {
|
|
1211
1507
|
for (const file of item.files) {
|
|
1212
|
-
const fileName =
|
|
1508
|
+
const fileName = path8.basename(file);
|
|
1213
1509
|
registryMap.set(fileName, item);
|
|
1214
1510
|
}
|
|
1215
1511
|
}
|
|
1512
|
+
let filesToCompare = installedFiles;
|
|
1513
|
+
if (components.length > 0) {
|
|
1514
|
+
const componentFileNames = components.map((c) => {
|
|
1515
|
+
return c.endsWith(".tsx") ? c : `${c}.tsx`;
|
|
1516
|
+
});
|
|
1517
|
+
filesToCompare = installedFiles.filter((file) => {
|
|
1518
|
+
const fileName = path8.basename(file);
|
|
1519
|
+
return componentFileNames.includes(fileName);
|
|
1520
|
+
});
|
|
1521
|
+
if (filesToCompare.length === 0) {
|
|
1522
|
+
spinner.fail(`None of the specified components found: ${components.join(", ")}`);
|
|
1523
|
+
process.exit(1);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1216
1526
|
spinner.text = "Comparing components...";
|
|
1217
1527
|
const results = [];
|
|
1218
|
-
for (const localFile of
|
|
1219
|
-
const fileName =
|
|
1528
|
+
for (const localFile of filesToCompare) {
|
|
1529
|
+
const fileName = path8.basename(localFile);
|
|
1530
|
+
const componentName = fileName.replace(/\.tsx?$/, "");
|
|
1220
1531
|
const registryItem = registryMap.get(fileName);
|
|
1221
1532
|
if (!registryItem) {
|
|
1222
1533
|
results.push({
|
|
1223
|
-
name:
|
|
1534
|
+
name: componentName,
|
|
1535
|
+
fileName,
|
|
1224
1536
|
status: "local-only",
|
|
1225
1537
|
localFile
|
|
1226
1538
|
});
|
|
@@ -1231,6 +1543,7 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1231
1543
|
if (!component) {
|
|
1232
1544
|
results.push({
|
|
1233
1545
|
name: registryItem.name,
|
|
1546
|
+
fileName,
|
|
1234
1547
|
status: "error",
|
|
1235
1548
|
error: "Failed to fetch from registry"
|
|
1236
1549
|
});
|
|
@@ -1240,31 +1553,44 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1240
1553
|
if (!registryFile) {
|
|
1241
1554
|
results.push({
|
|
1242
1555
|
name: registryItem.name,
|
|
1556
|
+
fileName,
|
|
1243
1557
|
status: "error",
|
|
1244
1558
|
error: "File not found in registry"
|
|
1245
1559
|
});
|
|
1246
1560
|
continue;
|
|
1247
1561
|
}
|
|
1248
|
-
const localContent = await
|
|
1249
|
-
const
|
|
1250
|
-
const
|
|
1251
|
-
|
|
1252
|
-
|
|
1562
|
+
const localContent = await fs7.readFile(localFile, "utf-8");
|
|
1563
|
+
const normalizedLocal = normalizeContent(localContent);
|
|
1564
|
+
const normalizedRegistry = normalizeContent(
|
|
1565
|
+
transformImports(registryFile.content, config)
|
|
1566
|
+
);
|
|
1567
|
+
if (normalizedLocal === normalizedRegistry) {
|
|
1253
1568
|
results.push({
|
|
1254
1569
|
name: registryItem.name,
|
|
1255
|
-
|
|
1570
|
+
fileName,
|
|
1571
|
+
status: "identical",
|
|
1256
1572
|
localFile
|
|
1257
1573
|
});
|
|
1258
1574
|
} else {
|
|
1575
|
+
const diffOutput = Diff.createPatch(
|
|
1576
|
+
fileName,
|
|
1577
|
+
normalizedRegistry,
|
|
1578
|
+
normalizedLocal,
|
|
1579
|
+
"registry",
|
|
1580
|
+
"local"
|
|
1581
|
+
);
|
|
1259
1582
|
results.push({
|
|
1260
1583
|
name: registryItem.name,
|
|
1261
|
-
|
|
1262
|
-
|
|
1584
|
+
fileName,
|
|
1585
|
+
status: "modified",
|
|
1586
|
+
localFile,
|
|
1587
|
+
diff: diffOutput
|
|
1263
1588
|
});
|
|
1264
1589
|
}
|
|
1265
1590
|
} catch (error) {
|
|
1266
1591
|
results.push({
|
|
1267
1592
|
name: registryItem.name,
|
|
1593
|
+
fileName,
|
|
1268
1594
|
status: "error",
|
|
1269
1595
|
error: String(error)
|
|
1270
1596
|
});
|
|
@@ -1273,100 +1599,71 @@ var diffCommand = new Command5().name("diff").description("Check for component u
|
|
|
1273
1599
|
spinner.stop();
|
|
1274
1600
|
if (options.json) {
|
|
1275
1601
|
console.log(JSON.stringify(results, null, 2));
|
|
1276
|
-
|
|
1602
|
+
const hasChanges2 = results.some((r) => r.status === "modified");
|
|
1603
|
+
process.exit(hasChanges2 ? 1 : 0);
|
|
1277
1604
|
}
|
|
1278
|
-
printResults(results, options.
|
|
1605
|
+
printResults(results, options.list);
|
|
1606
|
+
const hasChanges = results.some((r) => r.status === "modified");
|
|
1607
|
+
process.exit(hasChanges ? 1 : 0);
|
|
1279
1608
|
} catch (error) {
|
|
1280
|
-
spinner.fail("Failed to
|
|
1609
|
+
spinner.fail("Failed to diff components");
|
|
1281
1610
|
console.error(error);
|
|
1282
1611
|
process.exit(1);
|
|
1283
1612
|
}
|
|
1284
1613
|
});
|
|
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");
|
|
1614
|
+
function printResults(results, listOnly) {
|
|
1615
|
+
const identical = results.filter((r) => r.status === "identical");
|
|
1616
|
+
const modified = results.filter((r) => r.status === "modified");
|
|
1320
1617
|
const localOnly = results.filter((r) => r.status === "local-only");
|
|
1321
1618
|
const errors = results.filter((r) => r.status === "error");
|
|
1322
1619
|
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" : ""}`);
|
|
1620
|
+
console.log(chalk6.bold("Comparing components..."));
|
|
1328
1621
|
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}`));
|
|
1622
|
+
for (const result of results) {
|
|
1623
|
+
switch (result.status) {
|
|
1624
|
+
case "identical":
|
|
1625
|
+
console.log(`${chalk6.green("\u2713")} ${result.fileName} ${chalk6.dim("(identical)")}`);
|
|
1626
|
+
break;
|
|
1627
|
+
case "modified":
|
|
1628
|
+
console.log(`${chalk6.red("\u2717")} ${result.fileName} ${chalk6.yellow("(modified)")}`);
|
|
1629
|
+
if (!listOnly && result.diff) {
|
|
1630
|
+
printColoredDiff(result.diff);
|
|
1631
|
+
}
|
|
1632
|
+
break;
|
|
1633
|
+
case "local-only":
|
|
1634
|
+
console.log(`${chalk6.yellow("\u26A0")} ${result.fileName} ${chalk6.dim("(not in registry)")}`);
|
|
1635
|
+
break;
|
|
1636
|
+
case "error":
|
|
1637
|
+
console.log(`${chalk6.red("!")} ${result.fileName} ${chalk6.red(`(error: ${result.error})`)}`);
|
|
1638
|
+
break;
|
|
1347
1639
|
}
|
|
1348
|
-
console.log();
|
|
1349
1640
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1641
|
+
console.log();
|
|
1642
|
+
const parts = [];
|
|
1643
|
+
if (identical.length > 0) parts.push(`${identical.length} identical`);
|
|
1644
|
+
if (modified.length > 0) parts.push(`${modified.length} modified`);
|
|
1645
|
+
if (localOnly.length > 0) parts.push(`${localOnly.length} custom`);
|
|
1646
|
+
if (errors.length > 0) parts.push(`${errors.length} errors`);
|
|
1647
|
+
console.log(chalk6.dim(`Summary: ${parts.join(", ")}`));
|
|
1648
|
+
if (modified.length > 0) {
|
|
1355
1649
|
console.log();
|
|
1650
|
+
console.log(chalk6.dim("Update modified components with:"));
|
|
1651
|
+
console.log(chalk6.cyan(` npx mcellui add ${modified.map((m) => m.name).join(" ")} --overwrite`));
|
|
1356
1652
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1653
|
+
}
|
|
1654
|
+
function printColoredDiff(diffOutput) {
|
|
1655
|
+
const lines = diffOutput.split("\n");
|
|
1656
|
+
const contentLines = lines.slice(4);
|
|
1657
|
+
for (const line of contentLines) {
|
|
1658
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
1659
|
+
console.log(chalk6.green(` ${line}`));
|
|
1660
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
1661
|
+
console.log(chalk6.red(` ${line}`));
|
|
1662
|
+
} else if (line.startsWith("@@")) {
|
|
1663
|
+
console.log(chalk6.cyan(` ${line}`));
|
|
1664
|
+
} else if (line.trim()) {
|
|
1665
|
+
console.log(chalk6.dim(` ${line}`));
|
|
1361
1666
|
}
|
|
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
1667
|
}
|
|
1371
1668
|
}
|
|
1372
1669
|
|
|
@@ -1375,13 +1672,13 @@ import { Command as Command6 } from "commander";
|
|
|
1375
1672
|
import chalk7 from "chalk";
|
|
1376
1673
|
import ora5 from "ora";
|
|
1377
1674
|
import prompts3 from "prompts";
|
|
1378
|
-
import
|
|
1379
|
-
import
|
|
1380
|
-
import
|
|
1675
|
+
import fs8 from "fs-extra";
|
|
1676
|
+
import path9 from "path";
|
|
1677
|
+
import crypto from "crypto";
|
|
1381
1678
|
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
1679
|
const spinner = ora5();
|
|
1383
1680
|
try {
|
|
1384
|
-
const cwd =
|
|
1681
|
+
const cwd = path9.resolve(options.cwd);
|
|
1385
1682
|
const projectRoot = await getProjectRoot(cwd);
|
|
1386
1683
|
if (!projectRoot) {
|
|
1387
1684
|
console.log(chalk7.red("Could not find a valid project."));
|
|
@@ -1395,7 +1692,7 @@ var updateCommand = new Command6().name("update").description("Update installed
|
|
|
1395
1692
|
process.exit(1);
|
|
1396
1693
|
}
|
|
1397
1694
|
spinner.start("Checking for updates...");
|
|
1398
|
-
const componentsDir =
|
|
1695
|
+
const componentsDir = path9.join(projectRoot, config.componentsPath);
|
|
1399
1696
|
const diffs = await getComponentDiffs(componentsDir, config);
|
|
1400
1697
|
if (diffs.length === 0) {
|
|
1401
1698
|
spinner.info("No components installed yet.");
|
|
@@ -1457,12 +1754,12 @@ var updateCommand = new Command6().name("update").description("Update installed
|
|
|
1457
1754
|
failCount++;
|
|
1458
1755
|
continue;
|
|
1459
1756
|
}
|
|
1460
|
-
const targetDir =
|
|
1757
|
+
const targetDir = path9.join(projectRoot, config.componentsPath);
|
|
1461
1758
|
for (const file of component.files) {
|
|
1462
|
-
const targetPath =
|
|
1463
|
-
await
|
|
1759
|
+
const targetPath = path9.join(targetDir, file.name);
|
|
1760
|
+
await fs8.ensureDir(targetDir);
|
|
1464
1761
|
const transformedContent = transformImports3(file.content, config);
|
|
1465
|
-
await
|
|
1762
|
+
await fs8.writeFile(targetPath, transformedContent);
|
|
1466
1763
|
}
|
|
1467
1764
|
spinner.succeed(`Updated ${comp.name}`);
|
|
1468
1765
|
successCount++;
|
|
@@ -1503,18 +1800,18 @@ var updateCommand = new Command6().name("update").description("Update installed
|
|
|
1503
1800
|
}
|
|
1504
1801
|
});
|
|
1505
1802
|
async function getComponentDiffs(componentsDir, config) {
|
|
1506
|
-
if (!await
|
|
1803
|
+
if (!await fs8.pathExists(componentsDir)) {
|
|
1507
1804
|
return [];
|
|
1508
1805
|
}
|
|
1509
1806
|
const registry = await getRegistry();
|
|
1510
1807
|
const registryMap = /* @__PURE__ */ new Map();
|
|
1511
1808
|
for (const item of registry) {
|
|
1512
1809
|
for (const file of item.files) {
|
|
1513
|
-
const fileName =
|
|
1810
|
+
const fileName = path9.basename(file);
|
|
1514
1811
|
registryMap.set(fileName, item);
|
|
1515
1812
|
}
|
|
1516
1813
|
}
|
|
1517
|
-
const files = await
|
|
1814
|
+
const files = await fs8.readdir(componentsDir);
|
|
1518
1815
|
const results = [];
|
|
1519
1816
|
for (const file of files) {
|
|
1520
1817
|
if (!file.endsWith(".tsx") && !file.endsWith(".ts")) {
|
|
@@ -1527,7 +1824,7 @@ async function getComponentDiffs(componentsDir, config) {
|
|
|
1527
1824
|
if (!registryItem) {
|
|
1528
1825
|
continue;
|
|
1529
1826
|
}
|
|
1530
|
-
const localFile =
|
|
1827
|
+
const localFile = path9.join(componentsDir, file);
|
|
1531
1828
|
const hasUpdate = await checkForUpdate(localFile, registryItem, config);
|
|
1532
1829
|
results.push({
|
|
1533
1830
|
name: registryItem.name,
|
|
@@ -1541,21 +1838,21 @@ async function checkForUpdate(localFile, registryItem, config) {
|
|
|
1541
1838
|
try {
|
|
1542
1839
|
const component = await fetchComponent(registryItem.name);
|
|
1543
1840
|
if (!component) return false;
|
|
1544
|
-
const fileName =
|
|
1841
|
+
const fileName = path9.basename(localFile);
|
|
1545
1842
|
const registryFile = component.files.find((f) => f.name === fileName);
|
|
1546
1843
|
if (!registryFile) return false;
|
|
1547
|
-
const localContent = await
|
|
1844
|
+
const localContent = await fs8.readFile(localFile, "utf-8");
|
|
1548
1845
|
const transformedRegistryContent = transformImports3(registryFile.content, config);
|
|
1549
|
-
const localHash =
|
|
1550
|
-
const registryHash =
|
|
1846
|
+
const localHash = hashContent(localContent);
|
|
1847
|
+
const registryHash = hashContent(transformedRegistryContent);
|
|
1551
1848
|
return localHash !== registryHash;
|
|
1552
1849
|
} catch {
|
|
1553
1850
|
return false;
|
|
1554
1851
|
}
|
|
1555
1852
|
}
|
|
1556
|
-
function
|
|
1853
|
+
function hashContent(content) {
|
|
1557
1854
|
const normalized = content.replace(/\r\n/g, "\n").replace(/\s+$/gm, "").trim();
|
|
1558
|
-
return
|
|
1855
|
+
return crypto.createHash("md5").update(normalized).digest("hex");
|
|
1559
1856
|
}
|
|
1560
1857
|
function transformImports3(code, config) {
|
|
1561
1858
|
let transformed = code;
|
|
@@ -1575,12 +1872,12 @@ import { Command as Command7 } from "commander";
|
|
|
1575
1872
|
import chalk8 from "chalk";
|
|
1576
1873
|
import ora6 from "ora";
|
|
1577
1874
|
import prompts4 from "prompts";
|
|
1578
|
-
import
|
|
1579
|
-
import
|
|
1875
|
+
import fs9 from "fs-extra";
|
|
1876
|
+
import path10 from "path";
|
|
1580
1877
|
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
1878
|
const spinner = ora6();
|
|
1582
1879
|
try {
|
|
1583
|
-
const cwd =
|
|
1880
|
+
const cwd = path10.resolve(options.cwd);
|
|
1584
1881
|
const projectRoot = await getProjectRoot(cwd);
|
|
1585
1882
|
if (!projectRoot) {
|
|
1586
1883
|
console.log(chalk8.red("Could not find a valid project."));
|
|
@@ -1595,9 +1892,9 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
|
|
|
1595
1892
|
}
|
|
1596
1893
|
const componentName = toPascalCase(name);
|
|
1597
1894
|
const fileName = toKebabCase(name) + ".tsx";
|
|
1598
|
-
const targetDir =
|
|
1599
|
-
const targetPath =
|
|
1600
|
-
if (await
|
|
1895
|
+
const targetDir = path10.join(projectRoot, config.componentsPath);
|
|
1896
|
+
const targetPath = path10.join(targetDir, fileName);
|
|
1897
|
+
if (await fs9.pathExists(targetPath)) {
|
|
1601
1898
|
console.log(chalk8.red(`Component already exists: ${fileName}`));
|
|
1602
1899
|
console.log(chalk8.dim(`Path: ${targetPath}`));
|
|
1603
1900
|
process.exit(1);
|
|
@@ -1624,8 +1921,8 @@ var createCommand = new Command7().name("create").description("Scaffold a new cu
|
|
|
1624
1921
|
}
|
|
1625
1922
|
spinner.start("Creating component...");
|
|
1626
1923
|
const code = generateComponent(componentName, options.template, options.forwardRef);
|
|
1627
|
-
await
|
|
1628
|
-
await
|
|
1924
|
+
await fs9.ensureDir(targetDir);
|
|
1925
|
+
await fs9.writeFile(targetPath, code);
|
|
1629
1926
|
spinner.succeed(`Created ${fileName}`);
|
|
1630
1927
|
console.log();
|
|
1631
1928
|
console.log(chalk8.bold("Next steps:"));
|
|
@@ -2281,8 +2578,8 @@ import { Command as Command8 } from "commander";
|
|
|
2281
2578
|
import chalk9 from "chalk";
|
|
2282
2579
|
import prompts5 from "prompts";
|
|
2283
2580
|
import ora7 from "ora";
|
|
2284
|
-
import
|
|
2285
|
-
import
|
|
2581
|
+
import fs10 from "fs-extra";
|
|
2582
|
+
import path11 from "path";
|
|
2286
2583
|
function groupByCategory(items) {
|
|
2287
2584
|
const groups = {};
|
|
2288
2585
|
for (const item of items) {
|
|
@@ -2353,7 +2650,7 @@ function transformImports4(code, config) {
|
|
|
2353
2650
|
}
|
|
2354
2651
|
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
2652
|
console.log(chalk9.bold("\n\u{1F3A8} nativeui Component Picker\n"));
|
|
2356
|
-
const cwd =
|
|
2653
|
+
const cwd = path11.resolve(options.cwd);
|
|
2357
2654
|
const projectRoot = await getProjectRoot(cwd);
|
|
2358
2655
|
if (!projectRoot) {
|
|
2359
2656
|
console.log(chalk9.red("Could not find a valid project."));
|
|
@@ -2372,10 +2669,10 @@ var pickCommand = new Command8().name("pick").description("Interactively browse
|
|
|
2372
2669
|
return;
|
|
2373
2670
|
}
|
|
2374
2671
|
spinner.succeed(`Loaded ${registry.length} components`);
|
|
2375
|
-
const componentsDir =
|
|
2672
|
+
const componentsDir = path11.join(projectRoot, config.componentsPath);
|
|
2376
2673
|
const installed = /* @__PURE__ */ new Set();
|
|
2377
|
-
if (
|
|
2378
|
-
const files =
|
|
2674
|
+
if (fs10.existsSync(componentsDir)) {
|
|
2675
|
+
const files = fs10.readdirSync(componentsDir);
|
|
2379
2676
|
for (const file of files) {
|
|
2380
2677
|
if (file.endsWith(".tsx")) {
|
|
2381
2678
|
installed.add(file.replace(".tsx", ""));
|
|
@@ -2458,16 +2755,16 @@ ${installed.size} already installed, ${availableCount} available
|
|
|
2458
2755
|
installSpinner.fail(`Component "${name}" not found`);
|
|
2459
2756
|
continue;
|
|
2460
2757
|
}
|
|
2461
|
-
const targetDir =
|
|
2758
|
+
const targetDir = path11.join(projectRoot, config.componentsPath);
|
|
2462
2759
|
for (const file of component.files) {
|
|
2463
|
-
const targetPath =
|
|
2464
|
-
if (await
|
|
2760
|
+
const targetPath = path11.join(targetDir, file.name);
|
|
2761
|
+
if (await fs10.pathExists(targetPath) && !options.overwrite) {
|
|
2465
2762
|
installSpinner.warn(`${name}: ${file.name} already exists (use --overwrite)`);
|
|
2466
2763
|
continue;
|
|
2467
2764
|
}
|
|
2468
|
-
await
|
|
2765
|
+
await fs10.ensureDir(targetDir);
|
|
2469
2766
|
const transformedContent = transformImports4(file.content, config);
|
|
2470
|
-
await
|
|
2767
|
+
await fs10.writeFile(targetPath, transformedContent);
|
|
2471
2768
|
}
|
|
2472
2769
|
installSpinner.succeed(`Installed ${chalk9.green(name)}`);
|
|
2473
2770
|
successCount++;
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metacells/mcellui-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "CLI for mcellui - add beautiful, accessible UI components to your Expo/React Native project",
|
|
5
5
|
"author": "metacells",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://mcellui.dev",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/metacells/mcellui.git",
|
|
10
|
+
"url": "git+https://github.com/metacells-development/mcellui.git",
|
|
11
11
|
"directory": "packages/cli"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/metacells/mcellui/issues"
|
|
14
|
+
"url": "https://github.com/metacells-development/mcellui/issues"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"react-native",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"commander": "^12.0.0",
|
|
45
45
|
"chalk": "^5.3.0",
|
|
46
|
+
"diff": "^7.0.0",
|
|
46
47
|
"ora": "^8.0.0",
|
|
47
48
|
"prompts": "^2.4.2",
|
|
48
49
|
"fs-extra": "^11.2.0",
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"zod": "^3.23.0"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
56
|
+
"@types/diff": "^6.0.0",
|
|
55
57
|
"@types/fs-extra": "^11.0.0",
|
|
56
58
|
"@types/node": "^20.0.0",
|
|
57
59
|
"@types/prompts": "^2.4.0",
|