bejamas 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { BASE_COLORS, extractFrontmatter, getConfig, getProjectInfo, highlighter, logger, parseJsDocMetadata, resolveUiRoot, spinner } from "./utils-DfvCox_O.js";
2
+ import { S as BASE_COLORS, _ as getWorkspaceConfig, b as logger, d as parseJsDocMetadata, g as getConfig, o as extractFrontmatter, p as resolveUiRoot, v as getProjectInfo, x as highlighter, y as spinner } from "./utils-BXyPCddm.js";
3
3
  import { Command } from "commander";
4
4
  import { createRequire } from "module";
5
5
  import path from "path";
@@ -405,8 +405,7 @@ function resolveAliasPathUsingTsConfig(inputPath, projectRoot) {
405
405
  const wildcard = match[1] || "";
406
406
  const first = Array.isArray(values) ? values[0] : values;
407
407
  if (!first) continue;
408
- const target = String(first).replace(/\*/g, wildcard);
409
- return resolve(projectRoot, baseUrl, target);
408
+ return resolve(projectRoot, baseUrl, String(first).replace(/\*/g, wildcard));
410
409
  }
411
410
  return null;
412
411
  }
@@ -468,30 +467,27 @@ async function generateDocs({ cwd, outDir, verbose }) {
468
467
  }
469
468
  if (!process.env.BEJAMAS_DOCS_CWD) process.env.BEJAMAS_DOCS_CWD = shellCwd;
