bejamas 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as PRESET_RADII, A as getSemanticIconNameFromLucideExport, B as getHeadingFontValue, C as BASE_COLORS, D as buildRegistryTheme, E as ICON_LIBRARIES, F as designSystemConfigSchema, G as DEFAULT_PRESET_CONFIG, H as getStyleDefaultRadius, I as getDocumentDirection, J as PRESET_FONTS, K as PRESET_BASE_COLORS, L as getDocumentLanguage, M as renderSemanticIconSvgWithAttributeString, N as DEFAULT_DESIGN_SYSTEM_CONFIG, O as ICON_LIBRARY_COLLECTIONS, P as RTL_LANGUAGE_VALUES, Q as PRESET_MENU_COLORS, R as getFontPackageName, S as BEJAMAS_COMPONENTS_SCHEMA_URL, T as resolveRegistryUrl, U as getStyleId, V as getRadiusValue, W as normalizeDesignSystemConfig, X as PRESET_ICON_LIBRARIES, Y as PRESET_FONT_HEADINGS, Z as PRESET_MENU_ACCENTS, _ as getConfig, at as isPresetCode, b as logger, d as parseJsDocMetadata, et as PRESET_STYLES, g as spinner, it as encodePreset, j as getSemanticIconNameFromLucidePath, k as SEMANTIC_ICON_NAMES, nt as V1_CHART_COLOR_MAP, o as extractFrontmatter, ot as fonts, p as resolveUiRoot, q as PRESET_CHART_COLORS, rt as decodePreset, tt as PRESET_THEMES, v as getWorkspaceConfig, w as buildUiUrl, x as highlighter, y as getProjectInfo, z as getFontValue } from "./utils-CyYWo_qS.js";
2
+ import { $ as PRESET_MENU_COLORS, A as SEMANTIC_ICON_NAMES, B as getFontValue, C as BEJAMAS_COMPONENTS_SCHEMA_URL, D as ICON_LIBRARIES, E as resolveRegistryUrl, F as RTL_LANGUAGE_VALUES, G as normalizeDesignSystemConfig, H as getRadiusValue, I as designSystemConfigSchema, J as PRESET_CHART_COLORS, K as DEFAULT_PRESET_CONFIG, L as getDocumentDirection, M as getSemanticIconNameFromLucidePath, N as renderSemanticIconSvgWithAttributeString, O as buildRegistryTheme, P as DEFAULT_DESIGN_SYSTEM_CONFIG, Q as PRESET_MENU_ACCENTS, R as getDocumentLanguage, S as highlighter, T as buildUiUrl, U as getStyleDefaultRadius, V as getHeadingFontValue, W as getStyleId, X as PRESET_FONT_HEADINGS, Y as PRESET_FONTS, Z as PRESET_ICON_LIBRARIES, _ as findCommonRoot, at as encodePreset, b as getProjectInfo, d as parseJsDocMetadata, et as PRESET_RADII, g as spinner, it as decodePreset, j as getSemanticIconNameFromLucideExport, k as ICON_LIBRARY_COLLECTIONS, nt as PRESET_THEMES, o as extractFrontmatter, ot as isPresetCode, p as resolveUiRoot, q as PRESET_BASE_COLORS, rt as V1_CHART_COLOR_MAP, st as fonts, tt as PRESET_STYLES, v as getConfig, w as BASE_COLORS, x as logger, y as getWorkspaceConfig, z as getFontPackageName } from "./utils-Dmd3Tydo.js";
3
3
  import { Command } from "commander";
4
4
  import { createRequire } from "module";
5
5
  import path, { extname, isAbsolute, join, relative, resolve } from "node:path";
@@ -18,6 +18,8 @@ import { execa } from "execa";
18
18
  import prompts from "prompts";
19
19
  import * as tar from "tar";
20
20
  import os$1 from "node:os";
21
+ import { stripVTControlCharacters } from "node:util";
22
+ import { StringDecoder } from "node:string_decoder";
21
23
 
22
24
  //#region src/registry/context.ts
23
25
  let context = { headers: {} };
@@ -25,17 +27,12 @@ function clearRegistryContext() {
25
27
  context.headers = {};
26
28
  }
27
29
 
28
- //#endregion
29
- //#region src/utils/errors.ts
30
- const MISSING_DIR_OR_EMPTY_PROJECT = "1";
31
- const MISSING_CONFIG = "3";
32
-
33
30
  //#endregion
34
31
  //#region src/preflights/preflight-init.ts
35
32
  async function preFlightInit(options) {
36
33
  const errors = {};
37
34
  if (!fs.existsSync(options.cwd) || !fs.existsSync(path$1.resolve(options.cwd, "package.json"))) {
38
- errors[MISSING_DIR_OR_EMPTY_PROJECT] = true;
35
+ errors["1"] = true;
39
36
  return {
40
37
  errors,
41
38
  projectInfo: null
@@ -62,8 +59,17 @@ const ASTRO_CONFIG_CANDIDATES = [
62
59
  const ASTRO_LAYOUT_CANDIDATES = ["src/layouts/Layout.astro", "apps/web/src/layouts/Layout.astro"];
63
60
  const STARLIGHT_HEAD_RELATIVE_PATH = "apps/docs/src/components/Head.astro";
64
61
  const ASTRO_FONT_PROVIDERS = ["fontsource", "google"];
65
- const ASTRO_FONTSOURCE_FONT_NAMES = new Set(["geist", "geist-mono"]);
66
- const MANAGED_BODY_FONT_CLASSES = new Set(["font-mono", "font-serif"]);
62
+ const ASTRO_FONTSOURCE_FONT_NAMES = /* @__PURE__ */ new Set(["geist", "geist-mono"]);
63
+ const MANAGED_BODY_FONT_CLASSES = /* @__PURE__ */ new Set(["font-mono", "font-serif"]);
64
+ function parseStringArray(source) {
65
+ if (!source) return void 0;
66
+ try {
67
+ const parsed = JSON.parse(source);
68
+ return Array.isArray(parsed) && parsed.length > 0 && parsed.every((value) => typeof value === "string") ? parsed : void 0;
69
+ } catch {
70
+ return;
71
+ }
72
+ }
67
73
  function compactSource$1(source) {
68
74
  return source.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
69
75
  }
@@ -71,23 +77,25 @@ function escapeRegExp$2(value) {
71
77
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
72
78
  }
73
79
  function upsertImport(source, moduleName, importedName, fallbackNames = []) {
74
- const importPattern = /* @__PURE__ */ new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']${escapeRegExp$2(moduleName)}["'];?`);
80
+ const importPattern = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']${escapeRegExp$2(moduleName)}["'];?`);
75
81
  if (importPattern.test(source)) return source.replace(importPattern, (_match, importsSource) => {
76
82
  const imports = importsSource.split(",").map((value) => value.trim()).filter(Boolean);
77
83
  if (!imports.includes(importedName)) imports.push(importedName);
78
84
  for (const name of fallbackNames) if (!imports.includes(name)) imports.unshift(name);
79
85
  return `import { ${Array.from(new Set(imports)).join(", ")} } from "${moduleName}";`;
80
86
  });
81
- const importLine = `import { ${Array.from(new Set([...fallbackNames, importedName])).join(", ")} } from "${moduleName}";`;
87
+ const importLine = `import { ${Array.from(/* @__PURE__ */ new Set([...fallbackNames, importedName])).join(", ")} } from "${moduleName}";`;
82
88
  const frontmatterEnd = source.indexOf("---", 3);
83
89
  if (source.startsWith("---") && frontmatterEnd !== -1) return `${source.slice(0, frontmatterEnd).trimEnd()}\n${importLine}\n${source.slice(frontmatterEnd)}`;
84
90
  return `${importLine}\n${source}`;
85
91
  }
86
92
  function buildAstroConfigFontBlock(fontsToSerialize) {
87
- return `${ASTRO_CONFIG_BLOCK_START}\n/** @type {NonNullable<import("astro/config").AstroUserConfig["fonts"]>} */\nconst ${ASTRO_FONT_CONSTANT} = [${fontsToSerialize.length === 0 ? "" : `\n${fontsToSerialize.map((font) => ` {\n provider: fontProviders.${font.provider}(),\n name: ${JSON.stringify(font.name)},\n cssVariable: ${JSON.stringify(font.cssVariable)},\n subsets: ${JSON.stringify(font.subsets)},\n },`).join("\n")}\n`}];\n${ASTRO_CONFIG_BLOCK_END}`;
93
+ const body = fontsToSerialize.length === 0 ? "" : `\n${fontsToSerialize.map((font) => ` {\n provider: fontProviders.${font.provider}(),\n name: ${JSON.stringify(font.name)},\n cssVariable: ${JSON.stringify(font.cssVariable)},\n${font.weights ? ` weights: ${JSON.stringify(font.weights)},\n` : ""} subsets: ${JSON.stringify(font.subsets)},\n },`).join("\n")}\n`;
94
+ return `${ASTRO_CONFIG_BLOCK_START}\n/** @type {NonNullable<import("astro").AstroUserConfig["fonts"]>} */\nconst ${ASTRO_FONT_CONSTANT} = [${body}];\n${ASTRO_CONFIG_BLOCK_END}`;
88
95
  }
89
96
  function buildAstroLayoutFontBlock(fontsToSerialize) {
90
- return `${ASTRO_LAYOUT_BLOCK_START}\n${fontsToSerialize.map((font) => `<Font cssVariable="${font.cssVariable}" />`).join("\n")}\n${ASTRO_LAYOUT_BLOCK_END}`;
97
+ const body = fontsToSerialize.map((font) => `<Font cssVariable="${font.cssVariable}" />`).join("\n");
98
+ return `${ASTRO_LAYOUT_BLOCK_START}\n${body}\n${ASTRO_LAYOUT_BLOCK_END}`;
91
99
  }
92
100
  function bodyFontClassFromCssVariable(cssVariable) {
93
101
  if (cssVariable === "--font-mono" || cssVariable === "--font-serif") return fontClassFromCssVariable(cssVariable);
@@ -102,14 +110,14 @@ function upsertManagedBodyFontClass(source, cssVariable) {
102
110
  const classMatch = attrs.match(classPattern);
103
111
  const quote = classMatch?.[1] ?? "\"";
104
112
  const existingClasses = (classMatch?.[2] ?? "").split(/\s+/).filter(Boolean).filter((className) => !MANAGED_BODY_FONT_CLASSES.has(className));
105
- const nextClasses = bodyClass ? Array.from(new Set([...existingClasses, bodyClass])) : existingClasses;
113
+ const nextClasses = bodyClass ? Array.from(/* @__PURE__ */ new Set([...existingClasses, bodyClass])) : existingClasses;
106
114
  let nextAttrs = attrs.replace(classPattern, "");
107
115
  if (nextClasses.length > 0) nextAttrs = `${nextAttrs} class=${quote}${nextClasses.join(" ")}${quote}`;
108
116
  return `<body${nextAttrs}${selfClosing}>`;
109
117
  });
110
118
  }
111
119
  function parseManagedAstroFonts(source) {
112
- const markerPattern = /* @__PURE__ */ new RegExp(`${escapeRegExp$2(ASTRO_CONFIG_BLOCK_START)}[\\s\\S]*?const\\s+${ASTRO_FONT_CONSTANT}\\s*=\\s*\\[([\\s\\S]*?)\\];[\\s\\S]*?${escapeRegExp$2(ASTRO_CONFIG_BLOCK_END)}`);
120
+ const markerPattern = new RegExp(`${escapeRegExp$2(ASTRO_CONFIG_BLOCK_START)}[\\s\\S]*?const\\s+${ASTRO_FONT_CONSTANT}\\s*=\\s*\\[([\\s\\S]*?)\\];[\\s\\S]*?${escapeRegExp$2(ASTRO_CONFIG_BLOCK_END)}`);
113
121
  const match = source.match(markerPattern);
114
122
  if (!match) return [];
115
123
  return (match[1].match(/\{[\s\S]*?\}/g) ?? []).map((entry) => {
@@ -117,19 +125,14 @@ function parseManagedAstroFonts(source) {
117
125
  const cssVariable = entry.match(/cssVariable:\s*"([^"]+)"/)?.[1];
118
126
  const provider = entry.match(/provider:\s*fontProviders\.([a-z]+)\(\)/)?.[1];
119
127
  const subsetsSource = entry.match(/subsets:\s*(\[[^\]]*\])/s)?.[1];
128
+ const weights = parseStringArray(entry.match(/weights:\s*(\[[^\]]*\])/s)?.[1]);
120
129
  if (!name || !cssVariable || !provider || !ASTRO_FONT_PROVIDERS.includes(provider)) return null;
121
- let subsets = ["latin"];
122
- if (subsetsSource) try {
123
- const parsed = JSON.parse(subsetsSource);
124
- if (Array.isArray(parsed) && parsed.length > 0 && parsed.every((value) => typeof value === "string")) subsets = parsed;
125
- } catch {
126
- subsets = ["latin"];
127
- }
128
130
  return {
129
131
  name,
130
132
  cssVariable,
131
133
  provider,
132
- subsets
134
+ subsets: parseStringArray(subsetsSource) ?? ["latin"],
135
+ ...weights && { weights }
133
136
  };
134
137
  }).filter((value) => value !== null);
135
138
  }
@@ -233,7 +236,8 @@ function toManagedAstroFont(fontName) {
233
236
  name: font.title?.replace(/\s+\(Heading\)$/, "") ?? font.font.import.replace(/_/g, " "),
234
237
  cssVariable: font.font.variable,
235
238
  provider: ASTRO_FONTSOURCE_FONT_NAMES.has(normalized) ? "fontsource" : "google",
236
- subsets: normalizeManagedAstroFontSubsets(font.font.subsets)
239
+ subsets: normalizeManagedAstroFontSubsets(font.font.subsets),
240
+ ...font.font.weight?.length && { weights: [...font.font.weight] }
237
241
  };
