bejamas 0.2.0 → 0.2.2
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/{generate-mdx-5r1bKyim.js → generate-mdx-CPI1OKtC.js} +166 -24
- package/dist/generate-mdx-CPI1OKtC.js.map +1 -0
- package/dist/index.js +285 -8
- package/dist/index.js.map +1 -1
- package/dist/{utils-DfvCox_O.js → utils-BA01hKci.js} +47 -3
- package/dist/utils-BA01hKci.js.map +1 -0
- package/package.json +1 -1
- package/dist/generate-mdx-5r1bKyim.js.map +0 -1
- package/dist/utils-DfvCox_O.js.map +0 -1
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-
|
|
2
|
+
import { BASE_COLORS, extractFrontmatter, getConfig, getProjectInfo, getWorkspaceConfig, highlighter, logger, parseJsDocMetadata, resolveUiRoot, spinner } from "./utils-BA01hKci.js";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
5
|
import path from "path";
|
|
@@ -527,7 +527,7 @@ async function generateDocs({ cwd, outDir, verbose }) {
|
|
|
527
527
|
if (process.env.BEJAMAS_DOCS_CWD) logger.info(`Docs CWD: ${process.env.BEJAMAS_DOCS_CWD}`);
|
|
528
528
|
if (process.env.BEJAMAS_DOCS_OUT_DIR) logger.info(`Docs out: ${process.env.BEJAMAS_DOCS_OUT_DIR}`);
|
|
529
529
|
}
|
|
530
|
-
const mod = await import("./generate-mdx-
|
|
530
|
+
const mod = await import("./generate-mdx-CPI1OKtC.js");
|
|
531
531
|
if (typeof mod.runDocsGenerator === "function") await mod.runDocsGenerator();
|
|
532
532
|
else throw new Error("Failed to load docs generator. Export 'runDocsGenerator' not found.");
|
|
533
533
|
} catch (err) {
|
|
@@ -770,6 +770,116 @@ async function fixAstroImports(cwd, isVerbose) {
|
|
|
770
770
|
}
|
|
771
771
|
}
|
|
772
772
|
|
|
773
|
+
//#endregion
|
|
774
|
+
//#region src/utils/reorganize-components.ts
|
|
775
|
+
/**
|
|
776
|
+
* Fetches a registry item JSON from the registry URL.
|
|
777
|
+
*/
|
|
778
|
+
async function fetchRegistryItem(componentName, registryUrl) {
|
|
779
|
+
const url = `${registryUrl}/styles/new-york-v4/${componentName}.json`;
|
|
780
|
+
try {
|
|
781
|
+
const response = await fetch(url);
|
|
782
|
+
if (!response.ok) {
|
|
783
|
+
const fallbackUrl = `${registryUrl}/${componentName}.json`;
|
|
784
|
+
const fallbackResponse = await fetch(fallbackUrl);
|
|
785
|
+
if (!fallbackResponse.ok) return null;
|
|
786
|
+
return await fallbackResponse.json();
|
|
787
|
+
}
|
|
788
|
+
return await response.json();
|
|
789
|
+
} catch {
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Extracts the subfolder name from registry file paths.
|
|
795
|
+
* E.g., "components/ui/avatar/Avatar.astro" → "avatar"
|
|
796
|
+
*/
|
|
797
|
+
function getSubfolderFromPaths(files) {
|
|
798
|
+
const uiFiles = files.filter((f) => f.type === "registry:ui");
|
|
799
|
+
if (uiFiles.length < 2) return null;
|
|
800
|
+
const subfolders = /* @__PURE__ */ new Set();
|
|
801
|
+
for (const file of uiFiles) {
|
|
802
|
+
const parts = file.path.split("/");
|
|
803
|
+
const uiIndex = parts.indexOf("ui");
|
|
804
|
+
if (uiIndex !== -1 && parts.length > uiIndex + 2) subfolders.add(parts[uiIndex + 1]);
|
|
805
|
+
}
|
|
806
|
+
if (subfolders.size === 1) return Array.from(subfolders)[0];
|
|
807
|
+
if (uiFiles.length > 0) {
|
|
808
|
+
const firstPath = uiFiles[0].path;
|
|
809
|
+
const dirname$1 = path$1.dirname(firstPath);
|
|
810
|
+
const folderName = path$1.basename(dirname$1);
|
|
811
|
+
if (folderName && folderName !== "ui") return folderName;
|
|
812
|
+
}
|
|
813
|
+
return null;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Checks if a path exists.
|
|
817
|
+
*/
|
|
818
|
+
async function pathExists(filePath) {
|
|
819
|
+
try {
|
|
820
|
+
await fs.access(filePath);
|
|
821
|
+
return true;
|
|
822
|
+
} catch {
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Reorganizes multi-file components into correct subfolders.
|
|
828
|
+
* Only moves files from FLAT location to subfolder.
|
|
829
|
+
* Does NOT touch files already in subfolders.
|
|
830
|
+
* E.g., moves `uiDir/Avatar.astro` to `uiDir/avatar/Avatar.astro`.
|
|
831
|
+
* Returns info about moved files for display purposes.
|
|
832
|
+
*/
|
|
833
|
+
async function reorganizeComponents(components, uiDir, registryUrl, verbose) {
|
|
834
|
+
const result = {
|
|
835
|
+
totalMoved: 0,
|
|
836
|
+
movedFiles: [],
|
|
837
|
+
skippedFiles: []
|
|
838
|
+
};
|
|
839
|
+
if (!uiDir || components.length === 0) return result;
|
|
840
|
+
for (const componentName of components) try {
|
|
841
|
+
const registryItem = await fetchRegistryItem(componentName, registryUrl);
|
|
842
|
+
if (!registryItem) {
|
|
843
|
+
if (verbose) logger.info(`[bejamas-ui] Could not fetch registry for ${componentName}, skipping reorganization`);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
const subfolder = getSubfolderFromPaths(registryItem.files);
|
|
847
|
+
if (!subfolder) {
|
|
848
|
+
if (verbose) logger.info(`[bejamas-ui] ${componentName} is single-file or has no subfolder, skipping`);
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const uiFiles = registryItem.files.filter((f) => f.type === "registry:ui");
|
|
852
|
+
const targetDir = path$1.join(uiDir, subfolder);
|
|
853
|
+
let movedCount = 0;
|
|
854
|
+
for (const file of uiFiles) {
|
|
855
|
+
const filename = path$1.basename(file.path);
|
|
856
|
+
const flatPath = path$1.join(uiDir, filename);
|
|
857
|
+
const targetPath = path$1.join(targetDir, filename);
|
|
858
|
+
if (!await pathExists(flatPath)) continue;
|
|
859
|
+
if (await pathExists(targetPath)) {
|
|
860
|
+
try {
|
|
861
|
+
await fs.unlink(flatPath);
|
|
862
|
+
result.skippedFiles.push(`${subfolder}/${filename}`);
|
|
863
|
+
if (verbose) logger.info(`[bejamas-ui] Removed flat duplicate: ${filename} (${subfolder}/${filename} exists)`);
|
|
864
|
+
} catch {
|
|
865
|
+
result.skippedFiles.push(`${subfolder}/${filename}`);
|
|
866
|
+
}
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
870
|
+
await fs.rename(flatPath, targetPath);
|
|
871
|
+
movedCount++;
|
|
872
|
+
result.totalMoved++;
|
|
873
|
+
result.movedFiles.push(`${subfolder}/${filename}`);
|
|
874
|
+
if (verbose) logger.info(`[bejamas-ui] Moved ${filename} → ${subfolder}/${filename}`);
|
|
875
|
+
}
|
|
876
|
+
if (movedCount > 0 && verbose) logger.info(`[bejamas-ui] Reorganized ${componentName} into ${subfolder}/`);
|
|
877
|
+
} catch (err) {
|
|
878
|
+
if (verbose) logger.warn(`[bejamas-ui] Failed to reorganize ${componentName}: ${err}`);
|
|
879
|
+
}
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
882
|
+
|
|
773
883
|
//#endregion
|
|
774
884
|
//#region src/commands/add.ts
|
|
775
885
|
const DEFAULT_REGISTRY_URL = "https://ui.bejamas.com/r";
|
|
@@ -836,16 +946,110 @@ function extractOptionsForShadcn(rawArgv, cmd) {
|
|
|
836
946
|
}
|
|
837
947
|
return forwarded;
|
|
838
948
|
}
|
|
839
|
-
|
|
949
|
+
/** Build maps for path rewriting, handling filename collisions */
|
|
950
|
+
async function buildSubfolderMap(components, registryUrl) {
|
|
951
|
+
const filenameToSubfolders = /* @__PURE__ */ new Map();
|
|
952
|
+
const componentInfo = /* @__PURE__ */ new Map();
|
|
953
|
+
for (const componentName of components) {
|
|
954
|
+
const registryItem = await fetchRegistryItem(componentName, registryUrl);
|
|
955
|
+
if (!registryItem) continue;
|
|
956
|
+
const subfolder = getSubfolderFromPaths(registryItem.files);
|
|
957
|
+
if (!subfolder) continue;
|
|
958
|
+
const files = [];
|
|
959
|
+
for (const file of registryItem.files) if (file.type === "registry:ui") {
|
|
960
|
+
const filename = path$1.basename(file.path);
|
|
961
|
+
files.push(filename);
|
|
962
|
+
const subfolders = filenameToSubfolders.get(filename) || [];
|
|
963
|
+
subfolders.push(subfolder);
|
|
964
|
+
filenameToSubfolders.set(filename, subfolders);
|
|
965
|
+
}
|
|
966
|
+
componentInfo.set(subfolder, {
|
|
967
|
+
subfolder,
|
|
968
|
+
files
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
const uniqueMap = /* @__PURE__ */ new Map();
|
|
972
|
+
const sharedFilenames = /* @__PURE__ */ new Set();
|
|
973
|
+
filenameToSubfolders.forEach((subfolders, filename) => {
|
|
974
|
+
if (subfolders.length === 1) uniqueMap.set(filename, `${subfolders[0]}/${filename}`);
|
|
975
|
+
else sharedFilenames.add(filename);
|
|
976
|
+
});
|
|
977
|
+
return {
|
|
978
|
+
uniqueMap,
|
|
979
|
+
componentInfo,
|
|
980
|
+
sharedFilenames
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Rewrite file paths to include correct subfolders.
|
|
985
|
+
* Handles shared filenames (like index.ts) by tracking current component context.
|
|
986
|
+
*/
|
|
987
|
+
function rewritePaths(paths, mapResult) {
|
|
988
|
+
const { uniqueMap, componentInfo, sharedFilenames } = mapResult;
|
|
989
|
+
let currentSubfolder = null;
|
|
990
|
+
return paths.map((filePath) => {
|
|
991
|
+
const filename = path$1.basename(filePath);
|
|
992
|
+
const parentDir = path$1.basename(path$1.dirname(filePath));
|
|
993
|
+
const uniqueMapping = uniqueMap.get(filename);
|
|
994
|
+
if (uniqueMapping) {
|
|
995
|
+
const expectedSubfolder = path$1.dirname(uniqueMapping);
|
|
996
|
+
currentSubfolder = expectedSubfolder;
|
|
997
|
+
if (parentDir !== expectedSubfolder) return `${path$1.dirname(filePath)}/${uniqueMapping}`;
|
|
998
|
+
return filePath;
|
|
999
|
+
}
|
|
1000
|
+
if (sharedFilenames.has(filename) && currentSubfolder) {
|
|
1001
|
+
const expectedSubfolder = currentSubfolder;
|
|
1002
|
+
if (parentDir !== expectedSubfolder) return `${path$1.dirname(filePath)}/${expectedSubfolder}/${filename}`;
|
|
1003
|
+
}
|
|
1004
|
+
return filePath;
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
/** Parse shadcn output to extract file lists (stdout has paths, stderr has headers) */
|
|
1008
|
+
function parseShadcnOutput(stdout, stderr) {
|
|
1009
|
+
const result = {
|
|
1010
|
+
created: [],
|
|
1011
|
+
updated: [],
|
|
1012
|
+
skipped: []
|
|
1013
|
+
};
|
|
1014
|
+
const cleanStderr = stderr.replace(/\x1b\[[0-9;]*m/g, "");
|
|
1015
|
+
const cleanStdout = stdout.replace(/\x1b\[[0-9;]*m/g, "");
|
|
1016
|
+
const createdMatch = cleanStderr.match(/Created\s+(\d+)\s+file/i);
|
|
1017
|
+
const updatedMatch = cleanStderr.match(/Updated\s+(\d+)\s+file/i);
|
|
1018
|
+
const skippedMatch = cleanStderr.match(/Skipped\s+(\d+)\s+file/i);
|
|
1019
|
+
const createdCount = createdMatch ? parseInt(createdMatch[1], 10) : 0;
|
|
1020
|
+
const updatedCount = updatedMatch ? parseInt(updatedMatch[1], 10) : 0;
|
|
1021
|
+
const skippedCount = skippedMatch ? parseInt(skippedMatch[1], 10) : 0;
|
|
1022
|
+
const allPaths = [];
|
|
1023
|
+
for (const line of cleanStdout.split("\n")) {
|
|
1024
|
+
const match = line.match(/^\s+-\s+(.+)$/);
|
|
1025
|
+
if (match) allPaths.push(match[1].trim());
|
|
1026
|
+
}
|
|
1027
|
+
for (const line of cleanStderr.split("\n")) {
|
|
1028
|
+
const match = line.match(/^\s+-\s+(.+)$/);
|
|
1029
|
+
if (match) {
|
|
1030
|
+
const filePath = match[1].trim();
|
|
1031
|
+
if (!allPaths.includes(filePath)) allPaths.push(filePath);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
let idx = 0;
|
|
1035
|
+
for (let i = 0; i < createdCount && idx < allPaths.length; i++) result.created.push(allPaths[idx++]);
|
|
1036
|
+
for (let i = 0; i < updatedCount && idx < allPaths.length; i++) result.updated.push(allPaths[idx++]);
|
|
1037
|
+
for (let i = 0; i < skippedCount && idx < allPaths.length; i++) result.skipped.push(allPaths[idx++]);
|
|
1038
|
+
return result;
|
|
1039
|
+
}
|
|
1040
|
+
async function addComponents(packages, forwardedOptions, isVerbose, isSilent, subfolderMapResult) {
|
|
840
1041
|
const runner = await getPackageRunner(process.cwd());
|
|
841
1042
|
const env = {
|
|
842
1043
|
...process.env,
|
|
843
1044
|
REGISTRY_URL: process.env.REGISTRY_URL || DEFAULT_REGISTRY_URL
|
|
844
1045
|
};
|
|
1046
|
+
const autoFlags = [];
|
|
1047
|
+
if (!forwardedOptions.includes("--yes")) autoFlags.push("--yes");
|
|
845
1048
|
const baseArgs = [
|
|
846
1049
|
"shadcn@latest",
|
|
847
1050
|
"add",
|
|
848
1051
|
...packages,
|
|
1052
|
+
...autoFlags,
|
|
849
1053
|
...forwardedOptions
|
|
850
1054
|
];
|
|
851
1055
|
let cmd = "npx";
|
|
@@ -861,12 +1065,33 @@ async function addComponents(packages, forwardedOptions, isVerbose) {
|
|
|
861
1065
|
args = ["-y", ...baseArgs];
|
|
862
1066
|
}
|
|
863
1067
|
if (isVerbose) logger.info(`[bejamas-ui] ${cmd} ${args.join(" ")}`);
|
|
1068
|
+
const registrySpinner = spinner("Checking registry.", { silent: isSilent });
|
|
1069
|
+
registrySpinner.start();
|
|
864
1070
|
try {
|
|
865
|
-
await execa(cmd, args, {
|
|
866
|
-
|
|
867
|
-
|
|
1071
|
+
const result = await execa(cmd, args, {
|
|
1072
|
+
env,
|
|
1073
|
+
input: "n\nn\nn\nn\nn\nn\nn\nn\nn\nn\n",
|
|
1074
|
+
stdout: "pipe",
|
|
1075
|
+
stderr: "pipe",
|
|
1076
|
+
reject: false
|
|
868
1077
|
});
|
|
1078
|
+
registrySpinner.succeed();
|
|
1079
|
+
spinner("Installing components.", { silent: isSilent }).succeed();
|
|
1080
|
+
const stdout = result.stdout || "";
|
|
1081
|
+
const stderr = result.stderr || "";
|
|
1082
|
+
if (isVerbose) {
|
|
1083
|
+
logger.info(`[bejamas-ui] Raw stdout: ${stdout}`);
|
|
1084
|
+
logger.info(`[bejamas-ui] Raw stderr: ${stderr}`);
|
|
1085
|
+
}
|
|
1086
|
+
const parsed = parseShadcnOutput(stdout, stderr);
|
|
1087
|
+
if (result.exitCode !== 0) {
|
|
1088
|
+
if (result.stderr) logger.error(result.stderr);
|
|
1089
|
+
process.exit(result.exitCode);
|
|
1090
|
+
}
|
|
1091
|
+
return parsed;
|
|
869
1092
|
} catch (err) {
|
|
1093
|
+
registrySpinner.fail();
|
|
1094
|
+
logger.error("Failed to add components");
|
|
870
1095
|
process.exit(1);
|
|
871
1096
|
}
|
|
872
1097
|
}
|
|
@@ -875,8 +1100,60 @@ const add = new Command().name("add").description("Add components via shadcn@lat
|
|
|
875
1100
|
const verbose = Boolean(root?.opts?.().verbose);
|
|
876
1101
|
const rawArgv = process.argv.slice(2);
|
|
877
1102
|
const forwardedOptions = extractOptionsForShadcn(rawArgv, cmd);
|
|
878
|
-
const
|
|
879
|
-
|
|
1103
|
+
const opts = typeof cmd.optsWithGlobals === "function" ? cmd.optsWithGlobals() : cmd.opts?.() ?? {};
|
|
1104
|
+
const cwd = opts.cwd || process.cwd();
|
|
1105
|
+
const config = await getConfig(cwd);
|
|
1106
|
+
const registryUrl = process.env.REGISTRY_URL || DEFAULT_REGISTRY_URL;
|
|
1107
|
+
let uiDir = config?.resolvedPaths?.ui || "";
|
|
1108
|
+
let uiConfig = config;
|
|
1109
|
+
if (config) {
|
|
1110
|
+
const workspaceConfig = await getWorkspaceConfig(config);
|
|
1111
|
+
if (workspaceConfig?.ui) {
|
|
1112
|
+
uiConfig = workspaceConfig.ui;
|
|
1113
|
+
uiDir = uiConfig.resolvedPaths?.ui || uiDir;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
if (verbose) {
|
|
1117
|
+
logger.info(`[bejamas-ui] cwd: ${cwd}`);
|
|
1118
|
+
logger.info(`[bejamas-ui] uiDir: ${uiDir}`);
|
|
1119
|
+
logger.info(`[bejamas-ui] aliases.ui: ${uiConfig?.aliases?.ui || "not set"}`);
|
|
1120
|
+
}
|
|
1121
|
+
const isSilent = opts.silent || false;
|
|
1122
|
+
const componentsToAdd = packages || [];
|
|
1123
|
+
const totalComponents = componentsToAdd.length;
|
|
1124
|
+
for (let i = 0; i < componentsToAdd.length; i++) {
|
|
1125
|
+
const component = componentsToAdd[i];
|
|
1126
|
+
if (totalComponents > 1 && !isSilent) {
|
|
1127
|
+
logger.break();
|
|
1128
|
+
logger.info(highlighter.info(`[${i + 1}/${totalComponents}]`) + ` Adding ${highlighter.success(component)}...`);
|
|
1129
|
+
}
|
|
1130
|
+
const subfolderMapResult = await buildSubfolderMap([component], registryUrl);
|
|
1131
|
+
const parsed = await addComponents([component], forwardedOptions, verbose, isSilent, subfolderMapResult);
|
|
1132
|
+
let skippedCount = 0;
|
|
1133
|
+
if (uiDir) skippedCount = (await reorganizeComponents([component], uiDir, registryUrl, verbose)).skippedFiles.length;
|
|
1134
|
+
if (!isSilent) {
|
|
1135
|
+
uiDir && path$1.relative(cwd, uiDir);
|
|
1136
|
+
const actuallyCreated = Math.max(0, parsed.created.length - skippedCount);
|
|
1137
|
+
if (actuallyCreated > 0) {
|
|
1138
|
+
const createdPaths = rewritePaths(parsed.created.slice(0, actuallyCreated), subfolderMapResult);
|
|
1139
|
+
logger.success(`Created ${createdPaths.length} file${createdPaths.length > 1 ? "s" : ""}:`);
|
|
1140
|
+
for (const file of createdPaths) logger.log(` ${highlighter.info("-")} ${file}`);
|
|
1141
|
+
}
|
|
1142
|
+
if (parsed.updated.length > 0) {
|
|
1143
|
+
const uniqueUpdated = Array.from(new Set(parsed.updated));
|
|
1144
|
+
const updatedPaths = rewritePaths(uniqueUpdated, subfolderMapResult);
|
|
1145
|
+
logger.info(`Updated ${updatedPaths.length} file${updatedPaths.length > 1 ? "s" : ""}:`);
|
|
1146
|
+
for (const file of updatedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
|
|
1147
|
+
}
|
|
1148
|
+
if (skippedCount > 0) logger.info(`Skipped ${skippedCount} file${skippedCount > 1 ? "s" : ""}: (already exists)`);
|
|
1149
|
+
if (parsed.skipped.length > 0) {
|
|
1150
|
+
const skippedPaths = rewritePaths(parsed.skipped, subfolderMapResult);
|
|
1151
|
+
logger.info(`Skipped ${skippedPaths.length} file${skippedPaths.length > 1 ? "s" : ""}: (use --overwrite)`);
|
|
1152
|
+
for (const file of skippedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
|
|
1153
|
+
}
|
|
1154
|
+
if (actuallyCreated === 0 && parsed.updated.length === 0 && skippedCount === 0 && parsed.skipped.length === 0) logger.info("Already up to date.");
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
880
1157
|
await fixAstroImports(cwd, verbose);
|
|
881
1158
|
});
|
|
882
1159
|
|