470
469
  if (!process.env.BEJAMAS_UI_ROOT) {
471
- const defaultGuess = (() => {
472
- let current = shellCwd;
473
- for (let i = 0; i < 6; i += 1) {
474
- const cand = resolve(current, "packages/ui/package.json");
475
- if (existsSync(cand)) {
476
- const abs = resolve(current, "packages/ui");
477
- return relative$1(shellCwd, abs) || abs;
478
- }
479
- const parent = resolve(current, "..");
480
- if (parent === current) break;
481
- current = parent;
482
- }
483
- const nm = resolve(shellCwd, "node_modules/@bejamas/ui/package.json");
484
- if (existsSync(nm)) {
485
- const abs = resolve(shellCwd, "node_modules/@bejamas/ui");
486
- return relative$1(shellCwd, abs) || abs;
487
- }
488
- return "packages/ui";
489
- })();
490
470
  const { uiRoot } = await prompts({
491
471
  type: "text",
492
472
  name: "uiRoot",
493
473
  message: "Path to @bejamas/ui package root:",
494
- initial: defaultGuess,
474
+ initial: (() => {
475
+ let current = shellCwd;
476
+ for (let i = 0; i < 6; i += 1) {
477
+ if (existsSync(resolve(current, "packages/ui/package.json"))) {
478
+ const abs = resolve(current, "packages/ui");
479
+ return relative$1(shellCwd, abs) || abs;
480
+ }
481
+ const parent = resolve(current, "..");
482
+ if (parent === current) break;
483
+ current = parent;
484
+ }
485
+ if (existsSync(resolve(shellCwd, "node_modules/@bejamas/ui/package.json"))) {
486
+ const abs = resolve(shellCwd, "node_modules/@bejamas/ui");
487
+ return relative$1(shellCwd, abs) || abs;
488
+ }
489
+ return "packages/ui";
490
+ })(),
495
491
  validate: (val) => {
496
492
  const p = resolve(shellCwd, val);
497
493
  return existsSync(resolve(p, "package.json")) ? true : `No package.json found in ${p}`;
@@ -527,7 +523,7 @@ async function generateDocs({ cwd, outDir, verbose }) {
527
523
  if (process.env.BEJAMAS_DOCS_CWD) logger.info(`Docs CWD: ${process.env.BEJAMAS_DOCS_CWD}`);
528
524
  if (process.env.BEJAMAS_DOCS_OUT_DIR) logger.info(`Docs out: ${process.env.BEJAMAS_DOCS_OUT_DIR}`);
529
525
  }
530
- const mod = await import("./generate-mdx-Csh3BuQB.js");
526
+ const mod = await import("./generate-mdx-2KXXUsF1.js");
531
527
  if (typeof mod.runDocsGenerator === "function") await mod.runDocsGenerator();
532
528
  else throw new Error("Failed to load docs generator. Export 'runDocsGenerator' not found.");
533
529
  } catch (err) {
@@ -564,9 +560,7 @@ const FIELD_LABELS = {
564
560
  figmaUrl: "@figmaUrl"
565
561
  };
566
562
  function checkComponentDocs(filePath, fileName) {
567
- const content = readFileSync(filePath, "utf-8");
568
- const frontmatter = extractFrontmatter(content);
569
- const meta = parseJsDocMetadata(frontmatter);
563
+ const meta = parseJsDocMetadata(extractFrontmatter(readFileSync(filePath, "utf-8")));
570
564
  const componentName = fileName.replace(/\.astro$/i, "");
571
565
  const missingRequired = [];
572
566
  const missingRecommended = [];
@@ -647,8 +641,7 @@ async function checkDocs({ cwd, json }) {
647
641
  }
648
642
  const results = [];
649
643
  for (const file of files) {
650
- const filePath = join$1(componentsDir, file);
651
- const status = checkComponentDocs(filePath, file);
644
+ const status = checkComponentDocs(join$1(componentsDir, file), file);
652
645
  results.push(status);
653
646
  }
654
647
  const complete = results.filter((r) => r.status === "complete");
@@ -770,6 +763,116 @@ async function fixAstroImports(cwd, isVerbose) {
770
763
  }
771
764
  }
772
765
 
766
+ //#endregion
767
+ //#region src/utils/reorganize-components.ts
768
+ /**
769
+ * Fetches a registry item JSON from the registry URL.
770
+ */
771
+ async function fetchRegistryItem(componentName, registryUrl) {
772
+ const url = `${registryUrl}/styles/new-york-v4/${componentName}.json`;
773
+ try {
774
+ const response = await fetch(url);
775
+ if (!response.ok) {
776
+ const fallbackUrl = `${registryUrl}/${componentName}.json`;
777
+ const fallbackResponse = await fetch(fallbackUrl);
778
+ if (!fallbackResponse.ok) return null;
779
+ return await fallbackResponse.json();
780
+ }
781
+ return await response.json();
782
+ } catch {
783
+ return null;
784
+ }
785
+ }
786
+ /**
787
+ * Extracts the subfolder name from registry file paths.
788
+ * E.g., "components/ui/avatar/Avatar.astro" → "avatar"
789
+ */
790
+ function getSubfolderFromPaths(files) {
791
+ const uiFiles = files.filter((f) => f.type === "registry:ui");
792
+ if (uiFiles.length < 2) return null;
793
+ const subfolders = /* @__PURE__ */ new Set();
794
+ for (const file of uiFiles) {
795
+ const parts = file.path.split("/");
796
+ const uiIndex = parts.indexOf("ui");
797
+ if (uiIndex !== -1 && parts.length > uiIndex + 2) subfolders.add(parts[uiIndex + 1]);
798
+ }
799
+ if (subfolders.size === 1) return Array.from(subfolders)[0];
800
+ if (uiFiles.length > 0) {
801
+ const firstPath = uiFiles[0].path;
802
+ const dirname$1 = path$1.dirname(firstPath);
803
+ const folderName = path$1.basename(dirname$1);
804
+ if (folderName && folderName !== "ui") return folderName;
805
+ }
806
+ return null;
807
+ }
808
+ /**
809
+ * Checks if a path exists.
810
+ */
811
+ async function pathExists(filePath) {
812
+ try {
813
+ await fs.access(filePath);
814
+ return true;
815
+ } catch {
816
+ return false;
817
+ }
818
+ }
819
+ /**
820
+ * Reorganizes multi-file components into correct subfolders.
821
+ * Only moves files from FLAT location to subfolder.
822
+ * Does NOT touch files already in subfolders.
823
+ * E.g., moves `uiDir/Avatar.astro` to `uiDir/avatar/Avatar.astro`.
824
+ * Returns info about moved files for display purposes.
825
+ */
826
+ async function reorganizeComponents(components, uiDir, registryUrl, verbose) {
827
+ const result = {
828
+ totalMoved: 0,
829
+ movedFiles: [],
830
+ skippedFiles: []
831
+ };
832
+ if (!uiDir || components.length === 0) return result;
833
+ for (const componentName of components) try {
834
+ const registryItem = await fetchRegistryItem(componentName, registryUrl);
835
+ if (!registryItem) {
836
+ if (verbose) logger.info(`[bejamas-ui] Could not fetch registry for ${componentName}, skipping reorganization`);
837
+ continue;
838
+ }
839
+ const subfolder = getSubfolderFromPaths(registryItem.files);
840
+ if (!subfolder) {
841
+ if (verbose) logger.info(`[bejamas-ui] ${componentName} is single-file or has no subfolder, skipping`);
842
+ continue;
843
+ }
844
+ const uiFiles = registryItem.files.filter((f) => f.type === "registry:ui");
845
+ const targetDir = path$1.join(uiDir, subfolder);
846
+ let movedCount = 0;
847
+ for (const file of uiFiles) {
848
+ const filename = path$1.basename(file.path);
849
+ const flatPath = path$1.join(uiDir, filename);
850
+ const targetPath = path$1.join(targetDir, filename);
851
+ if (!await pathExists(flatPath)) continue;
852
+ if (await pathExists(targetPath)) {
853
+ try {
854
+ await fs.unlink(flatPath);
855
+ result.skippedFiles.push(`${subfolder}/${filename}`);
856
+ if (verbose) logger.info(`[bejamas-ui] Removed flat duplicate: ${filename} (${subfolder}/${filename} exists)`);
857
+ } catch {
858
+ result.skippedFiles.push(`${subfolder}/${filename}`);
859
+ }
860
+ continue;
861
+ }
862
+ await fs.mkdir(targetDir, { recursive: true });
863
+ await fs.rename(flatPath, targetPath);
864
+ movedCount++;
865
+ result.totalMoved++;
866
+ result.movedFiles.push(`${subfolder}/${filename}`);
867
+ if (verbose) logger.info(`[bejamas-ui] Moved ${filename} → ${subfolder}/${filename}`);
868
+ }
869
+ if (movedCount > 0 && verbose) logger.info(`[bejamas-ui] Reorganized ${componentName} into ${subfolder}/`);
870
+ } catch (err) {
871
+ if (verbose) logger.warn(`[bejamas-ui] Failed to reorganize ${componentName}: ${err}`);
872
+ }
873
+ return result;
874
+ }
875
+
773
876
  //#endregion
774
877
  //#region src/commands/add.ts
775
878
  const DEFAULT_REGISTRY_URL = "https://ui.bejamas.com/r";
@@ -836,16 +939,110 @@ function extractOptionsForShadcn(rawArgv, cmd) {
836
939
  }
837
940
  return forwarded;
838
941
  }
839
- async function addComponents(packages, forwardedOptions, isVerbose) {
942
+ /** Build maps for path rewriting, handling filename collisions */
943
+ async function buildSubfolderMap(components, registryUrl) {
944
+ const filenameToSubfolders = /* @__PURE__ */ new Map();
945
+ const componentInfo = /* @__PURE__ */ new Map();
946
+ for (const componentName of components) {
947
+ const registryItem = await fetchRegistryItem(componentName, registryUrl);
948
+ if (!registryItem) continue;
949
+ const subfolder = getSubfolderFromPaths(registryItem.files);
950
+ if (!subfolder) continue;
951
+ const files = [];
952
+ for (const file of registryItem.files) if (file.type === "registry:ui") {
953
+ const filename = path$1.basename(file.path);
954
+ files.push(filename);
955
+ const subfolders = filenameToSubfolders.get(filename) || [];
956
+ subfolders.push(subfolder);
957
+ filenameToSubfolders.set(filename, subfolders);
958
+ }
959
+ componentInfo.set(subfolder, {
960
+ subfolder,
961
+ files
962
+ });
963
+ }
964
+ const uniqueMap = /* @__PURE__ */ new Map();
965
+ const sharedFilenames = /* @__PURE__ */ new Set();
966
+ filenameToSubfolders.forEach((subfolders, filename) => {
967
+ if (subfolders.length === 1) uniqueMap.set(filename, `${subfolders[0]}/${filename}`);
968
+ else sharedFilenames.add(filename);
969
+ });
970
+ return {
971
+ uniqueMap,
972
+ componentInfo,
973
+ sharedFilenames
974
+ };
975
+ }
976
+ /**
977
+ * Rewrite file paths to include correct subfolders.
978
+ * Handles shared filenames (like index.ts) by tracking current component context.
979
+ */
980
+ function rewritePaths(paths, mapResult) {
981
+ const { uniqueMap, componentInfo, sharedFilenames } = mapResult;
982
+ let currentSubfolder = null;
983
+ return paths.map((filePath) => {
984
+ const filename = path$1.basename(filePath);
985
+ const parentDir = path$1.basename(path$1.dirname(filePath));
986
+ const uniqueMapping = uniqueMap.get(filename);
987
+ if (uniqueMapping) {
988
+ const expectedSubfolder = path$1.dirname(uniqueMapping);
989
+ currentSubfolder = expectedSubfolder;
990
+ if (parentDir !== expectedSubfolder) return `${path$1.dirname(filePath)}/${uniqueMapping}`;
991
+ return filePath;
992
+ }
993
+ if (sharedFilenames.has(filename) && currentSubfolder) {
994
+ const expectedSubfolder = currentSubfolder;
995
+ if (parentDir !== expectedSubfolder) return `${path$1.dirname(filePath)}/${expectedSubfolder}/${filename}`;
996
+ }
997
+ return filePath;
998
+ });
999
+ }
1000
+ /** Parse shadcn output to extract file lists (stdout has paths, stderr has headers) */
1001
+ function parseShadcnOutput(stdout, stderr) {
1002
+ const result = {
1003
+ created: [],
1004
+ updated: [],
1005
+ skipped: []
1006
+ };
1007
+ const cleanStderr = stderr.replace(/\x1b\[[0-9;]*m/g, "");
1008
+ const cleanStdout = stdout.replace(/\x1b\[[0-9;]*m/g, "");
1009
+ const createdMatch = cleanStderr.match(/Created\s+(\d+)\s+file/i);
1010
+ const updatedMatch = cleanStderr.match(/Updated\s+(\d+)\s+file/i);
1011
+ const skippedMatch = cleanStderr.match(/Skipped\s+(\d+)\s+file/i);
1012
+ const createdCount = createdMatch ? parseInt(createdMatch[1], 10) : 0;
1013
+ const updatedCount = updatedMatch ? parseInt(updatedMatch[1], 10) : 0;
1014
+ const skippedCount = skippedMatch ? parseInt(skippedMatch[1], 10) : 0;
1015
+ const allPaths = [];
1016
+ for (const line of cleanStdout.split("\n")) {
1017
+ const match = line.match(/^\s+-\s+(.+)$/);
1018
+ if (match) allPaths.push(match[1].trim());
1019
+ }
1020
+ for (const line of cleanStderr.split("\n")) {
1021
+ const match = line.match(/^\s+-\s+(.+)$/);
1022
+ if (match) {
1023
+ const filePath = match[1].trim();
1024
+ if (!allPaths.includes(filePath)) allPaths.push(filePath);
1025
+ }
1026
+ }
1027
+ let idx = 0;
1028
+ for (let i = 0; i < createdCount && idx < allPaths.length; i++) result.created.push(allPaths[idx++]);
1029
+ for (let i = 0; i < updatedCount && idx < allPaths.length; i++) result.updated.push(allPaths[idx++]);
1030
+ for (let i = 0; i < skippedCount && idx < allPaths.length; i++) result.skipped.push(allPaths[idx++]);
1031
+ return result;
1032
+ }
1033
+ async function addComponents(packages, forwardedOptions, isVerbose, isSilent, subfolderMapResult) {
840
1034
  const runner = await getPackageRunner(process.cwd());
841
1035
  const env = {
842
1036
  ...process.env,
843
1037
  REGISTRY_URL: process.env.REGISTRY_URL || DEFAULT_REGISTRY_URL
844
1038
  };
1039
+ const autoFlags = [];
1040
+ if (!forwardedOptions.includes("--yes")) autoFlags.push("--yes");
845
1041
  const baseArgs = [
846
1042
  "shadcn@latest",
847
1043
  "add",
848
1044
  ...packages,
1045
+ ...autoFlags,
849
1046
  ...forwardedOptions
850
1047
  ];
851
1048
  let cmd = "npx";
@@ -861,22 +1058,93 @@ async function addComponents(packages, forwardedOptions, isVerbose) {
861
1058
  args = ["-y", ...baseArgs];
862
1059
  }
863
1060
  if (isVerbose) logger.info(`[bejamas-ui] ${cmd} ${args.join(" ")}`);
1061
+ const registrySpinner = spinner("Checking registry.", { silent: isSilent });
1062
+ registrySpinner.start();
864
1063
  try {
865
- await execa(cmd, args, {
866
- stdio: "inherit",
867
- env
1064
+ const result = await execa(cmd, args, {
1065
+ env,
1066
+ input: "n\nn\nn\nn\nn\nn\nn\nn\nn\nn\n",
1067
+ stdout: "pipe",
1068
+ stderr: "pipe",
1069
+ reject: false
868
1070
  });
1071
+ registrySpinner.succeed();
1072
+ spinner("Installing components.", { silent: isSilent }).succeed();
1073
+ const stdout = result.stdout || "";
1074
+ const stderr = result.stderr || "";
1075
+ if (isVerbose) {
1076
+ logger.info(`[bejamas-ui] Raw stdout: ${stdout}`);
1077
+ logger.info(`[bejamas-ui] Raw stderr: ${stderr}`);
1078
+ }
1079
+ const parsed = parseShadcnOutput(stdout, stderr);
1080
+ if (result.exitCode !== 0) {
1081
+ if (result.stderr) logger.error(result.stderr);
1082
+ process.exit(result.exitCode);
1083
+ }
1084
+ return parsed;
869
1085
  } catch (err) {
1086
+ registrySpinner.fail();
1087
+ logger.error("Failed to add components");
870
1088
  process.exit(1);
871
1089
  }
872
1090
  }
873
1091
  const add = new Command().name("add").description("Add components via shadcn@latest using registry URLs").argument("[components...]", "Component package names to add").option("-y, --yes", "skip confirmation prompt.", false).option("-o, --overwrite", "overwrite existing files.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-a, --all", "add all available components", false).option("-p, --path <path>", "the path to add the component to.").option("-s, --silent", "mute output.", false).option("--src-dir", "use the src directory when creating a new project.", false).option("--no-src-dir", "do not use the src directory when creating a new project.").action(async function action(packages, _opts, cmd) {
874
1092
  const root = cmd?.parent;
875
1093
  const verbose = Boolean(root?.opts?.().verbose);
876
- const rawArgv = process.argv.slice(2);
877
- const forwardedOptions = extractOptionsForShadcn(rawArgv, cmd);
878
- const cwd = (typeof cmd.optsWithGlobals === "function" ? cmd.optsWithGlobals() : cmd.opts?.() ?? {}).cwd || process.cwd();
879
- await addComponents(packages || [], forwardedOptions, verbose);
1094
+ const forwardedOptions = extractOptionsForShadcn(process.argv.slice(2), cmd);
1095
+ const opts = typeof cmd.optsWithGlobals === "function" ? cmd.optsWithGlobals() : cmd.opts?.() ?? {};
1096
+ const cwd = opts.cwd || process.cwd();
1097
+ const config = await getConfig(cwd);
1098
+ const registryUrl = process.env.REGISTRY_URL || DEFAULT_REGISTRY_URL;
1099
+ let uiDir = config?.resolvedPaths?.ui || "";
1100
+ let uiConfig = config;
1101
+ if (config) {
1102
+ const workspaceConfig = await getWorkspaceConfig(config);
1103
+ if (workspaceConfig?.ui) {
1104
+ uiConfig = workspaceConfig.ui;
1105
+ uiDir = uiConfig.resolvedPaths?.ui || uiDir;
1106
+ }
1107
+ }
1108
+ if (verbose) {
1109
+ logger.info(`[bejamas-ui] cwd: ${cwd}`);
1110
+ logger.info(`[bejamas-ui] uiDir: ${uiDir}`);
1111
+ logger.info(`[bejamas-ui] aliases.ui: ${uiConfig?.aliases?.ui || "not set"}`);
1112
+ }
1113
+ const isSilent = opts.silent || false;
1114
+ const componentsToAdd = packages || [];
1115
+ const totalComponents = componentsToAdd.length;
1116
+ for (let i = 0; i < componentsToAdd.length; i++) {
1117
+ const component = componentsToAdd[i];
1118
+ if (totalComponents > 1 && !isSilent) {
1119
+ logger.break();
1120
+ logger.info(highlighter.info(`[${i + 1}/${totalComponents}]`) + ` Adding ${highlighter.success(component)}...`);
1121
+ }
1122
+ const subfolderMapResult = await buildSubfolderMap([component], registryUrl);
1123
+ const parsed = await addComponents([component], forwardedOptions, verbose, isSilent, subfolderMapResult);
1124
+ let skippedCount = 0;
1125
+ if (uiDir) skippedCount = (await reorganizeComponents([component], uiDir, registryUrl, verbose)).skippedFiles.length;
1126
+ if (!isSilent) {
1127
+ uiDir && path$1.relative(cwd, uiDir);
1128
+ const actuallyCreated = Math.max(0, parsed.created.length - skippedCount);
1129
+ if (actuallyCreated > 0) {
1130
+ const createdPaths = rewritePaths(parsed.created.slice(0, actuallyCreated), subfolderMapResult);
1131
+ logger.success(`Created ${createdPaths.length} file${createdPaths.length > 1 ? "s" : ""}:`);
1132
+ for (const file of createdPaths) logger.log(` ${highlighter.info("-")} ${file}`);
1133
+ }
1134
+ if (parsed.updated.length > 0) {
1135
+ const updatedPaths = rewritePaths(Array.from(new Set(parsed.updated)), subfolderMapResult);
1136
+ logger.info(`Updated ${updatedPaths.length} file${updatedPaths.length > 1 ? "s" : ""}:`);
1137
+ for (const file of updatedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
1138
+ }
1139
+ if (skippedCount > 0) logger.info(`Skipped ${skippedCount} file${skippedCount > 1 ? "s" : ""}: (already exists)`);
1140
+ if (parsed.skipped.length > 0) {
1141
+ const skippedPaths = rewritePaths(parsed.skipped, subfolderMapResult);
1142
+ logger.info(`Skipped ${skippedPaths.length} file${skippedPaths.length > 1 ? "s" : ""}: (use --overwrite)`);
1143
+ for (const file of skippedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
1144
+ }
1145
+ if (actuallyCreated === 0 && parsed.updated.length === 0 && skippedCount === 0 && parsed.skipped.length === 0) logger.info("Already up to date.");
1146
+ }
1147
+ }
880
1148
  await fixAstroImports(cwd, verbose);
881
1149
  });
882
1150