238
242
  }
239
243
  function mergeManagedAstroFonts(currentFonts, nextFont) {
@@ -325,7 +329,7 @@ const CREATE_BLOCK_START = "/* bejamas:create:start */";
325
329
  const CREATE_BLOCK_END = "/* bejamas:create:end */";
326
330
  const SHADCN_TAILWIND_IMPORT = "@import \"shadcn/tailwind.css\";";
327
331
  const BEJAMAS_TAILWIND_IMPORT = "@import \"bejamas/tailwind.css\";";
328
- const MANAGED_TAILWIND_IMPORTS = new Set([SHADCN_TAILWIND_IMPORT, BEJAMAS_TAILWIND_IMPORT]);
332
+ const MANAGED_TAILWIND_IMPORTS = /* @__PURE__ */ new Set([SHADCN_TAILWIND_IMPORT, BEJAMAS_TAILWIND_IMPORT]);
329
333
  const MANAGED_ICON_PACKAGES = new Set(ICON_LIBRARIES.map((library) => library.packageName));
330
334
  const FALLBACK_ICON_PACKAGE_VERSION = "latest";
331
335
  const TEMPLATE_APP_UI_IMPORT = "import { appUi } from \"@/i18n/ui\";";
@@ -677,10 +681,17 @@ function transformStarterPageToI18n(source) {
677
681
  if (!match) return source;
678
682
  return compactSource(match.i18nSource);
679
683
  }
680
- function upsertThemeInlineFont(source, fontVariable) {
684
+ /**
685
+ * Astro's <Font> defines the font variables outside any cascade layer, so it
686
+ * beats Tailwind's theme layer. The body font therefore needs no theme line
687
+ * (without a <Font>, Tailwind's default stack applies), and the heading font
688
+ * falls back to the body font until a heading <Font> defines it.
689
+ */
690
+ function upsertThemeInlineFont(source, fontVariable = "--font-sans") {
681
691
  const pattern = /@theme inline\s*\{[\s\S]*?\n\}/m;
692
+ const declarations = ` --font-heading: var(--font-heading, var(${fontVariable}));`;
682
693
  if (!pattern.test(source)) {
683
- const block = `@theme inline {\n${[fontVariable ? ` ${fontVariable}: var(${fontVariable});` : null, " --font-heading: var(--font-heading);"].filter(Boolean).join("\n")}\n}`;
694
+ const block = `@theme inline {\n${declarations}\n}`;
684
695
  const customVariantIndex = source.indexOf("@custom-variant");
685
696
  if (customVariantIndex !== -1) {
686
697
  const nextLineIndex = source.indexOf("\n", customVariantIndex);
@@ -689,9 +700,7 @@ function upsertThemeInlineFont(source, fontVariable) {
689
700
  return `${block}\n\n${source.trimStart()}`;
690
701
  }
691
702
  return source.replace(pattern, (block) => {
692
- const withoutManagedFonts = block.replace(/\n\s*--font-(sans|serif|mono|heading):[^\n]+/g, "").replace(/\n\s*--bejamas-font-family:[^\n]+/g, "").replace(/@theme inline\s*\{\n?/, "@theme inline {\n");
693
- const declarations = [fontVariable ? ` ${fontVariable}: var(${fontVariable});` : null, " --font-heading: var(--font-heading);"].filter(Boolean).join("\n");
694
- return withoutManagedFonts.replace("@theme inline {\n", `@theme inline {\n${declarations}\n`);
703
+ return block.replace(/\n\s*--font-(sans|serif|mono|heading):[^\n]+/g, "").replace(/\n\s*--bejamas-font-family:[^\n]+/g, "").replace(/@theme inline\s*\{\n?/, "@theme inline {\n").replace("@theme inline {\n", `@theme inline {\n${declarations}\n`);
695
704
  });
696
705
  }
697
706
  function upsertBaseLayerHtmlFont(source, fontClass) {
@@ -707,7 +716,7 @@ function transformDesignSystemCss(source, config, themeVars) {
707
716
  function transformDesignSystemThemeCss(source, config, themeVars) {
708
717
  const effectiveThemeVars = themeVars ?? buildRegistryTheme(config).cssVars;
709
718
  const rootVars = {
710
- ...Object.fromEntries(Object.entries(effectiveThemeVars.theme ?? {}).filter(([key]) => key !== "bejamas-font-family")),
719
+ ...Object.fromEntries(Object.entries(effectiveThemeVars.theme ?? {}).filter(([key]) => key !== "bejamas-font-family" && key !== "font-heading")),
711
720
  ...effectiveThemeVars.light ?? {}
712
721
  };
713
722
  const darkVars = { ...effectiveThemeVars.dark ?? {} };
@@ -740,7 +749,7 @@ function transformManagedTailwindImportCss(source) {
740
749
  }
741
750
  async function patchComponentsJson(filepath, config) {
742
751
  const current = await fs.readJson(filepath);
743
- const { rsc: _rsc, tsx: _tsx,...normalizedCurrent } = current;
752
+ const { rsc: _rsc, tsx: _tsx, ...normalizedCurrent } = current;
744
753
  const next = {
745
754
  ...normalizedCurrent,
746
755
  $schema: BEJAMAS_COMPONENTS_SCHEMA_URL,
@@ -865,13 +874,13 @@ async function patchLayoutFile(filepath, config) {
865
874
  const current = await fs.readFile(filepath, "utf8");
866
875
  if (current.includes("from \"@/i18n/ui\"")) return;
867
876
  if (config.rtl) {
868
- const next$1 = transformTemplateLayoutToI18n(current);
869
- if (next$1 !== current) await fs.writeFile(filepath, next$1, "utf8");
877
+ const next = transformTemplateLayoutToI18n(current);
878
+ if (next !== current) await fs.writeFile(filepath, next, "utf8");
870
879
  return;
871
880
  }
872
881
  const nextLanguage = getDocumentLanguage(config);
873
882
  const nextDirection = getDocumentDirection(config);
874
- let next = current.replace(/<html([^>]*?)lang="[^"]*"([^>]*)>/, "<html$1$2>").replace(/<html([^>]*?)dir="[^"]*"([^>]*)>/, "<html$1$2>");
883
+ let next = current.replace(/<html([^>]*?)\slang="[^"]*"([^>]*)>/, "<html$1$2>").replace(/<html([^>]*?)\sdir="[^"]*"([^>]*)>/, "<html$1$2>");
875
884
  next = next.replace(/<html([^>]*)>/, `<html$1 lang="${nextLanguage}"${config.rtl ? ` dir="${nextDirection}"` : ""}>`);
876
885
  if (next !== current) await fs.writeFile(filepath, next, "utf8");
877
886
  }
@@ -1012,7 +1021,8 @@ function rewriteAstroIcons(content, iconLibrary) {
1012
1021
  function renderSemanticIconUsage(original, library, attributeString = "") {
1013
1022
  const semanticName = attributeString.match(/\sname=(["'])([^"']+)\1/)?.[2];
1014
1023
  if (!semanticName || !SEMANTIC_ICON_NAMES.includes(semanticName)) return original;
1015
- return renderSemanticIconSvgWithAttributeString(library, semanticName, attributeString.replace(/\sname=(["'])[^"']+\1/g, "").replace(/\slibrary=(["'])[^"']+\1/g, "").replace(/\sdata-icon-library=(["'])[^"']+\1/g, "").trim());
1024
+ const normalizedAttributeString = attributeString.replace(/\sname=(["'])[^"']+\1/g, "").replace(/\slibrary=(["'])[^"']+\1/g, "").replace(/\sdata-icon-library=(["'])[^"']+\1/g, "").trim();
1025
+ return renderSemanticIconSvgWithAttributeString(library, semanticName, normalizedAttributeString);
1016
1026
  }
1017
1027
 
1018
1028
  //#endregion
@@ -1064,6 +1074,15 @@ function rewriteAstroMenus(content, menuColor) {
1064
1074
  //#endregion
1065
1075
  //#region src/utils/astro-imports.ts
1066
1076
  function updateImportAliases(moduleSpecifier, config, isRemote = false) {
1077
+ const scope = config.aliases.components.split("/")[0];
1078
+ for (const [segment, alias] of [
1079
+ ["lib/utils", config.aliases.utils],
1080
+ ["lib", config.aliases.lib],
1081
+ ["hooks", config.aliases.hooks]
1082
+ ]) {
1083
+ if (!alias) continue;
1084
+ for (const prefix of [`@/${segment}`, `${scope}/${segment}`]) if (moduleSpecifier === prefix || moduleSpecifier.startsWith(`${prefix}/`)) return alias + moduleSpecifier.slice(prefix.length);
1085
+ }
1067
1086
  if (!moduleSpecifier.startsWith("@/") && !isRemote) return moduleSpecifier;
1068
1087
  let specifier = moduleSpecifier;
1069
1088
  if (isRemote && specifier.startsWith("@/")) specifier = specifier.replace(/^@\//, `@/registry/${config.style || "bejamas-juno"}/`);
@@ -1088,6 +1107,7 @@ function rewriteAstroImports(content, config) {
1088
1107
  if (finalSpec === specifier) return full;
1089
1108
  return full.replace(specifier, finalSpec);
1090
1109
  });
1110
+ updated = updated.replace(/(?:export\s+(?:type\s+)?(?:\{[^}]*\}|\*)\s+from\s*|import\s*\(\s*)["']([^"']+)["']/g, (full, specifier) => full.replace(specifier, updateImportAliases(specifier, config)));
1091
1111
  updated = updated.replace(/import\s+["']([^"']+)["']/g, (full, specifier) => {
1092
1112
  const next = updateImportAliases(specifier, config, false);
1093
1113
  if (next === specifier) return full;
@@ -1096,25 +1116,45 @@ function rewriteAstroImports(content, config) {
1096
1116
  updated = rewriteAstroMenus(updated, config.menuColor);
1097
1117
  return rewriteAstroIcons(updated, config.iconLibrary);
1098
1118
  }
1099
- async function fixAstroImports(cwd, isVerbose) {
1100
- const config = await getConfig(cwd);
1119
+ function getConfiguredSourceRoots(config) {
1120
+ return Array.from(new Set([
1121
+ config.resolvedPaths.components,
1122
+ config.resolvedPaths.ui,
1123
+ config.resolvedPaths.lib,
1124
+ config.resolvedPaths.hooks
1125
+ ].filter((root) => Boolean(root))));
1126
+ }
1127
+ function isPathWithin(filePath, root) {
1128
+ const relative = path.relative(root, filePath);
1129
+ return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
1130
+ }
1131
+ async function fixAstroImports(cwd, isVerbose, targetConfig, scope = { kind: "configured-roots" }) {
1132
+ const config = targetConfig ?? await getConfig(cwd);
1101
1133
  if (!config) return;
1102
- const searchRoots = new Set([config.resolvedPaths.components, config.resolvedPaths.ui]);
1103
- for (const root of Array.from(searchRoots)) {
1104
- if (!root) continue;
1105
- const astroFiles = await fg("**/*.astro", {
1106
- cwd: root,
1107
- absolute: true,
1108
- dot: false
1109
- });
1110
- for (const filePath of astroFiles) {
1111
- const original = await fs$1.readFile(filePath, "utf8");
1112
- const rewritten = rewriteAstroImports(original, config);
1113
- if (rewritten === original) continue;
1114
- await fs$1.writeFile(filePath, rewritten, "utf8");
1115
- if (isVerbose) logger.info(`[bejamas-ui] fixed imports in ${path.relative(cwd, filePath)}`);
1134
+ const files = /* @__PURE__ */ new Set();
1135
+ if (scope.kind === "files") for (const filePath of scope.paths) {
1136
+ const absolutePath = path.resolve(cwd, filePath);
1137
+ if (!/\.(?:astro|ts|js)$/.test(absolutePath)) continue;
1138
+ if ((await fs$1.stat(absolutePath).catch(() => null))?.isFile()) files.add(absolutePath);
1139
+ }
1140
+ else {
1141
+ const excluded = new Set((scope.exclude ?? []).map((filePath) => path.resolve(cwd, filePath)));
1142
+ for (const root of getConfiguredSourceRoots(config)) {
1143
+ const matches = await fg("**/*.{astro,ts,js}", {
1144
+ cwd: root,
1145
+ absolute: true,
1146
+ dot: false
1147
+ });
1148
+ for (const filePath of matches) if (!excluded.has(path.resolve(filePath))) files.add(filePath);
1116
1149
  }
1117
1150
  }
1151
+ for (const filePath of files) {
1152
+ const original = await fs$1.readFile(filePath, "utf8");
1153
+ const rewritten = rewriteAstroImports(original, config);
1154
+ if (rewritten === original) continue;
1155
+ await fs$1.writeFile(filePath, rewritten, "utf8");
1156
+ if (isVerbose) logger.info(`[bejamas-ui] fixed imports in ${path.relative(cwd, filePath)}`);
1157
+ }
1118
1158
  }
1119
1159
 
1120
1160
  //#endregion
@@ -1235,7 +1275,7 @@ const TEMPLATE_DIRNAME = {
1235
1275
  "astro-monorepo": "monorepo-astro",
1236
1276
  "astro-with-component-docs-monorepo": "monorepo-astro-with-docs"
1237
1277
  };
1238
- const TEMPLATE_EXCLUDED_BASENAMES = new Set(["node_modules", ".astro"]);
1278
+ const TEMPLATE_EXCLUDED_BASENAMES = /* @__PURE__ */ new Set(["node_modules", ".astro"]);
1239
1279
  const DEFAULT_TEMPLATE_REPOSITORY = "bejamas/ui";
1240
1280
  const DEFAULT_TEMPLATE_REF = "main";
1241
1281
  function resolveLocalTemplatesDir(env = process.env) {
@@ -1333,20 +1373,33 @@ async function copyTemplateFromGitHub(templateDirName, projectPath) {
1333
1373
  failures.join("\n")
1334
1374
  ].join("\n"));
1335
1375
  }
1336
- async function applyLocalPackageOverrides(projectPath) {
1337
- const bejamasPackageOverride = process.env.BEJAMAS_PACKAGE_OVERRIDE;
1338
- if (!bejamasPackageOverride) return;
1376
+ /** A valid npm package name derived from the project's folder name. */
1377
+ function toPackageName(projectName) {
1378
+ return path$1.basename(projectName).toLowerCase().replace(/[^a-z0-9._~-]+/g, "-").replace(/^[._-]+|-+$/g, "") || "my-app";
1379
+ }
1380
+ /**
1381
+ * Fit the template's manifests to this project: the root package takes the
1382
+ * project's name (the template's `astro` shadows the real `astro` package in a
1383
+ * workspace), and every `bejamas` dependency points at the running CLI version,
1384
+ * or at `BEJAMAS_PACKAGE_OVERRIDE` when testing a local build.
1385
+ */
1386
+ async function applyProjectPackageMetadata(projectPath, { projectName, bejamasVersion = BEJAMAS_PACKAGE_METADATA.version, bejamasOverride = process.env.BEJAMAS_PACKAGE_OVERRIDE }) {
1387
+ const bejamasSpec = bejamasOverride ? `file:${bejamasOverride.replace(/\\/g, "/")}` : bejamasVersion && `^${bejamasVersion}`;
1388
+ const rootPackageJson = path$1.resolve(projectPath, "package.json");
1339
1389
  const packageJsonPaths = await fg("**/package.json", {
1340
1390
  cwd: projectPath,
1341
1391
  absolute: true,
1342
1392
  ignore: ["**/node_modules/**"]
1343
1393
  });
1344
- const normalizedOverride = bejamasPackageOverride.replace(/\\/g, "/");
1345
1394
  await Promise.all(packageJsonPaths.map(async (packageJsonPath) => {
1346
1395
  const packageJson = await fs.readJson(packageJsonPath);
1347
1396
  let changed = false;
1348
- for (const field of ["dependencies", "devDependencies"]) if (packageJson[field]?.bejamas) {
1349
- packageJson[field].bejamas = `file:${normalizedOverride}`;
1397
+ if (path$1.resolve(packageJsonPath) === rootPackageJson) {
1398
+ packageJson.name = toPackageName(projectName);
1399
+ changed = true;
1400
+ }
1401
+ for (const field of ["dependencies", "devDependencies"]) if (bejamasSpec && packageJson[field]?.bejamas) {
1402
+ packageJson[field].bejamas = bejamasSpec;
1350
1403
  changed = true;
1351
1404
  }
1352
1405
  if (changed) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
@@ -1412,6 +1465,7 @@ async function createProject(options) {
1412
1465
  process.exit(1);
1413
1466
  }
1414
1467
  await createProjectFromTemplate(projectPath, {
1468
+ projectName,
1415
1469
  templateKey: template,
1416
1470
  packageManager,
1417
1471
  cwd: options.cwd
@@ -1434,7 +1488,7 @@ async function createProjectFromTemplate(projectPath, options) {
1434
1488
  await copyTemplateFromGitHub(templateDirName, projectPath);
1435
1489
  }
1436
1490
  await removeEmptyTemplateI18nDirs(projectPath);
1437
- await applyLocalPackageOverrides(projectPath);
1491
+ await applyProjectPackageMetadata(projectPath, { projectName: options.projectName });
1438
1492
  await execa(options.packageManager, ["install"], { cwd: projectPath });
1439
1493
  try {
1440
1494
  const { stdout } = await execa("git", ["rev-parse", "--is-inside-work-tree"], { cwd: projectPath });
@@ -1498,12 +1552,32 @@ async function getInstalledUiComponents(cwd) {
1498
1552
 
1499
1553
  //#endregion
1500
1554
  //#region src/utils/reorganize-components.ts
1555
+ const BEJAMAS_REGISTRY_NAMESPACE = "@bejamas/";
1556
+ /**
1557
+ * Resolve the Bejamas item name behind a user-provided specifier. Returns null
1558
+ * for URLs and third-party namespaces, which shadcn resolves on its own.
1559
+ */
1560
+ function resolveBejamasRegistryItemName(specifier) {
1561
+ const name = specifier.startsWith("@bejamas/") ? specifier.slice(9) : specifier;
1562
+ return /^[a-z0-9][a-z0-9-]*$/.test(name) ? name : null;
1563
+ }
1564
+ function isRegistryItemUrl(specifier) {
1565
+ return /^https?:\/\//.test(specifier);
1566
+ }
1501
1567
  async function fetchRegistryItem(componentName, registryUrl, style = "bejamas-juno") {
1502
- const url$1 = `${registryUrl}/styles/${style}/${componentName}.json`;
1568
+ if (isRegistryItemUrl(componentName)) try {
1569
+ const response = await fetch(componentName);
1570
+ return response.ok ? await response.json() : null;
1571
+ } catch {
1572
+ return null;
1573
+ }
1574
+ const itemName = resolveBejamasRegistryItemName(componentName);
1575
+ if (!itemName) return null;
1576
+ const url = `${registryUrl}/styles/${style}/${itemName}.json`;
1503
1577
  try {
1504
- const response = await fetch(url$1);
1578
+ const response = await fetch(url);
1505
1579
  if (!response.ok) {
1506
- const fallbackUrl = `${registryUrl}/${componentName}.json`;
1580
+ const fallbackUrl = `${registryUrl}/${itemName}.json`;
1507
1581
  const fallbackResponse = await fetch(fallbackUrl);
1508
1582
  if (!fallbackResponse.ok) return null;
1509
1583
  return await fallbackResponse.json();
@@ -1516,7 +1590,7 @@ async function fetchRegistryItem(componentName, registryUrl, style = "bejamas-ju
1516
1590
  function getSubfolderFromPaths(files) {
1517
1591
  if (!files || files.length === 0) return null;
1518
1592
  const uiFiles = files.filter((file) => file.type === "registry:ui");
1519
- if (uiFiles.length < 2) return null;
1593
+ if (uiFiles.length === 0) return null;
1520
1594
  const subfolders = /* @__PURE__ */ new Set();
1521
1595
  for (const file of uiFiles) {
1522
1596
  const parts = file.path.split("/");
@@ -1525,8 +1599,8 @@ function getSubfolderFromPaths(files) {
1525
1599
  }
1526
1600
  if (subfolders.size === 1) return Array.from(subfolders)[0];
1527
1601
  if (uiFiles.length > 0) {
1528
- const dirname$1 = path.dirname(uiFiles[0].path);
1529
- const folderName = path.basename(dirname$1);
1602
+ const dirname = path.dirname(uiFiles[0].path);
1603
+ const folderName = path.basename(dirname);
1530
1604
  if (folderName && folderName !== "ui") return folderName;
1531
1605
  }
1532
1606
  return null;
@@ -1556,7 +1630,7 @@ function resolveShadcnUiRelativePath(filePath, uiDir) {
1556
1630
  function shouldReorganizeRegistryUiFiles(files, uiDir) {
1557
1631
  if (!uiDir) return false;
1558
1632
  if (!getSubfolderFromPaths(files)) return false;
1559
- return files.filter((file) => file.type === "registry:ui").some((file) => {
1633
+ return (files ?? []).filter((file) => file.type === "registry:ui").some((file) => {
1560
1634
  return !resolveShadcnUiRelativePath(file.path, uiDir).includes("/");
1561
1635
  });
1562
1636
  }
@@ -1577,6 +1651,7 @@ async function reorganizeRegistryUiFiles(files, uiDir, verbose, overwriteExistin
1577
1651
  const targetDir = path.join(uiDir, subfolder);
1578
1652
  for (const file of uiFiles) {
1579
1653
  const filename = path.basename(file.path);
1654
+ if (filename === "index.ts") continue;
1580
1655
  const flatPath = path.join(uiDir, filename);
1581
1656
  const targetPath = path.join(targetDir, filename);
1582
1657
  if (!await pathExists(flatPath)) continue;
@@ -1605,65 +1680,124 @@ async function reorganizeRegistryUiFiles(files, uiDir, verbose, overwriteExistin
1605
1680
  result.movedFiles.push(`${subfolder}/${filename}`);
1606
1681
  if (verbose) logger.info(`[bejamas-ui] Moved ${filename} -> ${subfolder}/${filename}`);
1607
1682
  }
1683
+ if (await pathExists(targetDir)) await repairComponentBarrel(targetDir, files, overwriteExisting);
1608
1684
  return result;
1609
1685
  }
1686
+ async function repairComponentBarrel(targetDir, files, overwriteExisting = false) {
1687
+ const localFiles = (await fs$1.readdir(targetDir)).filter((name) => name.endsWith(".astro")).sort();
1688
+ if (!localFiles.length) return;
1689
+ const indexPath = path.join(targetDir, "index.ts");
1690
+ const existing = await fs$1.readFile(indexPath, "utf8").catch(() => "");
1691
+ const registryBarrel = files.find((file) => path.basename(file.path) === "index.ts")?.content ?? "";
1692
+ const cleaned = (overwriteExisting || !existing ? registryBarrel : existing).replace(/export\s*\{[^}]*\}\s*from\s*["'](\.\/[^"']+\.astro)["'];?/g, (statement, specifier) => localFiles.includes(specifier.slice(2)) ? statement : "").trim();
1693
+ const next = [cleaned, ...localFiles.filter((file) => {
1694
+ const name = path.basename(file, ".astro");
1695
+ return !new RegExp(`default\\s+as\\s+${name}\\b`).test(cleaned);
1696
+ }).map((file) => `export { default as ${path.basename(file, ".astro")} } from "./${file}";`)].filter(Boolean).join("\n") + "\n";
1697
+ if (next !== existing) await fs$1.writeFile(indexPath, next);
1698
+ }
1699
+ /** Resolve dependencies as well as the requested item before repairing output. */
1700
+ async function fetchRegistryTree(components, registryUrl, style = "bejamas-juno") {
1701
+ const seen = /* @__PURE__ */ new Set();
1702
+ const items = [];
1703
+ async function visit(name) {
1704
+ if (seen.has(name)) return;
1705
+ seen.add(name);
1706
+ if (!isRegistryItemUrl(name) && !resolveBejamasRegistryItemName(name)) return;
1707
+ const item = await fetchRegistryItem(name, registryUrl, style);
1708
+ if (!item) throw new Error(`Unable to load registry item ${name} for installation validation.`);
1709
+ for (const dependency of item.registryDependencies ?? []) await visit(dependency);
1710
+ items.push(item);
1711
+ }
1712
+ for (const name of components) await visit(name);
1713
+ return items;
1714
+ }
1610
1715
  async function reorganizeComponents(components, uiDir, registryUrl, verbose, style = "bejamas-juno", overwriteExisting = false) {
1716
+ return reorganizeRegistryItems(uiDir && components.length ? await fetchRegistryTree(components, registryUrl, style) : [], uiDir, verbose, overwriteExisting);
1717
+ }
1718
+ /** Reuse the registry tree already resolved for this installation. */
1719
+ async function reorganizeRegistryItems(items, uiDir, verbose, overwriteExisting = false) {
1611
1720
  const result = {
1612
1721
  totalMoved: 0,
1613
1722
  movedFiles: [],
1614
1723
  skippedFiles: []
1615
1724
  };
1616
- if (!uiDir || components.length === 0) return result;
1617
- for (const componentName of components) try {
1618
- const registryItem = await fetchRegistryItem(componentName, registryUrl, style);
1619
- if (!registryItem) {
1620
- if (verbose) logger.info(`[bejamas-ui] Could not fetch registry for ${componentName}, skipping reorganization`);
1621
- continue;
1622
- }
1623
- if (!shouldReorganizeRegistryUiFiles(registryItem.files, uiDir)) continue;
1624
- const componentResult = await reorganizeRegistryUiFiles(registryItem.files, uiDir, verbose, overwriteExisting);
1725
+ if (!items.some((item) => shouldReorganizeRegistryUiFiles(item.files, uiDir))) return result;
1726
+ for (const item of items) {
1727
+ if (!shouldReorganizeRegistryUiFiles(item.files, uiDir)) continue;
1728
+ const componentResult = await reorganizeRegistryUiFiles(item.files, uiDir, verbose, overwriteExisting);
1625
1729
  result.totalMoved += componentResult.totalMoved;
1626
1730
  result.movedFiles.push(...componentResult.movedFiles);
1627
1731
  result.skippedFiles.push(...componentResult.skippedFiles);
1628
- if (componentResult.totalMoved > 0 && verbose) {
1629
- const subfolder = getSubfolderFromPaths(registryItem.files);
1630
- logger.info(`[bejamas-ui] Reorganized ${componentName} into ${subfolder}/`);
1631
- }
1632
- } catch (err) {
1633
- if (verbose) logger.warn(`[bejamas-ui] Failed to reorganize ${componentName}: ${err}`);
1634
1732
  }
1733
+ const flatIndex = path.join(uiDir, "index.ts");
1734
+ const flatContent = await fs$1.readFile(flatIndex, "utf8").catch(() => null);
1735
+ if (flatContent && items.some((item) => item.files?.some((file) => file.type === "registry:ui" && path.basename(file.path) === "index.ts" && file.content.trim() === flatContent.trim()))) await fs$1.unlink(flatIndex);
1635
1736
  return result;
1636
1737
  }
1738
+ /** Update legacy template exports once the installed UI uses nested barrels. */
1739
+ async function repairUiPackageExports(uiDir, packageRoot) {
1740
+ const entries = await fs$1.readdir(uiDir, { withFileTypes: true }).catch(() => []);
1741
+ if (!entries.some((entry) => entry.isDirectory()) || entries.some((entry) => entry.name.endsWith(".astro"))) return;
1742
+ const packagePath = path.join(packageRoot, "package.json");
1743
+ const manifest = JSON.parse(await fs$1.readFile(packagePath, "utf8"));
1744
+ if (!manifest.exports || typeof manifest.exports !== "object") return;
1745
+ const relativeUi = path.relative(packageRoot, uiDir).split(path.sep).join("/");
1746
+ if (relativeUi.startsWith("../")) return;
1747
+ const prefix = `./${relativeUi}`;
1748
+ let changed = false;
1749
+ for (const [specifier, target] of Object.entries(manifest.exports)) if (target === `${prefix}/*.astro` || target === `${prefix}/*.ts`) {
1750
+ manifest.exports[specifier] = `${prefix}/*/index.ts`;
1751
+ changed = true;
1752
+ }
1753
+ if (changed) await fs$1.writeFile(packagePath, JSON.stringify(manifest, null, 2) + "\n");
1754
+ }
1637
1755
 
1638
1756
  //#endregion
1639
- //#region src/utils/shadcn-cli.ts
1640
- const PINNED_SHADCN_VERSION = "4.6.0";
1641
- const PINNED_SHADCN_PACKAGE = `shadcn@${PINNED_SHADCN_VERSION}`;
1642
- const PINNED_SHADCN_EXEC_PREFIX = path.join(os$1.tmpdir(), "bejamas-shadcn", PINNED_SHADCN_VERSION);
1643
- async function ensurePinnedShadcnExecPrefix() {
1644
- await fs$1.mkdir(PINNED_SHADCN_EXEC_PREFIX, { recursive: true });
1645
- return PINNED_SHADCN_EXEC_PREFIX;
1646
- }
1647
- function getNpmExecutable() {
1648
- return process.platform === "win32" ? "npm.cmd" : "npm";
1649
- }
1650
- function buildPinnedShadcnInvocation(shadcnArgs) {
1757
+ //#region src/utils/pinned-npm-exec.ts
1758
+ /**
1759
+ * Run a package's bin at an exact version through `npm exec`, in a prefix of
1760
+ * its own so the user's project dependencies never interfere.
1761
+ */
1762
+ function definePinnedPackage({ name, version, bin, prefixDir }) {
1763
+ const execPrefix = path.join(os$1.tmpdir(), prefixDir, version);
1764
+ const spec = `${name}@${version}`;
1651
1765
  return {
1652
- cmd: getNpmExecutable(),
1653
- args: [
1654
- "exec",
1655
- "--yes",
1656
- "--prefix",
1657
- PINNED_SHADCN_EXEC_PREFIX,
1658
- `--package=${PINNED_SHADCN_PACKAGE}`,
1659
- "--",
1660
- "shadcn",
1661
- ...shadcnArgs
1662
- ],
1663
- source: "isolated"
1766
+ version,
1767
+ spec,
1768
+ execPrefix,
1769
+ async ensureExecPrefix() {
1770
+ await fs$1.mkdir(execPrefix, { recursive: true });
1771
+ return execPrefix;
1772
+ },
1773
+ invocation(args) {
1774
+ return {
1775
+ cmd: process.platform === "win32" ? "npm.cmd" : "npm",
1776
+ args: [
1777
+ "exec",
1778
+ "--yes",
1779
+ "--prefix",
1780
+ execPrefix,
1781
+ `--package=${spec}`,
1782
+ "--",
1783
+ bin,
1784
+ ...args
1785
+ ],
1786
+ source: "isolated"
1787
+ };
1788
+ }
1664
1789
  };
1665
1790
  }
1666
1791
 
1792
+ //#endregion
1793
+ //#region src/utils/shadcn-cli.ts
1794
+ const PINNED_SHADCN = definePinnedPackage({
1795
+ name: "shadcn",
1796
+ version: "4.21.0",
1797
+ bin: "shadcn",
1798
+ prefixDir: "bejamas-shadcn"
1799
+ });
1800
+
1667
1801
  //#endregion
1668
1802
  //#region src/utils/shadcn-command.ts
1669
1803
  function extractPassthroughArgs(rawArgv, commandName) {
@@ -1675,8 +1809,8 @@ function extractPassthroughArgs(rawArgv, commandName) {
1675
1809
  return rest.slice(doubleDashIndex + 1);
1676
1810
  }
1677
1811
  async function runShadcnCommand({ cwd, args, env }) {
1678
- await ensurePinnedShadcnExecPrefix();
1679
- const invocation = buildPinnedShadcnInvocation(args);
1812
+ await PINNED_SHADCN.ensureExecPrefix();
1813
+ const invocation = PINNED_SHADCN.invocation(args);
1680
1814
  await execa(invocation.cmd, invocation.args, {
1681
1815
  cwd,
1682
1816
  env: {
@@ -1701,14 +1835,13 @@ function resolveDesignSystemConfig(options) {
1701
1835
  rtlLanguage
1702
1836
  }));
1703
1837
  }
1704
- const baseColor = options.baseColor ?? DEFAULT_DESIGN_SYSTEM_CONFIG.baseColor;
1705
1838
  return normalizeDesignSystemConfig(designSystemConfigSchema.parse({
1706
1839
  ...DEFAULT_DESIGN_SYSTEM_CONFIG,
1707
1840
  template: options.template ?? DEFAULT_DESIGN_SYSTEM_CONFIG.template,
1708
1841
  rtl: options.rtl ?? false,
1709
1842
  rtlLanguage,
1710
- baseColor,
1711
- theme: baseColor === DEFAULT_DESIGN_SYSTEM_CONFIG.baseColor ? DEFAULT_DESIGN_SYSTEM_CONFIG.theme : baseColor
1843
+ baseColor: options.baseColor ?? DEFAULT_DESIGN_SYSTEM_CONFIG.baseColor,
1844
+ theme: options.baseColor ?? DEFAULT_DESIGN_SYSTEM_CONFIG.theme
1712
1845
  }));
1713
1846
  }
1714
1847
  function buildInitUrl(config, themeRef, env = process.env) {
@@ -1772,32 +1905,32 @@ function ensureShadcnReinstallFlag(forwardedOptions, shouldReinstall) {
1772
1905
  function extractOptionsForShadcnInit(rawArgv, cmd) {
1773
1906
  if (typeof cmd.getOptionValueSource === "function") {
1774
1907
  const opts = cmd.optsWithGlobals();
1775
- const forwarded$1 = [];
1908
+ const forwarded = [];
1776
1909
  const getSource = (key) => cmd.getOptionValueSource(key);
1777
1910
  const addBoolean = (key, flag, negateFlag) => {
1778
1911
  if (getSource(key) !== "cli") return;
1779
1912
  const value = opts[key];
1780
1913
  if (typeof value !== "boolean") return;
1781
- if (value) forwarded$1.push(flag);
1782
- else if (negateFlag) forwarded$1.push(negateFlag);
1914
+ if (value) forwarded.push(flag);
1915
+ else if (negateFlag) forwarded.push(negateFlag);
1783
1916
  };
1784
1917
  addBoolean("yes", "--yes");
1785
1918
  addBoolean("force", "--force");
1786
1919
  addBoolean("silent", "--silent");
1787
1920
  addBoolean("reinstall", "--reinstall", "--no-reinstall");
1788
- const initIndex$1 = rawArgv.findIndex((arg) => arg === "init");
1789
- if (initIndex$1 !== -1) {
1790
- const rest$1 = rawArgv.slice(initIndex$1 + 1);
1791
- const doubleDashIndex = rest$1.indexOf("--");
1792
- if (doubleDashIndex !== -1) forwarded$1.push(...rest$1.slice(doubleDashIndex));
1921
+ const initIndex = rawArgv.findIndex((arg) => arg === "init");
1922
+ if (initIndex !== -1) {
1923
+ const rest = rawArgv.slice(initIndex + 1);
1924
+ const doubleDashIndex = rest.indexOf("--");
1925
+ if (doubleDashIndex !== -1) forwarded.push(...rest.slice(doubleDashIndex));
1793
1926
  }
1794
- return forwarded$1;
1927
+ return forwarded;
1795
1928
  }
1796
1929
  const initIndex = rawArgv.findIndex((arg) => arg === "init");
1797
1930
  if (initIndex === -1) return [];
1798
1931
  const rest = rawArgv.slice(initIndex + 1);
1799
1932
  const forwarded = [];
1800
- const filteredFlags = new Set([
1933
+ const filteredFlags = /* @__PURE__ */ new Set([
1801
1934
  "-v",
1802
1935
  "--verbose",
1803
1936
  "-t",
@@ -1831,7 +1964,7 @@ function extractOptionsForShadcnInit(rawArgv, cmd) {
1831
1964
  }
1832
1965
  return forwarded;
1833
1966
  }
1834
- const init = new Command().name("init").description("initialize your project and install dependencies").argument("[components...]", "names, url or local path to component").option("-t, --template <template>", "the template to use. (astro, astro-monorepo, astro-with-component-docs-monorepo)").option("-b, --base-color <base-color>", "the base color to use. (neutral, gray, zinc, stone, slate)", void 0).option("-p, --preset <preset>", "the encoded create preset to use").option("--theme-ref <theme-ref>", "the custom theme ref to use").option("-y, --yes", "skip confirmation prompt.", false).option("-d, --defaults", "use default configuration.", false).option("-f, --force", "force overwrite of existing configuration.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).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.").option("--css-variables", "use css variables for theming.", true).option("--no-css-variables", "do not use css variables for theming.").option("--no-base-style", "do not install the base shadcn style.").option("--rtl", "enable right-to-left output", false).option("--lang <lang>", "set the RTL language. (ar, fa, he)").option("--reinstall", "re-install existing UI components. Enabled by default for preset switching.").option("--no-reinstall", "do not re-install existing UI components during preset switching.").action(async (_components, opts, cmd) => {
1967
+ const init = new Command().name("init").description("initialize your project and install dependencies").argument("[components...]", "names, url or local path to component").option("-t, --template <template>", "the template to use. (astro, astro-monorepo, astro-with-component-docs-monorepo)").option("-b, --base-color <base-color>", `the base color to use. (${BASE_COLORS.map((color) => color.name).join(", ")})`, void 0).option("-p, --preset <preset>", "the encoded create preset to use").option("--theme-ref <theme-ref>", "the custom theme ref to use").option("-y, --yes", "skip confirmation prompt.", false).option("-d, --defaults", "use default configuration.", false).option("-f, --force", "force overwrite of existing configuration.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).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.").option("--css-variables", "use css variables for theming.", true).option("--no-css-variables", "do not use css variables for theming.").option("--no-base-style", "do not install the base shadcn style.").option("--rtl", "enable right-to-left output", false).option("--lang <lang>", "set the RTL language. (ar, fa, he)").option("--reinstall", "re-install existing UI components. Enabled by default for preset switching.").option("--no-reinstall", "do not re-install existing UI components during preset switching.").action(async (_components, opts, cmd) => {
1835
1968
  try {
1836
1969
  await runInit({
1837
1970
  ...opts,
@@ -1848,7 +1981,7 @@ async function runInit(options) {
1848
1981
  const designConfig = resolveDesignSystemConfig(options);
1849
1982
  let newProjectTemplate;
1850
1983
  if (!options.skipPreflight) {
1851
- if ((await preFlightInit(options)).errors[MISSING_DIR_OR_EMPTY_PROJECT]) {
1984
+ if ((await preFlightInit(options)).errors["1"]) {
1852
1985
  const { projectPath, template } = await createProject(options);
1853
1986
  if (!projectPath) process.exit(1);
1854
1987
  options.cwd = projectPath;
@@ -1962,13 +2095,14 @@ function resolveAliasPathUsingTsConfig(inputPath, projectRoot) {
1962
2095
  const paths = cfg.compilerOptions.paths || {};
1963
2096
  for (const [key, values] of Object.entries(paths)) {
1964
2097
  const pattern = key.replace(/\*/g, "(.*)");
1965
- const re = /* @__PURE__ */ new RegExp(`^${pattern}$`);
2098
+ const re = new RegExp(`^${pattern}$`);
1966
2099
  const match = inputPath.match(re);
1967
2100
  if (!match) continue;
1968
2101
  const wildcard = match[1] || "";
1969
2102
  const first = Array.isArray(values) ? values[0] : values;
1970
2103
  if (!first) continue;
1971
- return resolve(projectRoot, baseUrl, String(first).replace(/\*/g, wildcard));
2104
+ const target = String(first).replace(/\*/g, wildcard);
2105
+ return resolve(projectRoot, baseUrl, target);
1972
2106
  }
1973
2107
  return null;
1974
2108
  }
@@ -1979,6 +2113,8 @@ async function generateDocs({ cwd, outDir, verbose }) {
1979
2113
  const DEBUG = process.env.BEJAMAS_DEBUG === "1" || process.env.BEJAMAS_DEBUG === "true" || verbose;
1980
2114
  try {
1981
2115
  const shellCwd = process.cwd();
2116
+ if (cwd && cwd.length) process.env.BEJAMAS_UI_ROOT = resolve(shellCwd, cwd);
2117
+ if (outDir && outDir.length) process.env.BEJAMAS_DOCS_OUT_DIR = outDir;
1982
2118
  let projectRoot = shellCwd;
1983
2119
  let probe = shellCwd;
1984
2120
  for (let i = 0; i < 6 && probe; i += 1) {
@@ -2030,27 +2166,30 @@ async function generateDocs({ cwd, outDir, verbose }) {
2030
2166
  }
2031
2167
  if (!process.env.BEJAMAS_DOCS_CWD) process.env.BEJAMAS_DOCS_CWD = shellCwd;
2032
2168
  if (!process.env.BEJAMAS_UI_ROOT) {
2169
+ const defaultGuess = (() => {
2170
+ let current = shellCwd;
2171
+ for (let i = 0; i < 6; i += 1) {
2172
+ const cand = resolve(current, "packages/ui/package.json");
2173
+ if (existsSync(cand)) {
2174
+ const abs = resolve(current, "packages/ui");
2175
+ return relative(shellCwd, abs) || abs;
2176
+ }
2177
+ const parent = resolve(current, "..");
2178
+ if (parent === current) break;
2179
+ current = parent;
2180
+ }
2181
+ const nm = resolve(shellCwd, "node_modules/@bejamas/ui/package.json");
2182
+ if (existsSync(nm)) {
2183
+ const abs = resolve(shellCwd, "node_modules/@bejamas/ui");
2184
+ return relative(shellCwd, abs) || abs;
2185
+ }
2186
+ return "packages/ui";
2187
+ })();
2033
2188
  const { uiRoot } = await prompts({
2034
2189
  type: "text",
2035
2190
  name: "uiRoot",
2036
2191
  message: "Path to @bejamas/ui package root:",
2037
- initial: (() => {
2038
- let current = shellCwd;
2039
- for (let i = 0; i < 6; i += 1) {
2040
- if (existsSync(resolve(current, "packages/ui/package.json"))) {
2041
- const abs = resolve(current, "packages/ui");
2042
- return relative(shellCwd, abs) || abs;
2043
- }
2044
- const parent = resolve(current, "..");
2045
- if (parent === current) break;
2046
- current = parent;
2047
- }
2048
- if (existsSync(resolve(shellCwd, "node_modules/@bejamas/ui/package.json"))) {
2049
- const abs = resolve(shellCwd, "node_modules/@bejamas/ui");
2050
- return relative(shellCwd, abs) || abs;
2051
- }
2052
- return "packages/ui";
2053
- })(),
2192
+ initial: defaultGuess,
2054
2193
  validate: (val) => {
2055
2194
  const p = resolve(shellCwd, val);
2056
2195
  return existsSync(resolve(p, "package.json")) ? true : `No package.json found in ${p}`;
@@ -2062,8 +2201,6 @@ async function generateDocs({ cwd, outDir, verbose }) {
2062
2201
  }
2063
2202
  process.env.BEJAMAS_UI_ROOT = resolve(shellCwd, uiRoot);
2064
2203
  }
2065
- if (cwd && cwd.length) process.env.BEJAMAS_UI_ROOT = resolve(cwd);
2066
- if (outDir && outDir.length) process.env.BEJAMAS_DOCS_OUT_DIR = outDir;
2067
2204
  if (!process.env.BEJAMAS_DOCS_OUT_DIR) {
2068
2205
  const { out } = await prompts({
2069
2206
  type: "text",
@@ -2086,7 +2223,7 @@ async function generateDocs({ cwd, outDir, verbose }) {
2086
2223
  if (process.env.BEJAMAS_DOCS_CWD) logger.info(`Docs CWD: ${process.env.BEJAMAS_DOCS_CWD}`);
2087
2224
  if (process.env.BEJAMAS_DOCS_OUT_DIR) logger.info(`Docs out: ${process.env.BEJAMAS_DOCS_OUT_DIR}`);
2088
2225
  }
2089
- const mod = await import("./generate-mdx-Ce7A9Uxe.js");
2226
+ const mod = await import("./generate-mdx-Cb8rPipn.js");
2090
2227
  if (typeof mod.runDocsGenerator === "function") await mod.runDocsGenerator();
2091
2228
  else throw new Error("Failed to load docs generator. Export 'runDocsGenerator' not found.");
2092
2229
  } catch (err) {
@@ -2123,7 +2260,9 @@ const FIELD_LABELS = {
2123
2260
  figmaUrl: "@figmaUrl"
2124
2261
  };
2125
2262
  function checkComponentDocs(filePath, fileName) {
2126
- const meta = parseJsDocMetadata(extractFrontmatter(readFileSync(filePath, "utf-8")));
2263
+ const content = readFileSync(filePath, "utf-8");
2264
+ const frontmatter = extractFrontmatter(content);
2265
+ const meta = parseJsDocMetadata(frontmatter);
2127
2266
  const componentName = fileName.replace(/\.astro$/i, "");
2128
2267
  const missingRequired = [];
2129
2268
  const missingRecommended = [];
@@ -2271,33 +2410,286 @@ const docsCheck = new Command().name("docs:check").description("check documentat
2271
2410
  });
2272
2411
  });
2273
2412
 
2413
+ //#endregion
2414
+ //#region ../registry/src/lib/headless-dependencies.ts
2415
+ /** Headless behaviors imported by emitted frontmatter, client scripts or helpers. */
2416
+ function getHeadlessDependencies(files) {
2417
+ const dependencies = /* @__PURE__ */ new Set();
2418
+ for (const file of files) {
2419
+ const source = (file.content ?? "").replace(/\/\*[\s\S]*?\*\//g, "");
2420
+ for (const match of source.matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*)["'](@data-slot\/[^/"']+)(?:\/[^"']*)?["']/g)) dependencies.add(match[1]);
2421
+ }
2422
+ return [...dependencies].sort();
2423
+ }
2424
+
2425
+ //#endregion
2426
+ //#region src/utils/registry-dependencies.ts
2427
+ function missingRegistryDependencies(manifest, items) {
2428
+ const declared = {
2429
+ ...manifest.devDependencies,
2430
+ ...manifest.dependencies
2431
+ };
2432
+ const candidates = /* @__PURE__ */ new Map();
2433
+ for (const item of items) for (const specifier of item.dependencies ?? []) {
2434
+ const name = specifier.match(/^(@[^/]+\/[^@/]+|[^@/]+)/)?.[1];
2435
+ if (name && !declared[name]) candidates.set(name, specifier);
2436
+ }
2437
+ for (const item of items) for (const name of getHeadlessDependencies(item.files ?? [])) if (!declared[name] && !candidates.has(name)) candidates.set(name, name);
2438
+ return [...candidates.values()].sort();
2439
+ }
2440
+ async function ensureRegistryDependencies(cwd, items) {
2441
+ const missing = missingRegistryDependencies(JSON.parse(await fs$1.readFile(path.join(cwd, "package.json"), "utf8")), items);
2442
+ if (!missing.length) return;
2443
+ const manager = await getPackageManager(cwd);
2444
+ await execa(manager, [manager === "npm" ? "install" : "add", ...missing], {
2445
+ cwd,
2446
+ stdio: "inherit"
2447
+ });
2448
+ }
2449
+
2450
+ //#endregion
2451
+ //#region src/utils/registry-install.ts
2452
+ /**
2453
+ * shadcn reports written files relative to the directory it ran in, except in
2454
+ * a workspace where paths are relative to the monorepo root. Resolve each
2455
+ * reported path against the candidate bases and keep the ones that exist; the
2456
+ * first base with a match wins, so list the one shadcn reports against first.
2457
+ */
2458
+ function resolveReportedFiles(files, bases, exists = existsSync) {
2459
+ const resolved = /* @__PURE__ */ new Set();
2460
+ for (const file of files) {
2461
+ const match = (path.isAbsolute(file) ? [file] : bases.map((base) => path.resolve(base, file))).find((candidate) => exists(candidate));
2462
+ if (match) resolved.add(match);
2463
+ }
2464
+ return Array.from(resolved);
2465
+ }
2466
+ /**
2467
+ * Bases for `resolveReportedFiles`. In a workspace, shadcn reports paths
2468
+ * relative to the monorepo root, so it comes before the app directory.
2469
+ */
2470
+ function getReportedFileBases(cwd, config, uiConfig) {
2471
+ const workspaceRoot = config && uiConfig && uiConfig.resolvedPaths.cwd !== config.resolvedPaths.cwd ? findCommonRoot(config.resolvedPaths.cwd, uiConfig.resolvedPaths.cwd) : null;
2472
+ return workspaceRoot ? [workspaceRoot, cwd] : [cwd];
2473
+ }
2474
+ /** Installed files (relative to cwd or absolute) not covered by config aliases. */
2475
+ function filesOutsideConfigRoots(cwd, files, config) {
2476
+ const roots = getConfiguredSourceRoots(config);
2477
+ return files.filter((file) => !roots.some((root) => isPathWithin(path.resolve(cwd, file), root)));
2478
+ }
2479
+ function buildSubfolderMap(registryItems) {
2480
+ const filenameToSubfolders = /* @__PURE__ */ new Map();
2481
+ for (const registryItem of registryItems) {
2482
+ const subfolder = getSubfolderFromPaths(registryItem.files);
2483
+ if (!subfolder) continue;
2484
+ for (const file of registryItem.files ?? []) if (file.type === "registry:ui") {
2485
+ const filename = path.basename(file.path);
2486
+ const subfolders = filenameToSubfolders.get(filename) || [];
2487
+ subfolders.push(subfolder);
2488
+ filenameToSubfolders.set(filename, subfolders);
2489
+ }
2490
+ }
2491
+ const uniqueMap = /* @__PURE__ */ new Map();
2492
+ const sharedFilenames = /* @__PURE__ */ new Set();
2493
+ filenameToSubfolders.forEach((subfolders, filename) => {
2494
+ if (subfolders.length === 1) uniqueMap.set(filename, `${subfolders[0]}/${filename}`);
2495
+ else sharedFilenames.add(filename);
2496
+ });
2497
+ return {
2498
+ uniqueMap,
2499
+ sharedFilenames
2500
+ };
2501
+ }
2502
+ /**
2503
+ * Repairs the output of several shadcn runs in one command. Work the next run
2504
+ * depends on happens per item in `install`; project-wide passes (import scan,
2505
+ * package exports, dependency installs) run once in `finish`, which the command
2506
+ * also calls before exiting on a failure so completed items stay usable.
2507
+ */
2508
+ var RegistryInstallBatch = class {
2509
+ constructor(context) {
2510
+ this.context = context;
2511
+ this.items = [];
2512
+ this.written = /* @__PURE__ */ new Set();
2513
+ this.kept = /* @__PURE__ */ new Set();
2514
+ this.bases = getReportedFileBases(context.cwd, context.config, context.uiConfig);
2515
+ }
2516
+ async install({ items, writtenFiles, skippedFiles }) {
2517
+ const { cwd, uiDir, verbose, overwrite } = this.context;
2518
+ const reorganization = await reorganizeRegistryItems(items, uiDir, verbose, overwrite);
2519
+ this.items.push(...items);
2520
+ for (const file of resolveReportedFiles(writtenFiles, this.bases)) this.written.add(file);
2521
+ for (const file of resolveReportedFiles(skippedFiles, this.bases)) this.kept.add(file);
2522
+ for (const file of reorganization.skippedFiles) this.kept.add(path.resolve(uiDir, file));
2523
+ await syncManagedTailwindCss(cwd);
2524
+ const requestedItem = items.at(-1);
2525
+ if (requestedItem?.type === "registry:font") {
2526
+ const nextFont = toManagedAstroFont(requestedItem.name);
2527
+ if (nextFont) {
2528
+ const currentFonts = await readManagedAstroFontsFromProject(cwd);
2529
+ const nextFonts = mergeManagedAstroFonts(currentFonts, nextFont);
2530
+ await syncAstroFontsInProject(cwd, nextFonts, nextFont.cssVariable);
2531
+ await syncAstroManagedFontCss(cwd, nextFont.cssVariable);
2532
+ await cleanupAstroFontPackages(cwd);
2533
+ }
2534
+ }
2535
+ return reorganization;
2536
+ }
2537
+ async finish() {
2538
+ if (!this.items.length) return;
2539
+ const { cwd, config, uiConfig, uiDir, verbose } = this.context;
2540
+ const items = this.items.splice(0);
2541
+ const written = Array.from(this.written);
2542
+ const kept = Array.from(this.kept).filter((file) => !this.written.has(file));
2543
+ this.written.clear();
2544
+ this.kept.clear();
2545
+ await fixAstroImports(cwd, verbose, uiConfig, {
2546
+ kind: "configured-roots",
2547
+ exclude: kept
2548
+ });
2549
+ if (config) {
2550
+ const appFiles = uiConfig ? filesOutsideConfigRoots(cwd, written, uiConfig) : written;
2551
+ if (appFiles.length) await fixAstroImports(cwd, verbose, config, {
2552
+ kind: "files",
2553
+ paths: appFiles
2554
+ });
2555
+ }
2556
+ if (uiConfig) {
2557
+ await repairUiPackageExports(uiDir, uiConfig.resolvedPaths.cwd);
2558
+ if (!config || config.resolvedPaths.cwd === uiConfig.resolvedPaths.cwd) await ensureRegistryDependencies(uiConfig.resolvedPaths.cwd, items);
2559
+ else {
2560
+ await ensureRegistryDependencies(uiConfig.resolvedPaths.cwd, items.filter((item) => item.type !== "registry:block"));
2561
+ const blockItems = items.filter((item) => item.type === "registry:block");
2562
+ if (blockItems.length) await ensureRegistryDependencies(config.resolvedPaths.cwd, blockItems);
2563
+ }
2564
+ }
2565
+ }
2566
+ };
2567
+
2568
+ //#endregion
2569
+ //#region src/utils/shadcn-prompts.ts
2570
+ const QUESTION_PATTERN = /(?:^|\s)\?\s+([^\r\n]+?)\s+›/g;
2571
+ const CONFIRM_PATTERN = /(?:^|\s)\?\s+([^\r\n]+?)\s+›\s*\((?:y\/N|Y\/n)\)/g;
2572
+ const SUMMARY_PATTERN = /(?:Created|Updated|Skipped)\s+\d+\s+file|No files updated/i;
2573
+ /**
2574
+ * Decline every confirm question (overwrites, setup offers), then close stdin
2575
+ * when shadcn finishes its file phase. Any other question is reported through
2576
+ * `onUnsupportedPrompt` instead of being left to hang.
2577
+ */
2578
+ function respondToShadcnPrompts(subprocess, { onUnsupportedPrompt, unsupportedPromptDelayMs = 250 } = {}) {
2579
+ let unsupportedTimer;
2580
+ let waitingOn;
2581
+ let reported = false;
2582
+ function clearUnsupportedTimer() {
2583
+ if (unsupportedTimer) clearTimeout(unsupportedTimer);
2584
+ unsupportedTimer = void 0;
2585
+ waitingOn = void 0;
2586
+ }
2587
+ function inspect(line, answered) {
2588
+ const text = stripVTControlCharacters(line);
2589
+ const stdin = subprocess.stdin;
2590
+ const canAnswer = stdin && !stdin.destroyed && !stdin.writableEnded;
2591
+ if (canAnswer) for (const match of text.matchAll(CONFIRM_PATTERN)) {
2592
+ const message = match[1];
2593
+ if (answered.has(message)) continue;
2594
+ answered.add(message);
2595
+ stdin.write("n\n");
2596
+ if (message === waitingOn) clearUnsupportedTimer();
2597
+ }
2598
+ const message = Array.from(text.matchAll(QUESTION_PATTERN), (match) => match[1]).filter((message) => !answered.has(message)).at(-1);
2599
+ if (message && message !== waitingOn && onUnsupportedPrompt && !reported) {
2600
+ clearUnsupportedTimer();
2601
+ waitingOn = message;
2602
+ unsupportedTimer = setTimeout(() => {
2603
+ reported = true;
2604
+ onUnsupportedPrompt(message);
2605
+ }, unsupportedPromptDelayMs);
2606
+ unsupportedTimer.unref?.();
2607
+ }
2608
+ if (canAnswer && SUMMARY_PATTERN.test(text)) stdin.end();
2609
+ }
2610
+ for (const stream of [subprocess.stdout, subprocess.stderr]) {
2611
+ const decoder = new StringDecoder("utf8");
2612
+ const answered = /* @__PURE__ */ new Set();
2613
+ let pending = "";
2614
+ stream?.on("data", (chunk) => {
2615
+ pending += typeof chunk === "string" ? chunk : decoder.write(chunk);
2616
+ const lines = pending.split("\n");
2617
+ pending = lines.pop();
2618
+ for (const line of lines) {
2619
+ inspect(line, answered);
2620
+ answered.clear();
2621
+ }
2622
+ inspect(pending, answered);
2623
+ });
2624
+ stream?.on("end", clearUnsupportedTimer);
2625
+ }
2626
+ }
2627
+
2274
2628
  //#endregion
2275
2629
  //#region src/commands/add.ts
2630
+ /** shadcn failed; the output has already been reported. */
2631
+ var InstallFailure = class extends Error {
2632
+ constructor(exitCode) {
2633
+ super(`shadcn exited with code ${exitCode}`);
2634
+ this.exitCode = exitCode;
2635
+ }
2636
+ };
2637
+ function isUiRegistryItem(item) {
2638
+ return !item.type || item.type === "registry:ui";
2639
+ }
2640
+ function isBlockRegistryItem(item) {
2641
+ return item?.type === "registry:block";
2642
+ }
2643
+ /** Items a user can pick by name: UI components and blocks. */
2644
+ function isAddableRegistryItem(item) {
2645
+ return isUiRegistryItem(item) || isBlockRegistryItem(item);
2646
+ }
2647
+ /** `--all` is expanded locally, so shadcn must not expand it a second time. */
2648
+ function withoutExpandedAllOption(forwardedOptions) {
2649
+ return forwardedOptions.filter((option) => option !== "--all" && option !== "-a");
2650
+ }
2651
+ /**
2652
+ * shadcn output is captured and re-reported by this command, and silent mode
2653
+ * would hide the file list needed for post-install repair.
2654
+ */
2655
+ function withoutShadcnSilentOption(forwardedOptions) {
2656
+ return forwardedOptions.filter((option) => option !== "--silent" && option !== "-s");
2657
+ }
2658
+ /**
2659
+ * shadcn only knows the registries declared in components.json. The built-in
2660
+ * `@bejamas/<item>` namespace is the default registry (REGISTRY_URL), so it is
2661
+ * passed to shadcn as the bare item name, which resolves to the styled payload.
2662
+ * Bare names, URLs, and third-party namespaces are passed through unchanged.
2663
+ */
2664
+ function toShadcnAddArgument(item) {
2665
+ if (!item.startsWith("@bejamas/")) return item;
2666
+ return resolveBejamasRegistryItemName(item) ?? item;
2667
+ }
2276
2668
  function extractOptionsForShadcn(rawArgv, cmd) {
2277
2669
  if (typeof cmd.getOptionValueSource === "function") {
2278
2670
  const opts = cmd.optsWithGlobals();
2279
- const forwarded$1 = [];
2671
+ const forwarded = [];
2280
2672
  const getSource = (key) => cmd.getOptionValueSource(key);
2281
2673
  const addBoolean = (key, flag, negateFlag) => {
2282
2674
  if (getSource(key) !== "cli") return;
2283
2675
  const value = opts[key];
2284
2676
  if (typeof value !== "boolean") return;
2285
- if (value) forwarded$1.push(flag);
2286
- else if (negateFlag) forwarded$1.push(negateFlag);
2677
+ if (value) forwarded.push(flag);
2678
+ else if (negateFlag) forwarded.push(negateFlag);
2287
2679
  };
2288
2680
  const addString = (key, flag) => {
2289
2681
  if (getSource(key) !== "cli") return;
2290
2682
  const value = opts[key];
2291
- if (typeof value === "string") forwarded$1.push(flag, value);
2683
+ if (typeof value === "string") forwarded.push(flag, value);
2292
2684
  };
2293
2685
  const addOptionalString = (key, flag) => {
2294
2686
  if (getSource(key) !== "cli") return;
2295
2687
  const value = opts[key];
2296
2688
  if (value === true) {
2297
- forwarded$1.push(flag);
2689
+ forwarded.push(flag);
2298
2690
  return;
2299
2691
  }
2300
- if (typeof value === "string") forwarded$1.push(flag, value);
2692
+ if (typeof value === "string") forwarded.push(flag, value);
2301
2693
  };
2302
2694
  addBoolean("yes", "--yes");
2303
2695
  addBoolean("overwrite", "--overwrite");
@@ -2309,19 +2701,19 @@ function extractOptionsForShadcn(rawArgv, cmd) {
2309
2701
  addBoolean("srcDir", "--src-dir", "--no-src-dir");
2310
2702
  addOptionalString("diff", "--diff");
2311
2703
  addOptionalString("view", "--view");
2312
- const addIndex$1 = rawArgv.findIndex((arg) => arg === "add");
2313
- if (addIndex$1 !== -1) {
2314
- const rest$1 = rawArgv.slice(addIndex$1 + 1);
2315
- const doubleDashIndex = rest$1.indexOf("--");
2316
- if (doubleDashIndex !== -1) forwarded$1.push(...rest$1.slice(doubleDashIndex));
2704
+ const addIndex = rawArgv.findIndex((arg) => arg === "add");
2705
+ if (addIndex !== -1) {
2706
+ const rest = rawArgv.slice(addIndex + 1);
2707
+ const doubleDashIndex = rest.indexOf("--");
2708
+ if (doubleDashIndex !== -1) forwarded.push(...rest.slice(doubleDashIndex));
2317
2709
  }
2318
- return forwarded$1;
2710
+ return forwarded;
2319
2711
  }
2320
2712
  const addIndex = rawArgv.findIndex((arg) => arg === "add");
2321
2713
  if (addIndex === -1) return [];
2322
2714
  const rest = rawArgv.slice(addIndex + 1);
2323
2715
  const forwarded = [];
2324
- const optionsWithValues = new Set([
2716
+ const optionsWithValues = /* @__PURE__ */ new Set([
2325
2717
  "-c",
2326
2718
  "--cwd",
2327
2719
  "-p",
@@ -2329,7 +2721,7 @@ function extractOptionsForShadcn(rawArgv, cmd) {
2329
2721
  "--diff",
2330
2722
  "--view"
2331
2723
  ]);
2332
- const filteredFlags = new Set(["-v", "--verbose"]);
2724
+ const filteredFlags = /* @__PURE__ */ new Set(["-v", "--verbose"]);
2333
2725
  for (let index = 0; index < rest.length; index += 1) {
2334
2726
  const token = rest[index];
2335
2727
  if (token === "--") {
@@ -2365,34 +2757,6 @@ function writeCapturedShadcnOutput(stdout, stderr) {
2365
2757
  if (stdout) process.stdout.write(ensureTrailingNewline(stdout));
2366
2758
  if (stderr) process.stderr.write(ensureTrailingNewline(stderr));
2367
2759
  }
2368
- async function buildSubfolderMap(components, uiDir, registryUrl, style) {
2369
- const filenameToSubfolders = /* @__PURE__ */ new Map();
2370
- let requiresReorganization = false;
2371
- for (const componentName of components) {
2372
- const registryItem = await fetchRegistryItem(componentName, registryUrl, style);
2373
- if (!registryItem) continue;
2374
- if (shouldReorganizeRegistryUiFiles(registryItem.files, uiDir)) requiresReorganization = true;
2375
- const subfolder = getSubfolderFromPaths(registryItem.files);
2376
- if (!subfolder) continue;
2377
- for (const file of registryItem.files) if (file.type === "registry:ui") {
2378
- const filename = path.basename(file.path);
2379
- const subfolders = filenameToSubfolders.get(filename) || [];
2380
- subfolders.push(subfolder);
2381
- filenameToSubfolders.set(filename, subfolders);
2382
- }
2383
- }
2384
- const uniqueMap = /* @__PURE__ */ new Map();
2385
- const sharedFilenames = /* @__PURE__ */ new Set();
2386
- filenameToSubfolders.forEach((subfolders, filename) => {
2387
- if (subfolders.length === 1) uniqueMap.set(filename, `${subfolders[0]}/${filename}`);
2388
- else sharedFilenames.add(filename);
2389
- });
2390
- return {
2391
- uniqueMap,
2392
- sharedFilenames,
2393
- requiresReorganization
2394
- };
2395
- }
2396
2760
  function rewritePaths(paths, mapResult) {
2397
2761
  const { uniqueMap, sharedFilenames } = mapResult;
2398
2762
  let currentSubfolder = null;
@@ -2434,14 +2798,16 @@ async function promptForComponents(registryUrl) {
2434
2798
  logger.warn("No components available in registry.");
2435
2799
  return null;
2436
2800
  }
2801
+ const choices = components.filter(isAddableRegistryItem).map((component) => ({
2802
+ title: component.name,
2803
+ value: component.name,
2804
+ description: isBlockRegistryItem(component) ? "block" : void 0
2805
+ }));
2437
2806
  const { selected } = await prompts({
2438
2807
  type: "autocompleteMultiselect",
2439
2808
  name: "selected",
2440
- message: "Which components would you like to add?",
2441
- choices: components.filter((component) => !component.type || component.type === "registry:ui").map((component) => ({
2442
- title: component.name,
2443
- value: component.name
2444
- })),
2809
+ message: "Which components or blocks would you like to add?",
2810
+ choices,
2445
2811
  hint: "- Space to select. Return to submit.",
2446
2812
  instructions: false
2447
2813
  });
@@ -2454,11 +2820,12 @@ function parseShadcnOutput(stdout, stderr) {
2454
2820
  updated: [],
2455
2821
  skipped: []
2456
2822
  };
2457
- const cleanStderr = stderr.replace(/\x1b\[[0-9;]*m/g, "");
2458
- const cleanStdout = stdout.replace(/\x1b\[[0-9;]*m/g, "");
2459
- const createdMatch = cleanStderr.match(/Created\s+(\d+)\s+file/i);
2460
- const updatedMatch = cleanStderr.match(/Updated\s+(\d+)\s+file/i);
2461
- const skippedMatch = cleanStderr.match(/Skipped\s+(\d+)\s+file/i);
2823
+ const cleanStderr = stripVTControlCharacters(stderr);
2824
+ const cleanStdout = stripVTControlCharacters(stdout);
2825
+ const cleanOutput = `${cleanStdout}\n${cleanStderr}`;
2826
+ const createdMatch = cleanOutput.match(/Created\s+(\d+)\s+file/i);
2827
+ const updatedMatch = cleanOutput.match(/Updated\s+(\d+)\s+file/i);
2828
+ const skippedMatch = cleanOutput.match(/Skipped\s+(\d+)\s+file/i);
2462
2829
  const createdCount = createdMatch ? parseInt(createdMatch[1], 10) : 0;
2463
2830
  const updatedCount = updatedMatch ? parseInt(updatedMatch[1], 10) : 0;
2464
2831
  const skippedCount = skippedMatch ? parseInt(skippedMatch[1], 10) : 0;
@@ -2494,20 +2861,33 @@ async function addComponents(cwd, packages, forwardedOptions, isVerbose, isSilen
2494
2861
  ...process.env,
2495
2862
  REGISTRY_URL: resolveRegistryUrl()
2496
2863
  };
2497
- await ensurePinnedShadcnExecPrefix();
2498
- const invocation = buildPinnedShadcnInvocation(buildShadcnAddArgs(packages, forwardedOptions));
2864
+ await PINNED_SHADCN.ensureExecPrefix();
2865
+ const shadcnArgs = buildShadcnAddArgs(packages, forwardedOptions);
2866
+ const invocation = PINNED_SHADCN.invocation(shadcnArgs);
2499
2867
  if (isVerbose) logger.info(`[bejamas-ui] ${invocation.cmd} ${invocation.args.join(" ")}`);
2500
2868
  const registrySpinner = spinner("Checking registry.", { silent: isSilent });
2501
2869
  registrySpinner.start();
2502
2870
  try {
2503
- const result = await execa(invocation.cmd, invocation.args, {
2871
+ const subprocess = execa(invocation.cmd, invocation.args, {
2504
2872
  cwd,
2505
2873
  env,
2506
- input: "n\nn\nn\nn\nn\nn\nn\nn\nn\nn\n",
2874
+ stdin: "pipe",
2507
2875
  stdout: "pipe",
2508
2876
  stderr: "pipe",
2509
2877
  reject: false
2510
2878
  });
2879
+ let unsupportedPrompt;
2880
+ respondToShadcnPrompts(subprocess, { onUnsupportedPrompt(message) {
2881
+ unsupportedPrompt = message;
2882
+ subprocess.kill();
2883
+ } });
2884
+ const result = await subprocess;
2885
+ if (unsupportedPrompt) {
2886
+ registrySpinner.fail();
2887
+ logger.error(`shadcn asked a question that bejamas add cannot answer: "${unsupportedPrompt}"`);
2888
+ writeCapturedShadcnOutput(result.stdout || "", result.stderr || "");
2889
+ throw new InstallFailure(1);
2890
+ }
2511
2891
  registrySpinner.succeed();
2512
2892
  spinner("Installing components.", { silent: isSilent }).succeed();
2513
2893
  const stdout = result.stdout || "";
@@ -2524,13 +2904,14 @@ async function addComponents(cwd, packages, forwardedOptions, isVerbose, isSilen
2524
2904
  } : parseShadcnOutput(stdout, stderr);
2525
2905
  if (result.exitCode !== 0) {
2526
2906
  if (!inspectionMode) writeCapturedShadcnOutput(stdout, stderr);
2527
- process.exit(result.exitCode);
2907
+ throw new InstallFailure(result.exitCode ?? 1);
2528
2908
  }
2529
2909
  return parsed;
2530
- } catch {
2910
+ } catch (error) {
2911
+ if (error instanceof InstallFailure) throw error;
2531
2912
  registrySpinner.fail();
2532
2913
  logger.error("Failed to add components");
2533
- process.exit(1);
2914
+ throw new InstallFailure(1);
2534
2915
  }
2535
2916
  }
2536
2917
  function buildShadcnAddArgs(packages, forwardedOptions) {
@@ -2552,13 +2933,13 @@ const add = new Command().name("add").description("Add components via the Bejama
2552
2933
  const overwriteUsed = forwardedOptions.includes("--overwrite") || forwardedOptions.includes("-o");
2553
2934
  const cwd = opts.cwd || process.cwd();
2554
2935
  let componentsToAdd = packages || [];
2555
- const wantsAll = Boolean(opts.all);
2936
+ const expandsAll = Boolean(opts.all) && componentsToAdd.length === 0;
2556
2937
  const isSilent = opts.silent || false;
2557
2938
  const registryUrl = resolveRegistryUrl();
2558
- if (wantsAll && componentsToAdd.length === 0) {
2939
+ if (expandsAll) {
2559
2940
  const fetchingSpinner = spinner("Fetching available components.", { silent: isSilent }).start();
2560
2941
  try {
2561
- componentsToAdd = (await fetchAvailableComponents(registryUrl)).filter((component) => !component.type || component.type === "registry:ui").map((component) => component.name);
2942
+ componentsToAdd = (await fetchAvailableComponents(registryUrl)).filter(isUiRegistryItem).map((component) => component.name);
2562
2943
  fetchingSpinner.succeed();
2563
2944
  } catch {
2564
2945
  fetchingSpinner.fail();
@@ -2588,54 +2969,60 @@ const add = new Command().name("add").description("Add components via the Bejama
2588
2969
  }
2589
2970
  const activeStyle = uiConfig?.style || config?.style || "bejamas-juno";
2590
2971
  const totalComponents = componentsToAdd.length;
2591
- for (let index = 0; index < componentsToAdd.length; index += 1) {
2592
- const component = componentsToAdd[index];
2593
- if (totalComponents > 1 && !isSilent) {
2594
- logger.break();
2595
- logger.info(highlighter.info(`[${index + 1}/${totalComponents}]`) + ` Adding ${highlighter.success(component)}...`);
2596
- }
2597
- const subfolderMapResult = inspectionMode ? {
2598
- uniqueMap: /* @__PURE__ */ new Map(),
2599
- sharedFilenames: /* @__PURE__ */ new Set(),
2600
- requiresReorganization: false
2601
- } : await buildSubfolderMap([component], uiDir, registryUrl, activeStyle);
2602
- const parsed = await addComponents(cwd, [component], forwardedOptions, verbose, isSilent, inspectionMode);
2603
- if (!inspectionMode) {
2604
- await syncManagedTailwindCss(cwd);
2605
- const registryItem = await fetchRegistryItem(component, registryUrl, activeStyle);
2606
- if (registryItem?.type === "registry:font") {
2607
- const nextFont = toManagedAstroFont(registryItem.name);
2608
- if (nextFont) {
2609
- await syncAstroFontsInProject(cwd, mergeManagedAstroFonts(await readManagedAstroFontsFromProject(cwd), nextFont), nextFont.cssVariable);
2610
- await syncAstroManagedFontCss(cwd, nextFont.cssVariable);
2611
- await cleanupAstroFontPackages(cwd);
2612
- }
2613
- }
2614
- }
2615
- let skippedCount = 0;
2616
- if (!inspectionMode && uiDir && subfolderMapResult.requiresReorganization) skippedCount = (await reorganizeComponents([component], uiDir, registryUrl, verbose, activeStyle)).skippedFiles.length;
2617
- if (!isSilent && !inspectionMode) {
2618
- const actuallyCreated = Math.max(0, parsed.created.length - skippedCount);
2619
- if (actuallyCreated > 0) {
2620
- const createdPaths = rewritePaths(parsed.created.slice(0, actuallyCreated), subfolderMapResult);
2621
- logger.success(`Created ${createdPaths.length} file${createdPaths.length > 1 ? "s" : ""}:`);
2622
- for (const file of createdPaths) logger.log(` ${highlighter.info("-")} ${file}`);
2623
- }
2624
- if (parsed.updated.length > 0) {
2625
- const updatedPaths = rewritePaths(Array.from(new Set(parsed.updated)), subfolderMapResult);
2626
- logger.info(`Updated ${updatedPaths.length} file${updatedPaths.length > 1 ? "s" : ""}:`);
2627
- for (const file of updatedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
2972
+ const outputBearingOptions = withoutShadcnSilentOption(forwardedOptions);
2973
+ const addOptions = expandsAll ? withoutExpandedAllOption(outputBearingOptions) : outputBearingOptions;
2974
+ const batch = new RegistryInstallBatch({
2975
+ cwd,
2976
+ config,
2977
+ uiConfig,
2978
+ uiDir,
2979
+ verbose,
2980
+ overwrite: overwriteUsed
2981
+ });
2982
+ try {
2983
+ for (let index = 0; index < componentsToAdd.length; index += 1) {
2984
+ const component = componentsToAdd[index];
2985
+ if (totalComponents > 1 && !isSilent) {
2986
+ logger.break();
2987
+ logger.info(highlighter.info(`[${index + 1}/${totalComponents}]`) + ` Adding ${highlighter.success(component)}...`);
2628
2988
  }
2629
- if (skippedCount > 0) logger.info(`Skipped ${skippedCount} file${skippedCount > 1 ? "s" : ""}: (already exists)`);
2630
- if (parsed.skipped.length > 0) {
2631
- const skippedPaths = rewritePaths(parsed.skipped, subfolderMapResult);
2632
- logger.info(formatSkippedFilesHeading(skippedPaths.length, overwriteUsed));
2633
- for (const file of skippedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
2989
+ const items = inspectionMode ? [] : await fetchRegistryTree([component], registryUrl, activeStyle);
2990
+ const subfolderMapResult = buildSubfolderMap(items);
2991
+ const parsed = await addComponents(cwd, [toShadcnAddArgument(component)], addOptions, verbose, isSilent, inspectionMode);
2992
+ if (inspectionMode) continue;
2993
+ const { skippedFiles } = await batch.install({
2994
+ items,
2995
+ writtenFiles: [...parsed.created, ...parsed.updated],
2996
+ skippedFiles: parsed.skipped
2997
+ });
2998
+ const skippedCount = skippedFiles.length;
2999
+ if (!isSilent) {
3000
+ const actuallyCreated = Math.max(0, parsed.created.length - skippedCount);
3001
+ if (actuallyCreated > 0) {
3002
+ const createdPaths = rewritePaths(parsed.created.slice(0, actuallyCreated), subfolderMapResult);
3003
+ logger.success(`Created ${createdPaths.length} file${createdPaths.length > 1 ? "s" : ""}:`);
3004
+ for (const file of createdPaths) logger.log(` ${highlighter.info("-")} ${file}`);
3005
+ }
3006
+ if (parsed.updated.length > 0) {
3007
+ const updatedPaths = rewritePaths(Array.from(new Set(parsed.updated)), subfolderMapResult);
3008
+ logger.info(`Updated ${updatedPaths.length} file${updatedPaths.length > 1 ? "s" : ""}:`);
3009
+ for (const file of updatedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
3010
+ }
3011
+ if (skippedCount > 0) logger.info(`Skipped ${skippedCount} file${skippedCount > 1 ? "s" : ""}: (already exists)`);
3012
+ if (parsed.skipped.length > 0) {
3013
+ const skippedPaths = rewritePaths(parsed.skipped, subfolderMapResult);
3014
+ logger.info(formatSkippedFilesHeading(skippedPaths.length, overwriteUsed));
3015
+ for (const file of skippedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
3016
+ }
3017
+ if (actuallyCreated === 0 && parsed.updated.length === 0 && skippedCount === 0 && parsed.skipped.length === 0) logger.info("Already up to date.");
2634
3018
  }
2635
- if (actuallyCreated === 0 && parsed.updated.length === 0 && skippedCount === 0 && parsed.skipped.length === 0) logger.info("Already up to date.");
2636
3019
  }
3020
+ } catch (error) {
3021
+ await batch.finish();
3022
+ if (error instanceof InstallFailure) process.exit(error.exitCode);
3023
+ throw error;
2637
3024
  }
2638
- if (!inspectionMode) await fixAstroImports(cwd, verbose);
3025
+ await batch.finish();
2639
3026
  });
2640
3027
 
2641
3028
  //#endregion
@@ -2802,14 +3189,14 @@ const apply = new Command().name("apply").description("apply a preset to an exis
2802
3189
  only: explicitOnly
2803
3190
  });
2804
3191
  const preflight = await preFlightApply(options);
2805
- if (preflight.errors[MISSING_DIR_OR_EMPTY_PROJECT]) {
3192
+ if (preflight.errors["1"]) {
2806
3193
  logger.break();
2807
3194
  logger.error(`The ${highlighter.info("apply")} command only works in an existing project.`);
2808
3195
  logger.error(`Run ${highlighter.info(getInitCommand(rawPreset))} first.`);
2809
3196
  logger.break();
2810
3197
  process.exit(1);
2811
3198
  }
2812
- if (preflight.errors[MISSING_CONFIG]) {
3199
+ if (preflight.errors["3"]) {
2813
3200
  logger.break();
2814
3201
  logger.error(`No ${highlighter.info("components.json")} found at ${highlighter.info(options.cwd)}.`);
2815
3202
  logger.error(`Run ${highlighter.info(getInitCommand(rawPreset))} first.`);
@@ -2890,9 +3277,9 @@ function resolveApplyPreset(options) {
2890
3277
  if (positionalPreset && flagPreset && positionalPreset !== flagPreset) throw new ApplyPresetError(`Received two different preset values. Use either the positional preset or ${highlighter.info("--preset")}, or pass the same value to both.`);
2891
3278
  return flagPreset ?? positionalPreset;
2892
3279
  }
2893
- function getPresetUrlOnly(preset$1) {
2894
- if (!isUrl(preset$1)) return;
2895
- return new URL(preset$1).searchParams.get("only") ?? void 0;
3280
+ function getPresetUrlOnly(preset) {
3281
+ if (!isUrl(preset)) return;
3282
+ return new URL(preset).searchParams.get("only") ?? void 0;
2896
3283
  }
2897
3284
  function resolveApplyOnly(value) {
2898
3285
  if (value === void 0 || value === false) return;
@@ -2922,27 +3309,27 @@ function validateApplyOnlyPreset(options) {
2922
3309
  if (!options.only || options.preset) return;
2923
3310
  throw new ApplyOnlyError(["Missing preset for --only.", "Use: bejamas apply <preset> --only theme,font."].join("\n"));
2924
3311
  }
2925
- function resolveApplyPresetSource(preset$1) {
2926
- if (isPresetCode(preset$1)) {
2927
- validatePresetCode(preset$1);
2928
- return { preset: preset$1 };
3312
+ function resolveApplyPresetSource(preset) {
3313
+ if (isPresetCode(preset)) {
3314
+ validatePresetCode(preset);
3315
+ return { preset };
2929
3316
  }
2930
- if (!isUrl(preset$1)) throw new ApplyPresetError(`Invalid preset: ${highlighter.info(preset$1)}. Use a preset code or a Bejamas create/init URL containing a preset code.`);
2931
- const url$1 = new URL(preset$1);
2932
- if (!isSupportedPresetUrl(url$1)) throw new ApplyPresetError(`Invalid preset URL: ${highlighter.info(preset$1)}. Use a Bejamas create or init URL containing a preset code.`);
2933
- const code = url$1.searchParams.get("preset")?.trim();
2934
- if (!code) throw new ApplyPresetError(`Invalid preset URL: ${highlighter.info(preset$1)}. Expected a URL containing a preset query parameter.`);
3317
+ if (!isUrl(preset)) throw new ApplyPresetError(`Invalid preset: ${highlighter.info(preset)}. Use a preset code or a Bejamas create/init URL containing a preset code.`);
3318
+ const url = new URL(preset);
3319
+ if (!isSupportedPresetUrl(url)) throw new ApplyPresetError(`Invalid preset URL: ${highlighter.info(preset)}. Use a Bejamas create or init URL containing a preset code.`);
3320
+ const code = url.searchParams.get("preset")?.trim();
3321
+ if (!code) throw new ApplyPresetError(`Invalid preset URL: ${highlighter.info(preset)}. Expected a URL containing a preset query parameter.`);
2935
3322
  validatePresetCode(code);
2936
3323
  return {
2937
3324
  preset: code,
2938
- themeRef: url$1.searchParams.get("themeRef") ?? void 0,
2939
- only: url$1.searchParams.get("only") ?? void 0
3325
+ themeRef: url.searchParams.get("themeRef") ?? void 0,
3326
+ only: url.searchParams.get("only") ?? void 0
2940
3327
  };
2941
3328
  }
2942
3329
  async function preFlightApply(options) {
2943
3330
  const errors = {};
2944
3331
  if (!fs.existsSync(options.cwd) || !fs.existsSync(path.resolve(options.cwd, "package.json"))) {
2945
- errors[MISSING_DIR_OR_EMPTY_PROJECT] = true;
3332
+ errors["1"] = true;
2946
3333
  return {
2947
3334
  errors,
2948
3335
  config: null
@@ -2964,7 +3351,7 @@ async function preFlightApply(options) {
2964
3351
  process.exit(1);
2965
3352
  }
2966
3353
  }
2967
- errors[MISSING_CONFIG] = true;
3354
+ errors["3"] = true;
2968
3355
  return {
2969
3356
  errors,
2970
3357
  config: null
@@ -3015,28 +3402,29 @@ async function confirmApply(options) {
3015
3402
  process.exit(1);
3016
3403
  }
3017
3404
  }
3018
- async function runPartialApply(cwd, designConfig, preset$1, only) {
3405
+ async function runPartialApply(cwd, designConfig, preset, only) {
3019
3406
  if (only.includes("theme")) {
3020
- const initUrl = buildInitUrl(designConfig, preset$1.themeRef);
3021
- await applyDesignSystemThemeToProject(cwd, designConfig, { themeVars: preset$1.themeRef ? await fetchInitThemeVars(initUrl) ?? void 0 : void 0 });
3407
+ const initUrl = buildInitUrl(designConfig, preset.themeRef);
3408
+ const themeVars = preset.themeRef ? await fetchInitThemeVars(initUrl) ?? void 0 : void 0;
3409
+ await applyDesignSystemThemeToProject(cwd, designConfig, { themeVars });
3022
3410
  await fixAstroImports(cwd, false);
3023
3411
  }
3024
3412
  if (only.includes("font")) await applyDesignSystemFontToProject(cwd, designConfig);
3025
3413
  }
3026
- function resolveApplyDesignSystemConfig(preset$1, existingConfig) {
3414
+ function resolveApplyDesignSystemConfig(preset, existingConfig) {
3027
3415
  return resolveDesignSystemConfig({
3028
- preset: preset$1.preset,
3416
+ preset: preset.preset,
3029
3417
  template: "astro",
3030
3418
  rtl: existingConfig.rtl ?? false,
3031
3419
  baseColor: void 0,
3032
3420
  lang: void 0
3033
3421
  });
3034
3422
  }
3035
- function validatePresetCode(preset$1) {
3036
- if (!isPresetCode(preset$1) || !decodePreset(preset$1)) throw new ApplyPresetError(`Invalid preset code: ${highlighter.info(preset$1)}.`);
3423
+ function validatePresetCode(preset) {
3424
+ if (!isPresetCode(preset) || !decodePreset(preset)) throw new ApplyPresetError(`Invalid preset code: ${highlighter.info(preset)}.`);
3037
3425
  }
3038
3426
  async function getApplyConfigPaths(config) {
3039
- const configPaths = new Set([path.resolve(config.resolvedPaths.cwd, "components.json")]);
3427
+ const configPaths = /* @__PURE__ */ new Set([path.resolve(config.resolvedPaths.cwd, "components.json")]);
3040
3428
  const workspaceConfig = await getWorkspaceConfig(config);
3041
3429
  if (workspaceConfig) for (const linkedConfig of Object.values(workspaceConfig)) configPaths.add(path.resolve(linkedConfig.resolvedPaths.cwd, "components.json"));
3042
3430
  const existingPaths = [];
@@ -3110,9 +3498,9 @@ async function getApplyWorkspaceConfigs(config) {
3110
3498
  function quoteShellArg(value) {
3111
3499
  return /[^A-Za-z0-9_./:-]/.test(value) ? JSON.stringify(value) : value;
3112
3500
  }
3113
- function getInitCommand(preset$1) {
3114
- if (!preset$1) return "bejamas init";
3115
- return `bejamas init --preset ${quoteShellArg(preset$1)}`;
3501
+ function getInitCommand(preset) {
3502
+ if (!preset) return "bejamas init";
3503
+ return `bejamas init --preset ${quoteShellArg(preset)}`;
3116
3504
  }
3117
3505
  function isUrl(value) {
3118
3506
  try {
@@ -3122,8 +3510,8 @@ function isUrl(value) {
3122
3510
  return false;
3123
3511
  }
3124
3512
  }
3125
- function isSupportedPresetUrl(url$1) {
3126
- const pathname = url$1.pathname.replace(/\/+$/, "");
3513
+ function isSupportedPresetUrl(url) {
3514
+ const pathname = url.pathname.replace(/\/+$/, "");
3127
3515
  return pathname === "/create" || pathname === "/init";
3128
3516
  }
3129
3517
 
@@ -3159,13 +3547,14 @@ const PRESET_ICON_LIBRARY_SET = new Set(PRESET_ICON_LIBRARIES);
3159
3547
  const PRESET_MENU_ACCENT_SET = new Set(PRESET_MENU_ACCENTS);
3160
3548
  const PRESET_MENU_COLOR_SET = new Set(PRESET_MENU_COLORS);
3161
3549
  const PRESET_STYLE_SET = new Set(PRESET_STYLES);
3550
+ new Set(PRESET_THEMES);
3162
3551
  const ROOT_FONT_VARIABLES = [
3163
3552
  "--font-sans",
3164
3553
  "--font-serif",
3165
3554
  "--font-mono"
3166
3555
  ];
3167
3556
  const ROOT_FONT_VARIABLE_SET = new Set(ROOT_FONT_VARIABLES);
3168
- const FONT_VARIABLE_SET = new Set([...ROOT_FONT_VARIABLES, "--font-heading"]);
3557
+ const FONT_VARIABLE_SET = /* @__PURE__ */ new Set([...ROOT_FONT_VARIABLES, "--font-heading"]);
3169
3558
  async function resolveProjectPreset(config) {
3170
3559
  const style = normalizePresetStyle(config.style);
3171
3560
  if (!style) return {
@@ -3463,31 +3852,31 @@ function printPresetDecode(result) {
3463
3852
  values: result.values
3464
3853
  }, { fallbackNote: " * Compatibility value for older preset versions." });
3465
3854
  }
3466
- function printPresetInfo(preset$1, options = {}) {
3855
+ function printPresetInfo(preset, options = {}) {
3467
3856
  logger.log(highlighter.info("Preset"));
3468
- if (!preset$1.code || !preset$1.values) {
3857
+ if (!preset.code || !preset.values) {
3469
3858
  printEntries({ code: "-" });
3470
3859
  return;
3471
3860
  }
3472
- const fallbacks = preset$1.fallbacks ?? [];
3861
+ const fallbacks = preset.fallbacks ?? [];
3473
3862
  const formatPresetValue = (key, value) => {
3474
3863
  const suffix = fallbacks.includes(key) ? "*" : "";
3475
3864
  return `${value ?? "-"}${suffix}`;
3476
3865
  };
3477
3866
  printEntries({
3478
- code: preset$1.code,
3479
- version: preset$1.code[0],
3480
- style: preset$1.values.style,
3481
- baseColor: formatPresetValue("baseColor", preset$1.values.baseColor),
3482
- theme: formatPresetValue("theme", preset$1.values.theme),
3483
- chartColor: formatPresetValue("chartColor", preset$1.values.chartColor),
3484
- iconLibrary: formatPresetValue("iconLibrary", preset$1.values.iconLibrary),
3485
- font: formatPresetValue("font", preset$1.values.font),
3486
- fontHeading: formatPresetValue("fontHeading", preset$1.values.fontHeading),
3487
- radius: formatPresetValue("radius", preset$1.values.radius),
3488
- menuAccent: formatPresetValue("menuAccent", preset$1.values.menuAccent),
3489
- menuColor: formatPresetValue("menuColor", preset$1.values.menuColor),
3490
- url: getPresetUrl(preset$1.code)
3867
+ code: preset.code,
3868
+ version: preset.code[0],
3869
+ style: preset.values.style,
3870
+ baseColor: formatPresetValue("baseColor", preset.values.baseColor),
3871
+ theme: formatPresetValue("theme", preset.values.theme),
3872
+ chartColor: formatPresetValue("chartColor", preset.values.chartColor),
3873
+ iconLibrary: formatPresetValue("iconLibrary", preset.values.iconLibrary),
3874
+ font: formatPresetValue("font", preset.values.font),
3875
+ fontHeading: formatPresetValue("fontHeading", preset.values.fontHeading),
3876
+ radius: formatPresetValue("radius", preset.values.radius),
3877
+ menuAccent: formatPresetValue("menuAccent", preset.values.menuAccent),
3878
+ menuColor: formatPresetValue("menuColor", preset.values.menuColor),
3879
+ url: getPresetUrl(preset.code)
3491
3880
  });
3492
3881
  if (fallbacks.length > 0) {
3493
3882
  logger.log("");
@@ -3587,10 +3976,32 @@ function handlePresetError(error) {
3587
3976
  process.exit(1);
3588
3977
  }
3589
3978
 
3979
+ //#endregion
3980
+ //#region ../bench/package.json
3981
+ var version = "0.1.0";
3982
+
3983
+ //#endregion
3984
+ //#region src/commands/bench.ts
3985
+ const PINNED_BENCH = definePinnedPackage({
3986
+ name: "@bejamas/bench",
3987
+ version,
3988
+ bin: "bejamas-bench",
3989
+ prefixDir: "bejamas-bench"
3990
+ });
3991
+ const bench = new Command().name("bench").description("compare an original site with its ported version (runs @bejamas/bench)").usage("--original <url> --ported <url> [options]").argument("[args...]", "run `bejamas bench --help` for all options").helpOption(false).allowUnknownOption().passThroughOptions().allowExcessArguments().action(async (args) => {
3992
+ await PINNED_BENCH.ensureExecPrefix();
3993
+ const invocation = PINNED_BENCH.invocation(args);
3994
+ const result = await execa(invocation.cmd, invocation.args, {
3995
+ stdio: "inherit",
3996
+ reject: false
3997
+ });
3998
+ process.exit(result.exitCode ?? 2);
3999
+ });
4000
+
3590
4001
  //#endregion
3591
4002
  //#region src/index.ts
3592
4003
  const pkg = createRequire(import.meta.url)("../package.json");
3593
- const program = new Command().name("bejamas").description("bejamas/ui cli").configureHelp({ helpWidth: Math.min(100, process.stdout.columns || 100) }).version(pkg.version, "-v, --version", "output the version number");
4004
+ const program = new Command().name("bejamas").description("bejamas/ui cli").configureHelp({ helpWidth: Math.min(100, process.stdout.columns || 100) }).version(pkg.version, "-v, --version", "output the version number").enablePositionalOptions();
3594
4005
  program.addCommand(init);
3595
4006
  program.addCommand(add);
3596
4007
  program.addCommand(apply);
@@ -3599,9 +4010,9 @@ program.addCommand(info);
3599
4010
  program.addCommand(docs);
3600
4011
  program.addCommand(docsBuild);
3601
4012
  program.addCommand(docsCheck);
4013
+ program.addCommand(bench);
3602
4014
  program.parse(process.argv);
3603
- var src_default = program;
3604
4015
 
3605
4016
  //#endregion
3606
- export { src_default as default };
4017
+ export { program as default };
3607
4018
  //# sourceMappingURL=index.js.map