bejamas 0.0.0-canary.0138fd3

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 ADDED
@@ -0,0 +1,3717 @@
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-Cxc_kZGv.js";
3
+ import { Command } from "commander";
4
+ import { createRequire } from "module";
5
+ import path, { extname, isAbsolute, join, relative, resolve } from "node:path";
6
+ import { z } from "zod";
7
+ import path$1 from "path";
8
+ import { fileURLToPath } from "url";
9
+ import { existsSync, promises, readFileSync, readdirSync } from "node:fs";
10
+ import fs from "fs-extra";
11
+ import fg from "fast-glob";
12
+ import fs$1 from "node:fs/promises";
13
+ import { bold, cyan, dim, green, magenta, red, white, yellow } from "kleur/colors";
14
+ import os from "os";
15
+ import dotenv from "dotenv";
16
+ import { detect } from "@antfu/ni";
17
+ import { execa } from "execa";
18
+ import prompts from "prompts";
19
+ import * as tar from "tar";
20
+ import os$1 from "node:os";
21
+
22
+ //#region src/registry/context.ts
23
+ let context = { headers: {} };
24
+ function clearRegistryContext() {
25
+ context.headers = {};
26
+ }
27
+
28
+ //#endregion
29
+ //#region src/preflights/preflight-init.ts
30
+ async function preFlightInit(options) {
31
+ const errors = {};
32
+ if (!fs.existsSync(options.cwd) || !fs.existsSync(path$1.resolve(options.cwd, "package.json"))) {
33
+ errors["1"] = true;
34
+ return {
35
+ errors,
36
+ projectInfo: null
37
+ };
38
+ }
39
+ return {
40
+ errors,
41
+ projectInfo: null
42
+ };
43
+ }
44
+
45
+ //#endregion
46
+ //#region src/utils/astro-fonts.ts
47
+ const ASTRO_CONFIG_BLOCK_START = "// bejamas:astro-fonts:start";
48
+ const ASTRO_CONFIG_BLOCK_END = "// bejamas:astro-fonts:end";
49
+ const ASTRO_LAYOUT_BLOCK_START = "<!-- bejamas:astro-fonts:start -->";
50
+ const ASTRO_LAYOUT_BLOCK_END = "<!-- bejamas:astro-fonts:end -->";
51
+ const ASTRO_FONT_CONSTANT = "BEJAMAS_ASTRO_FONTS";
52
+ const ASTRO_CONFIG_CANDIDATES = [
53
+ "astro.config.mjs",
54
+ "apps/web/astro.config.mjs",
55
+ "apps/docs/astro.config.mjs"
56
+ ];
57
+ const ASTRO_LAYOUT_CANDIDATES = ["src/layouts/Layout.astro", "apps/web/src/layouts/Layout.astro"];
58
+ const STARLIGHT_HEAD_RELATIVE_PATH = "apps/docs/src/components/Head.astro";
59
+ const ASTRO_FONT_PROVIDERS = ["fontsource", "google"];
60
+ const ASTRO_FONTSOURCE_FONT_NAMES = /* @__PURE__ */ new Set(["geist", "geist-mono"]);
61
+ const MANAGED_BODY_FONT_CLASSES = /* @__PURE__ */ new Set(["font-mono", "font-serif"]);
62
+ function compactSource$1(source) {
63
+ return source.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
64
+ }
65
+ function escapeRegExp$2(value) {
66
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
67
+ }
68
+ function upsertImport(source, moduleName, importedName, fallbackNames = []) {
69
+ const importPattern = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']${escapeRegExp$2(moduleName)}["'];?`);
70
+ if (importPattern.test(source)) return source.replace(importPattern, (_match, importsSource) => {
71
+ const imports = importsSource.split(",").map((value) => value.trim()).filter(Boolean);
72
+ if (!imports.includes(importedName)) imports.push(importedName);
73
+ for (const name of fallbackNames) if (!imports.includes(name)) imports.unshift(name);
74
+ return `import { ${Array.from(new Set(imports)).join(", ")} } from "${moduleName}";`;
75
+ });
76
+ const importLine = `import { ${Array.from(/* @__PURE__ */ new Set([...fallbackNames, importedName])).join(", ")} } from "${moduleName}";`;
77
+ const frontmatterEnd = source.indexOf("---", 3);
78
+ if (source.startsWith("---") && frontmatterEnd !== -1) return `${source.slice(0, frontmatterEnd).trimEnd()}\n${importLine}\n${source.slice(frontmatterEnd)}`;
79
+ return `${importLine}\n${source}`;
80
+ }
81
+ function buildAstroConfigFontBlock(fontsToSerialize) {
82
+ 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 subsets: ${JSON.stringify(font.subsets)},\n },`).join("\n")}\n`;
83
+ return `${ASTRO_CONFIG_BLOCK_START}\n/** @type {NonNullable<import("astro").AstroUserConfig["fonts"]>} */\nconst ${ASTRO_FONT_CONSTANT} = [${body}];\n${ASTRO_CONFIG_BLOCK_END}`;
84
+ }
85
+ function buildAstroLayoutFontBlock(fontsToSerialize) {
86
+ const body = fontsToSerialize.map((font) => `<Font cssVariable="${font.cssVariable}" />`).join("\n");
87
+ return `${ASTRO_LAYOUT_BLOCK_START}\n${body}\n${ASTRO_LAYOUT_BLOCK_END}`;
88
+ }
89
+ function bodyFontClassFromCssVariable(cssVariable) {
90
+ if (cssVariable === "--font-mono" || cssVariable === "--font-serif") return fontClassFromCssVariable(cssVariable);
91
+ return "";
92
+ }
93
+ function upsertManagedBodyFontClass(source, cssVariable) {
94
+ const bodyClass = bodyFontClassFromCssVariable(cssVariable);
95
+ const bodyTagPattern = /<body\b([^>]*?)(\/?)>/m;
96
+ if (!bodyTagPattern.test(source)) return source;
97
+ return source.replace(bodyTagPattern, (_match, attrs, selfClosing) => {
98
+ const classPattern = /\sclass=(["'])(.*?)\1/m;
99
+ const classMatch = attrs.match(classPattern);
100
+ const quote = classMatch?.[1] ?? "\"";
101
+ const existingClasses = (classMatch?.[2] ?? "").split(/\s+/).filter(Boolean).filter((className) => !MANAGED_BODY_FONT_CLASSES.has(className));
102
+ const nextClasses = bodyClass ? Array.from(/* @__PURE__ */ new Set([...existingClasses, bodyClass])) : existingClasses;
103
+ let nextAttrs = attrs.replace(classPattern, "");
104
+ if (nextClasses.length > 0) nextAttrs = `${nextAttrs} class=${quote}${nextClasses.join(" ")}${quote}`;
105
+ return `<body${nextAttrs}${selfClosing}>`;
106
+ });
107
+ }
108
+ function parseManagedAstroFonts(source) {
109
+ 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)}`);
110
+ const match = source.match(markerPattern);
111
+ if (!match) return [];
112
+ return (match[1].match(/\{[\s\S]*?\}/g) ?? []).map((entry) => {
113
+ const name = entry.match(/name:\s*"([^"]+)"/)?.[1];
114
+ const cssVariable = entry.match(/cssVariable:\s*"([^"]+)"/)?.[1];
115
+ const provider = entry.match(/provider:\s*fontProviders\.([a-z]+)\(\)/)?.[1];
116
+ const subsetsSource = entry.match(/subsets:\s*(\[[^\]]*\])/s)?.[1];
117
+ if (!name || !cssVariable || !provider || !ASTRO_FONT_PROVIDERS.includes(provider)) return null;
118
+ let subsets = ["latin"];
119
+ if (subsetsSource) try {
120
+ const parsed = JSON.parse(subsetsSource);
121
+ if (Array.isArray(parsed) && parsed.length > 0 && parsed.every((value) => typeof value === "string")) subsets = parsed;
122
+ } catch {
123
+ subsets = ["latin"];
124
+ }
125
+ return {
126
+ name,
127
+ cssVariable,
128
+ provider,
129
+ subsets
130
+ };
131
+ }).filter((value) => value !== null);
132
+ }
133
+ function normalizeManagedAstroFontSubsets(subsets) {
134
+ if (!subsets?.length) return ["latin"];
135
+ return [subsets[0], ...subsets.slice(1)];
136
+ }
137
+ function replaceManagedBlock(source, nextBlock, start, end) {
138
+ const blockPattern = new RegExp(`${escapeRegExp$2(start)}[\\s\\S]*?${escapeRegExp$2(end)}`, "m");
139
+ if (blockPattern.test(source)) return source.replace(blockPattern, nextBlock);
140
+ return null;
141
+ }
142
+ function stripExperimentalFontsProperty(source) {
143
+ const experimentalMatch = source.match(/experimental\s*:\s*\{/);
144
+ if (!experimentalMatch || experimentalMatch.index === void 0) return source;
145
+ const propertyStart = experimentalMatch.index;
146
+ const openingBraceIndex = propertyStart + experimentalMatch[0].lastIndexOf("{");
147
+ let depth = 0;
148
+ let closingBraceIndex = -1;
149
+ let quote = null;
150
+ for (let index = openingBraceIndex; index < source.length; index += 1) {
151
+ const character = source[index];
152
+ const previousCharacter = index > 0 ? source[index - 1] : "";
153
+ if (quote) {
154
+ if (character === quote && previousCharacter !== "\\") quote = null;
155
+ continue;
156
+ }
157
+ if (character === "\"" || character === "'" || character === "`") {
158
+ quote = character;
159
+ continue;
160
+ }
161
+ if (character === "{") {
162
+ depth += 1;
163
+ continue;
164
+ }
165
+ if (character === "}") {
166
+ depth -= 1;
167
+ if (depth === 0) {
168
+ closingBraceIndex = index;
169
+ break;
170
+ }
171
+ }
172
+ }
173
+ if (closingBraceIndex === -1) return source;
174
+ const nextBlockContent = source.slice(openingBraceIndex + 1, closingBraceIndex).replace(/(^|\n)\s*fonts\s*:\s*BEJAMAS_ASTRO_FONTS\s*,?\s*(?=\n|$)/g, "$1").replace(/\n{3,}/g, "\n\n");
175
+ let propertyEnd = closingBraceIndex + 1;
176
+ while (propertyEnd < source.length && /\s/.test(source[propertyEnd])) propertyEnd += 1;
177
+ if (source[propertyEnd] === ",") propertyEnd += 1;
178
+ if (nextBlockContent.trim().length === 0) return `${source.slice(0, propertyStart)}${source.slice(propertyEnd)}`;
179
+ const rebuiltProperty = `experimental: {${nextBlockContent}\n }`;
180
+ return `${source.slice(0, propertyStart)}${rebuiltProperty}${source.slice(closingBraceIndex + 1)}`;
181
+ }
182
+ function patchAstroConfigSource(source, fontsToSerialize, options = {}) {
183
+ let next = upsertImport(source, "astro/config", "fontProviders", ["defineConfig"]);
184
+ const fontBlock = buildAstroConfigFontBlock(fontsToSerialize);
185
+ const replacedBlock = replaceManagedBlock(next, fontBlock, ASTRO_CONFIG_BLOCK_START, ASTRO_CONFIG_BLOCK_END);
186
+ if (replacedBlock) next = replacedBlock;
187
+ else {
188
+ const lastImport = Array.from(next.matchAll(/^import .*$/gm)).at(-1);
189
+ if (lastImport?.index !== void 0) {
190
+ const insertAt = lastImport.index + lastImport[0].length;
191
+ next = `${next.slice(0, insertAt)}\n\n${fontBlock}${next.slice(insertAt)}`;
192
+ } else next = `${fontBlock}\n\n${next}`;
193
+ }
194
+ next = stripExperimentalFontsProperty(next);
195
+ if (!/^\s*fonts:\s*BEJAMAS_ASTRO_FONTS\b/m.test(next)) next = next.replace(/defineConfig\(\s*\{/, `defineConfig({\n fonts: ${ASTRO_FONT_CONSTANT},`);
196
+ if (options.starlightHeadOverride) {
197
+ if (next.includes("Head: \"./src/components/Head.astro\"")) return compactSource$1(next);
198
+ const starlightComponentsPattern = /starlight\(\{\s*([\s\S]*?)components:\s*\{([\s\S]*?)\}/m;
199
+ if (starlightComponentsPattern.test(next)) next = next.replace(starlightComponentsPattern, (_match, before, inner) => `starlight({${before}components: {\n Head: "./src/components/Head.astro",${inner ? `${inner}` : ""}\n }`);
200
+ else next = next.replace(/starlight\(\s*\{/, `starlight({\n components: {\n Head: "./src/components/Head.astro",\n },`);
201
+ }
202
+ return compactSource$1(next);
203
+ }
204
+ function patchAstroLayoutSource(source, fontsToSerialize, activeFontCssVariable) {
205
+ let next = upsertImport(source, "astro:assets", "Font");
206
+ const fontBlock = buildAstroLayoutFontBlock(fontsToSerialize);
207
+ const replacedBlock = replaceManagedBlock(next, fontBlock, ASTRO_LAYOUT_BLOCK_START, ASTRO_LAYOUT_BLOCK_END);
208
+ if (replacedBlock) next = replacedBlock;
209
+ else if (next.includes("<head>")) next = next.replace("<head>", `<head>\n ${fontBlock.replace(/\n/g, "\n ")}`);
210
+ else if (next.includes("</head>")) next = next.replace("</head>", ` ${fontBlock.replace(/\n/g, "\n ")}\n</head>`);
211
+ else next = `${next}\n${fontBlock}\n`;
212
+ next = upsertManagedBodyFontClass(next, activeFontCssVariable);
213
+ return compactSource$1(next);
214
+ }
215
+ function patchStarlightHeadSource(source, fontsToSerialize) {
216
+ let next = upsertImport(source, "@astrojs/starlight/components/Head.astro", "DefaultHead");
217
+ next = upsertImport(next, "astro:assets", "Font");
218
+ const fontBlock = buildAstroLayoutFontBlock(fontsToSerialize);
219
+ const replacedBlock = replaceManagedBlock(next, fontBlock, ASTRO_LAYOUT_BLOCK_START, ASTRO_LAYOUT_BLOCK_END);
220
+ if (replacedBlock) next = replacedBlock;
221
+ else if (next.includes("<DefaultHead")) next = next.replace(/<DefaultHead[^>]*\/>/, (match) => `${match}\n${fontBlock}`);
222
+ else next = `${next.trimEnd()}\n\n<DefaultHead />\n${fontBlock}\n`;
223
+ return compactSource$1(next);
224
+ }
225
+ function toManagedAstroFont(fontName) {
226
+ const normalized = fontName.replace(/^font-heading-/, "").replace(/^font-/, "");
227
+ const font = fontName.startsWith("font-heading-") ? getHeadingFontValue(normalized) : getFontValue(normalized);
228
+ if (!font || font.type !== "registry:font" || !ASTRO_FONT_PROVIDERS.includes(font.font.provider)) return null;
229
+ return {
230
+ name: font.title?.replace(/\s+\(Heading\)$/, "") ?? font.font.import.replace(/_/g, " "),
231
+ cssVariable: font.font.variable,
232
+ provider: ASTRO_FONTSOURCE_FONT_NAMES.has(normalized) ? "fontsource" : "google",
233
+ subsets: normalizeManagedAstroFontSubsets(font.font.subsets)
234
+ };
235
+ }
236
+ function mergeManagedAstroFonts(currentFonts, nextFont) {
237
+ return [...currentFonts.filter((font) => font.cssVariable !== nextFont.cssVariable), nextFont].sort((left, right) => left.cssVariable.localeCompare(right.cssVariable));
238
+ }
239
+ function fontClassFromCssVariable(cssVariable) {
240
+ return `font-${cssVariable.replace(/^--font-/, "")}`;
241
+ }
242
+ async function patchFileIfExists(filePath, transformer) {
243
+ if (!await fs.pathExists(filePath)) return;
244
+ const current = await fs.readFile(filePath, "utf8");
245
+ const next = transformer(current);
246
+ if (next !== current) await fs.writeFile(filePath, next, "utf8");
247
+ }
248
+ async function collectPackageJsonsFromComponents(projectPath) {
249
+ const componentJsonFiles = await fg("**/components.json", {
250
+ cwd: projectPath,
251
+ ignore: [
252
+ "**/node_modules/**",
253
+ "**/dist/**",
254
+ "**/.astro/**"
255
+ ]
256
+ });
257
+ const packageJsonFiles = /* @__PURE__ */ new Set();
258
+ for (const relativePath of componentJsonFiles) {
259
+ const absolutePath = path.resolve(projectPath, relativePath);
260
+ const cssPath = (await fs.readJson(absolutePath))?.tailwind?.css;
261
+ if (typeof cssPath !== "string" || cssPath.length === 0) continue;
262
+ let currentDir = path.dirname(path.resolve(path.dirname(absolutePath), cssPath));
263
+ while (true) {
264
+ const packageJsonPath = path.resolve(currentDir, "package.json");
265
+ if (await fs.pathExists(packageJsonPath)) {
266
+ packageJsonFiles.add(packageJsonPath);
267
+ break;
268
+ }
269
+ const parentDir = path.dirname(currentDir);
270
+ if (parentDir === currentDir) break;
271
+ currentDir = parentDir;
272
+ }
273
+ }
274
+ return packageJsonFiles;
275
+ }
276
+ async function upsertStarlightHeadFile(projectPath, fontsToSerialize) {
277
+ const headPath = path.resolve(projectPath, STARLIGHT_HEAD_RELATIVE_PATH);
278
+ const next = patchStarlightHeadSource(await fs.pathExists(headPath) ? await fs.readFile(headPath, "utf8") : "---\nimport DefaultHead from \"@astrojs/starlight/components/Head.astro\";\nimport { Font } from \"astro:assets\";\n---\n\n<DefaultHead />\n", fontsToSerialize);
279
+ await fs.ensureDir(path.dirname(headPath));
280
+ await fs.writeFile(headPath, next, "utf8");
281
+ }
282
+ async function readManagedAstroFontsFromProject(projectPath) {
283
+ for (const relativePath of ASTRO_CONFIG_CANDIDATES) {
284
+ const absolutePath = path.resolve(projectPath, relativePath);
285
+ if (!await fs.pathExists(absolutePath)) continue;
286
+ const parsed = parseManagedAstroFonts(await fs.readFile(absolutePath, "utf8"));
287
+ if (parsed.length > 0) return parsed;
288
+ }
289
+ return [];
290
+ }
291
+ async function cleanupAstroFontPackages(projectPath) {
292
+ const managedPackages = new Set(fonts.filter((font) => font.type === "registry:font").map((font) => getFontPackageName(font.name.replace(/^font-heading-/, "").replace(/^font-/, ""))));
293
+ const packageJsonFiles = await collectPackageJsonsFromComponents(projectPath);
294
+ await Promise.all(Array.from(packageJsonFiles).map(async (absolutePath) => {
295
+ const current = await fs.readJson(absolutePath);
296
+ let changed = false;
297
+ for (const bucket of ["dependencies", "devDependencies"]) {
298
+ const currentBucket = current[bucket];
299
+ if (!currentBucket || typeof currentBucket !== "object") continue;
300
+ for (const packageName of managedPackages) if (packageName in currentBucket) {
301
+ delete currentBucket[packageName];
302
+ changed = true;
303
+ }
304
+ if (Object.keys(currentBucket).length === 0) {
305
+ delete current[bucket];
306
+ changed = true;
307
+ }
308
+ }
309
+ if (changed) await fs.writeJson(absolutePath, current, { spaces: 2 });
310
+ }));
311
+ }
312
+ async function syncAstroFontsInProject(projectPath, fontsToSerialize, activeFontCssVariable) {
313
+ await Promise.all(ASTRO_CONFIG_CANDIDATES.map((relativePath) => patchFileIfExists(path.resolve(projectPath, relativePath), (source) => patchAstroConfigSource(source, fontsToSerialize, { starlightHeadOverride: relativePath === "apps/docs/astro.config.mjs" }))));
314
+ await Promise.all(ASTRO_LAYOUT_CANDIDATES.map((relativePath) => patchFileIfExists(path.resolve(projectPath, relativePath), (source) => patchAstroLayoutSource(source, fontsToSerialize, activeFontCssVariable))));
315
+ const docsConfigPath = path.resolve(projectPath, "apps/docs/astro.config.mjs");
316
+ if (await fs.pathExists(docsConfigPath)) await upsertStarlightHeadFile(projectPath, fontsToSerialize);
317
+ }
318
+
319
+ //#endregion
320
+ //#region src/utils/apply-design-system.ts
321
+ const CREATE_BLOCK_START = "/* bejamas:create:start */";
322
+ const CREATE_BLOCK_END = "/* bejamas:create:end */";
323
+ const SHADCN_TAILWIND_IMPORT = "@import \"shadcn/tailwind.css\";";
324
+ const BEJAMAS_TAILWIND_IMPORT = "@import \"bejamas/tailwind.css\";";
325
+ const MANAGED_TAILWIND_IMPORTS = /* @__PURE__ */ new Set([SHADCN_TAILWIND_IMPORT, BEJAMAS_TAILWIND_IMPORT]);
326
+ const MANAGED_ICON_PACKAGES = new Set(ICON_LIBRARIES.map((library) => library.packageName));
327
+ const FALLBACK_ICON_PACKAGE_VERSION = "latest";
328
+ const TEMPLATE_APP_UI_IMPORT = "import { appUi } from \"@/i18n/ui\";";
329
+ const TEMPLATE_I18N_SOURCES = {
330
+ astro: `const RTL_LANGUAGES = ["ar", "fa", "he"] as const;
331
+
332
+ export type TemplateLanguage = "en" | (typeof RTL_LANGUAGES)[number];
333
+
334
+ export const CURRENT_LANGUAGE: TemplateLanguage = "en";
335
+
336
+ const ui = {
337
+ en: {
338
+ metadataTitle: "bejamas/ui Astro project",
339
+ welcomeMessage: "Welcome to {project} Astro project.",
340
+ getStartedMessage: "Get started by editing src/pages/index.astro.",
341
+ readDocs: "Read docs",
342
+ getCustomDemo: "Get custom demo",
343
+ },
344
+ ar: {
345
+ metadataTitle: "مشروع Astro من bejamas/ui",
346
+ welcomeMessage: "مرحبًا بك في مشروع Astro الخاص بـ {project}.",
347
+ getStartedMessage: "ابدأ بتحرير src/pages/index.astro.",
348
+ readDocs: "اقرأ التوثيق",
349
+ getCustomDemo: "احصل على عرض توضيحي مخصص",
350
+ },
351
+ fa: {
352
+ metadataTitle: "پروژه Astro با bejamas/ui",
353
+ welcomeMessage: "به پروژه Astro با {project} خوش آمدید.",
354
+ getStartedMessage: "برای شروع src/pages/index.astro را ویرایش کنید.",
355
+ readDocs: "مطالعه مستندات",
356
+ getCustomDemo: "درخواست دموی سفارشی",
357
+ },
358
+ he: {
359
+ metadataTitle: "פרויקט Astro עם bejamas/ui",
360
+ welcomeMessage: "ברוכים הבאים לפרויקט Astro של {project}.",
361
+ getStartedMessage: "התחילו בעריכת src/pages/index.astro.",
362
+ readDocs: "קריאת התיעוד",
363
+ getCustomDemo: "קבלת דמו מותאם",
364
+ },
365
+ } as const satisfies Record<TemplateLanguage, Record<string, string>>;
366
+
367
+ export const appUi = {
368
+ lang: CURRENT_LANGUAGE,
369
+ dir: RTL_LANGUAGES.includes(
370
+ CURRENT_LANGUAGE as (typeof RTL_LANGUAGES)[number],
371
+ )
372
+ ? "rtl"
373
+ : "ltr",
374
+ ...ui[CURRENT_LANGUAGE],
375
+ };
376
+ `,
377
+ monorepo: `const RTL_LANGUAGES = ["ar", "fa", "he"] as const;
378
+
379
+ export type TemplateLanguage = "en" | (typeof RTL_LANGUAGES)[number];
380
+
381
+ export const CURRENT_LANGUAGE: TemplateLanguage = "en";
382
+
383
+ const ui = {
384
+ en: {
385
+ metadataTitle: "bejamas/ui Astro project",
386
+ welcomeMessage: "Welcome to {project} Astro monorepo.",
387
+ getStartedMessage: "Get started by editing apps/web/src/pages/index.astro.",
388
+ readDocs: "Read docs",
389
+ getCustomDemo: "Get custom demo",
390
+ },
391
+ ar: {
392
+ metadataTitle: "مشروع Astro من bejamas/ui",
393
+ welcomeMessage: "مرحبًا بك في مشروع Astro الأحادي من {project}.",
394
+ getStartedMessage: "ابدأ بتحرير apps/web/src/pages/index.astro.",
395
+ readDocs: "اقرأ التوثيق",
396
+ getCustomDemo: "احصل على عرض توضيحي مخصص",
397
+ },
398
+ fa: {
399
+ metadataTitle: "پروژه Astro با bejamas/ui",
400
+ welcomeMessage: "به مونوریپوی Astro با {project} خوش آمدید.",
401
+ getStartedMessage:
402
+ "برای شروع apps/web/src/pages/index.astro را ویرایش کنید.",
403
+ readDocs: "مطالعه مستندات",
404
+ getCustomDemo: "درخواست دموی سفارشی",
405
+ },
406
+ he: {
407
+ metadataTitle: "פרויקט Astro עם bejamas/ui",
408
+ welcomeMessage: "ברוכים הבאים למונורפו Astro של {project}.",
409
+ getStartedMessage: "התחילו בעריכת apps/web/src/pages/index.astro.",
410
+ readDocs: "קריאת התיעוד",
411
+ getCustomDemo: "קבלת דמו מותאם",
412
+ },
413
+ } as const satisfies Record<TemplateLanguage, Record<string, string>>;
414
+
415
+ export const appUi = {
416
+ lang: CURRENT_LANGUAGE,
417
+ dir: RTL_LANGUAGES.includes(
418
+ CURRENT_LANGUAGE as (typeof RTL_LANGUAGES)[number],
419
+ )
420
+ ? "rtl"
421
+ : "ltr",
422
+ ...ui[CURRENT_LANGUAGE],
423
+ };
424
+ `
425
+ };
426
+ const TEMPLATE_PAGE_VARIANTS = [
427
+ {
428
+ matchers: [
429
+ "import { Button } from \"@/ui/button\";",
430
+ "Astro project.",
431
+ "Get started by editing src/pages/index.astro."
432
+ ],
433
+ i18nSource: `---
434
+ import Layout from "@/layouts/Layout.astro";
435
+ import { appUi } from "@/i18n/ui";
436
+ import { Button } from "@/ui/button";
437
+
438
+ const [welcomePrefix, welcomeSuffix = ""] =
439
+ appUi.welcomeMessage.split("{project}");
440
+ ---
441
+
442
+ <Layout>
443
+ <div
444
+ class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20"
445
+ >
446
+ <div class="flex flex-col gap-6 row-start-2 items-center sm:items-start">
447
+ <img src="/bejamas.svg" alt="bejamas/ui" class="w-8 dark:invert" />
448
+ <div class="space-y-2">
449
+ <p class="text-sm">
450
+ {welcomePrefix}<code
451
+ class="font-bold relative bg-muted rounded p-1"
452
+ >@bejamas/ui</code
453
+ >{welcomeSuffix}
454
+ </p>
455
+ <p class="text-sm">
456
+ {appUi.getStartedMessage}
457
+ </p>
458
+ </div>
459
+ <div class="flex gap-2">
460
+ <Button as="a" href="https://ui.bejamas.com/docs"
461
+ >{appUi.readDocs}</Button
462
+ >
463
+ <Button as="a" href="https://bejamas.com/get-in-touch" variant="outline"
464
+ >{appUi.getCustomDemo}</Button
465
+ >
466
+ </div>
467
+ </div>
468
+ </div>
469
+ </Layout>
470
+ `
471
+ },
472
+ {
473
+ matchers: [
474
+ "import { Button } from \"@repo/ui/components/button\";",
475
+ "width=\"32\"",
476
+ "Astro monorepo.",
477
+ "Get started by editing apps/web/src/pages/index.astro."
478
+ ],
479
+ i18nSource: `---
480
+ import Layout from "@/layouts/Layout.astro";
481
+ import { appUi } from "@/i18n/ui";
482
+ import { Button } from "@repo/ui/components/button";
483
+
484
+ const [welcomePrefix, welcomeSuffix = ""] =
485
+ appUi.welcomeMessage.split("{project}");
486
+ ---
487
+
488
+ <Layout>
489
+ <div
490
+ class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20"
491
+ >
492
+ <div class="flex flex-col gap-6 row-start-2 items-center sm:items-start">
493
+ <img
494
+ src="/bejamas.svg"
495
+ alt="bejamas/ui"
496
+ class="w-8 dark:invert"
497
+ width="32"
498
+ height="32"
499
+ alt="bejamas/ui logo"
500
+ />
501
+ <div class="space-y-2">
502
+ <p class="text-sm">
503
+ {welcomePrefix}<code
504
+ class="font-bold relative bg-muted rounded p-1"
505
+ >@bejamas/ui</code
506
+ >{welcomeSuffix}
507
+ </p>
508
+ <p class="text-sm">
509
+ {appUi.getStartedMessage}
510
+ </p>
511
+ </div>
512
+ <div class="flex gap-2">
513
+ <Button as="a" href="https://ui.bejamas.com/docs"
514
+ >{appUi.readDocs}</Button
515
+ >
516
+ <Button as="a" href="https://bejamas.com/get-in-touch" variant="outline"
517
+ >{appUi.getCustomDemo}</Button
518
+ >
519
+ </div>
520
+ </div>
521
+ </div>
522
+ </Layout>
523
+ `
524
+ },
525
+ {
526
+ matchers: [
527
+ "import { Button } from \"@repo/ui/components/button\";",
528
+ "<img src=\"/bejamas.svg\" alt=\"bejamas/ui\" class=\"w-8 dark:invert\" />",
529
+ "Astro monorepo.",
530
+ "Get started by editing apps/web/src/pages/index.astro."
531
+ ],
532
+ i18nSource: `---
533
+ import Layout from "@/layouts/Layout.astro";
534
+ import { appUi } from "@/i18n/ui";
535
+ import { Button } from "@repo/ui/components/button";
536
+
537
+ const [welcomePrefix, welcomeSuffix = ""] =
538
+ appUi.welcomeMessage.split("{project}");
539
+ ---
540
+
541
+ <Layout>
542
+ <div
543
+ class="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20"
544
+ >
545
+ <div class="flex flex-col gap-6 row-start-2 items-center sm:items-start">
546
+ <img src="/bejamas.svg" alt="bejamas/ui" class="w-8 dark:invert" />
547
+ <div class="space-y-2">
548
+ <p class="text-sm">
549
+ {welcomePrefix}<code
550
+ class="font-bold relative bg-muted rounded p-1"
551
+ >@bejamas/ui</code
552
+ >{welcomeSuffix}
553
+ </p>
554
+ <p class="text-sm">
555
+ {appUi.getStartedMessage}
556
+ </p>
557
+ </div>
558
+ <div class="flex gap-2">
559
+ <Button as="a" href="https://ui.bejamas.com/docs"
560
+ >{appUi.readDocs}</Button
561
+ >
562
+ <Button as="a" href="https://bejamas.com/get-in-touch" variant="outline"
563
+ >{appUi.getCustomDemo}</Button
564
+ >
565
+ </div>
566
+ </div>
567
+ </div>
568
+ </Layout>
569
+ `
570
+ }
571
+ ];
572
+ function escapeRegExp$1(value) {
573
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
574
+ }
575
+ function compactCss(source) {
576
+ return source.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
577
+ }
578
+ function compactSource(source) {
579
+ return source.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
580
+ }
581
+ function stripLegacyCreateBlock(source) {
582
+ const fullBlockPattern = new RegExp(`\\n*${escapeRegExp$1(CREATE_BLOCK_START)}[\\s\\S]*?${escapeRegExp$1(CREATE_BLOCK_END)}\\n*`, "m");
583
+ const danglingBlockPattern = new RegExp(`\\n*${escapeRegExp$1(CREATE_BLOCK_START)}[\\s\\S]*$`, "m");
584
+ return compactCss(source.replace(fullBlockPattern, "\n\n").replace(danglingBlockPattern, "\n\n"));
585
+ }
586
+ function buildCssVarBlock(selector, vars) {
587
+ return [
588
+ `${selector} {`,
589
+ ...Object.entries(vars).map(([key, value]) => ` --${key}: ${value};`),
590
+ "}"
591
+ ].join("\n");
592
+ }
593
+ function replaceTopLevelBlock(source, selector, nextBlock) {
594
+ const pattern = new RegExp(`^${escapeRegExp$1(selector)}\\s*\\{[\\s\\S]*?^\\}`, "m");
595
+ if (pattern.test(source)) return source.replace(pattern, nextBlock);
596
+ const layerIndex = source.indexOf("@layer base");
597
+ if (layerIndex !== -1) return `${source.slice(0, layerIndex).trimEnd()}\n\n${nextBlock}\n\n${source.slice(layerIndex).trimStart()}`;
598
+ return `${source.trimEnd()}\n\n${nextBlock}\n`;
599
+ }
600
+ function upsertImports(source, imports) {
601
+ const cleanedLines = source.split("\n").filter((line) => {
602
+ const trimmed = line.trim();
603
+ if (MANAGED_TAILWIND_IMPORTS.has(trimmed)) return false;
604
+ return !trimmed.startsWith("@import \"@fontsource-variable/");
605
+ });
606
+ let insertAt = -1;
607
+ for (let index = 0; index < cleanedLines.length; index += 1) {
608
+ if (cleanedLines[index].trim().startsWith("@import ")) {
609
+ insertAt = index;
610
+ continue;
611
+ }
612
+ if (insertAt !== -1 && cleanedLines[index].trim() !== "") break;
613
+ }
614
+ const uniqueImports = imports.filter((line, index) => imports.indexOf(line) === index);
615
+ if (insertAt === -1) return compactCss([
616
+ ...uniqueImports,
617
+ "",
618
+ ...cleanedLines
619
+ ].join("\n"));
620
+ cleanedLines.splice(insertAt + 1, 0, ...uniqueImports);
621
+ return compactCss(cleanedLines.join("\n"));
622
+ }
623
+ function upsertManagedTailwindImport(source) {
624
+ const nextImport = resolveManagedTailwindImport(source);
625
+ const cleanedLines = source.split("\n").filter((line) => !MANAGED_TAILWIND_IMPORTS.has(line.trim()));
626
+ let insertAt = -1;
627
+ for (let index = 0; index < cleanedLines.length; index += 1) {
628
+ if (cleanedLines[index].trim().startsWith("@import ")) {
629
+ insertAt = index;
630
+ continue;
631
+ }
632
+ if (insertAt !== -1 && cleanedLines[index].trim() !== "") break;
633
+ }
634
+ if (insertAt === -1) return compactCss([
635
+ nextImport,
636
+ "",
637
+ ...cleanedLines
638
+ ].join("\n"));
639
+ cleanedLines.splice(insertAt + 1, 0, nextImport);
640
+ return compactCss(cleanedLines.join("\n"));
641
+ }
642
+ function resolveManagedTailwindImport(source) {
643
+ if (source.includes(SHADCN_TAILWIND_IMPORT)) return SHADCN_TAILWIND_IMPORT;
644
+ if (source.includes(BEJAMAS_TAILWIND_IMPORT)) return BEJAMAS_TAILWIND_IMPORT;
645
+ return BEJAMAS_TAILWIND_IMPORT;
646
+ }
647
+ function normalizeSourceForComparison(source) {
648
+ return source.replace(/\r\n/g, "\n").replace(/\s+/g, " ").trim();
649
+ }
650
+ function upsertFrontmatterImport(source, importLine) {
651
+ if (source.includes(importLine)) return source;
652
+ const frontmatterEnd = source.indexOf("\n---", 3);
653
+ if (source.startsWith("---\n") && frontmatterEnd !== -1) return `${source.slice(0, frontmatterEnd).trimEnd()}\n${importLine}${source.slice(frontmatterEnd)}`;
654
+ return `${importLine}\n${source}`;
655
+ }
656
+ function getTemplateI18nVariant(filepath) {
657
+ return filepath.includes(`${path.sep}apps${path.sep}web${path.sep}`) ? "monorepo" : "astro";
658
+ }
659
+ function buildTemplateI18nSource(variant, language) {
660
+ return compactSource(TEMPLATE_I18N_SOURCES[variant].replace("export const CURRENT_LANGUAGE: TemplateLanguage = \"en\";", `export const CURRENT_LANGUAGE: TemplateLanguage = "${language}";`));
661
+ }
662
+ function transformTemplateLayoutToI18n(source) {
663
+ if (source.includes(TEMPLATE_APP_UI_IMPORT)) return source;
664
+ let next = upsertFrontmatterImport(source, TEMPLATE_APP_UI_IMPORT);
665
+ next = next.replace(/<html([^>]*?)\slang=(\"[^\"]*\"|\{[^}]+\})([^>]*)>/, "<html$1$3>").replace(/<html([^>]*?)\sdir=(\"[^\"]*\"|\{[^}]+\})([^>]*)>/, "<html$1$3>");
666
+ next = next.replace(/<html([^>]*)>/, "<html$1 lang={appUi.lang} dir={appUi.dir}>");
667
+ next = next.replace(/<title>[\s\S]*?<\/title>/, "<title>{appUi.metadataTitle}</title>");
668
+ return compactSource(next);
669
+ }
670
+ function transformStarterPageToI18n(source) {
671
+ if (source.includes(TEMPLATE_APP_UI_IMPORT)) return source;
672
+ const normalized = normalizeSourceForComparison(source);
673
+ const match = TEMPLATE_PAGE_VARIANTS.find((variant) => variant.matchers.every((matcher) => normalized.includes(normalizeSourceForComparison(matcher))));
674
+ if (!match) return source;
675
+ return compactSource(match.i18nSource);
676
+ }
677
+ function upsertThemeInlineFont(source, fontVariable) {
678
+ const pattern = /@theme inline\s*\{[\s\S]*?\n\}/m;
679
+ if (!pattern.test(source)) {
680
+ const block = `@theme inline {\n${[fontVariable ? ` ${fontVariable}: var(${fontVariable});` : null, " --font-heading: var(--font-heading);"].filter(Boolean).join("\n")}\n}`;
681
+ const customVariantIndex = source.indexOf("@custom-variant");
682
+ if (customVariantIndex !== -1) {
683
+ const nextLineIndex = source.indexOf("\n", customVariantIndex);
684
+ return `${source.slice(0, nextLineIndex + 1).trimEnd()}\n\n${block}\n\n${source.slice(nextLineIndex + 1).trimStart()}`;
685
+ }
686
+ return `${block}\n\n${source.trimStart()}`;
687
+ }
688
+ return source.replace(pattern, (block) => {
689
+ 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");
690
+ const declarations = [fontVariable ? ` ${fontVariable}: var(${fontVariable});` : null, " --font-heading: var(--font-heading);"].filter(Boolean).join("\n");
691
+ return withoutManagedFonts.replace("@theme inline {\n", `@theme inline {\n${declarations}\n`);
692
+ });
693
+ }
694
+ function upsertBaseLayerHtmlFont(source, fontClass) {
695
+ const pattern = /@layer base\s*\{[\s\S]*?\n\}/m;
696
+ if (!pattern.test(source)) return `${source.trimEnd()}\n\n@layer base {\n .cn-font-heading {\n @apply font-heading;\n }\n html {\n @apply ${fontClass};\n }\n}\n`;
697
+ return source.replace(pattern, (block) => {
698
+ return block.replace(/\n\s*html\s*\{[\s\S]*?\n\s*\}/m, "").replace(/\n\s*\.cn-font-heading\s*\{[\s\S]*?\n\s*\}/m, "").replace(/\n\}$/, `\n .cn-font-heading {\n @apply font-heading;\n }\n html {\n @apply ${fontClass};\n }\n}`);
699
+ });
700
+ }
701
+ function transformDesignSystemCss(source, config, themeVars) {
702
+ return transformDesignSystemFontCss(transformDesignSystemThemeCss(source, config, themeVars), config);
703
+ }
704
+ function transformDesignSystemThemeCss(source, config, themeVars) {
705
+ const effectiveThemeVars = themeVars ?? buildRegistryTheme(config).cssVars;
706
+ const rootVars = {
707
+ ...Object.fromEntries(Object.entries(effectiveThemeVars.theme ?? {}).filter(([key]) => key !== "bejamas-font-family")),
708
+ ...effectiveThemeVars.light ?? {}
709
+ };
710
+ const darkVars = { ...effectiveThemeVars.dark ?? {} };
711
+ const tailwindImport = resolveManagedTailwindImport(source);
712
+ let next = stripLegacyCreateBlock(source);
713
+ next = upsertImports(next, [tailwindImport]);
714
+ next = replaceTopLevelBlock(next, ":root", buildCssVarBlock(":root", rootVars));
715
+ next = replaceTopLevelBlock(next, ".dark", buildCssVarBlock(".dark", darkVars));
716
+ return compactCss(next);
717
+ }
718
+ function transformDesignSystemFontCss(source, config) {
719
+ const font = getFontValue(config.font);
720
+ const tailwindImport = resolveManagedTailwindImport(source);
721
+ let next = stripLegacyCreateBlock(source);
722
+ next = upsertImports(next, [tailwindImport]);
723
+ next = upsertThemeInlineFont(next, font?.font.variable);
724
+ if (font) next = upsertBaseLayerHtmlFont(next, fontClassFromCssVariable(font.font.variable));
725
+ return compactCss(next);
726
+ }
727
+ function transformAstroManagedFontCss(source, fontVariable) {
728
+ const tailwindImport = resolveManagedTailwindImport(source);
729
+ let next = stripLegacyCreateBlock(source);
730
+ next = upsertImports(next, [tailwindImport]);
731
+ next = upsertThemeInlineFont(next, fontVariable);
732
+ if (fontVariable) next = upsertBaseLayerHtmlFont(next, fontClassFromCssVariable(fontVariable));
733
+ return compactCss(next);
734
+ }
735
+ function transformManagedTailwindImportCss(source) {
736
+ return upsertManagedTailwindImport(source);
737
+ }
738
+ async function patchComponentsJson(filepath, config) {
739
+ const current = await fs.readJson(filepath);
740
+ const { rsc: _rsc, tsx: _tsx, ...normalizedCurrent } = current;
741
+ const next = {
742
+ ...normalizedCurrent,
743
+ $schema: BEJAMAS_COMPONENTS_SCHEMA_URL,
744
+ style: getStyleId(config.style),
745
+ iconLibrary: config.iconLibrary,
746
+ rtl: config.rtl,
747
+ menuColor: config.menuColor,
748
+ menuAccent: config.menuAccent,
749
+ tailwind: {
750
+ ...current.tailwind ?? {},
751
+ baseColor: config.baseColor,
752
+ cssVariables: true
753
+ }
754
+ };
755
+ await fs.writeJson(filepath, next, { spaces: 2 });
756
+ }
757
+ async function patchComponentsJsonWithTheme(filepath, config) {
758
+ await patchComponentsJson(filepath, config);
759
+ }
760
+ async function patchCssFileWithTheme(filepath, config, themeVars) {
761
+ const next = transformDesignSystemCss(await fs.readFile(filepath, "utf8"), config, themeVars);
762
+ await fs.writeFile(filepath, next, "utf8");
763
+ }
764
+ async function patchCssFileWithThemeOnly(filepath, config, themeVars) {
765
+ const next = transformDesignSystemThemeCss(await fs.readFile(filepath, "utf8"), config, themeVars);
766
+ await fs.writeFile(filepath, next, "utf8");
767
+ }
768
+ async function patchCssFileWithAstroFont(filepath, fontVariable) {
769
+ const next = transformAstroManagedFontCss(await fs.readFile(filepath, "utf8"), fontVariable);
770
+ await fs.writeFile(filepath, next, "utf8");
771
+ }
772
+ async function patchCssFileManagedTailwindImport(filepath) {
773
+ const next = transformManagedTailwindImportCss(await fs.readFile(filepath, "utf8"));
774
+ await fs.writeFile(filepath, next, "utf8");
775
+ }
776
+ function getSelectedIconPackageName(config) {
777
+ return ICON_LIBRARIES.find((library) => library.name === config.iconLibrary)?.packageName;
778
+ }
779
+ async function patchPackageJsonIconDependency(filepath, config) {
780
+ const selectedIconPackage = getSelectedIconPackageName(config);
781
+ if (!selectedIconPackage) return;
782
+ const packageJson = await fs.readJson(filepath);
783
+ let targetField = null;
784
+ let changed = false;
785
+ for (const field of ["dependencies", "devDependencies"]) {
786
+ const dependencies = packageJson[field];
787
+ if (!dependencies) continue;
788
+ for (const iconPackage of MANAGED_ICON_PACKAGES) {
789
+ if (!dependencies[iconPackage]) continue;
790
+ targetField ??= field;
791
+ if (iconPackage === selectedIconPackage) continue;
792
+ delete dependencies[iconPackage];
793
+ changed = true;
794
+ }
795
+ }
796
+ if (!targetField) return;
797
+ if (!(packageJson.dependencies?.[selectedIconPackage] || packageJson.devDependencies?.[selectedIconPackage])) {
798
+ packageJson[targetField] ??= {};
799
+ packageJson[targetField][selectedIconPackage] = FALLBACK_ICON_PACKAGE_VERSION;
800
+ changed = true;
801
+ }
802
+ if (changed) await fs.writeJson(filepath, packageJson, { spaces: 2 });
803
+ }
804
+ async function syncPackageIconDependencies(projectPath, config) {
805
+ const packageJsonFiles = await fg("**/package.json", {
806
+ cwd: projectPath,
807
+ absolute: true,
808
+ ignore: [
809
+ "**/node_modules/**",
810
+ "**/dist/**",
811
+ "**/.astro/**"
812
+ ]
813
+ });
814
+ await Promise.all(packageJsonFiles.map((filepath) => patchPackageJsonIconDependency(filepath, config)));
815
+ }
816
+ async function syncManagedTailwindCss(projectPath) {
817
+ const componentJsonFiles = await fg("**/components.json", {
818
+ cwd: projectPath,
819
+ absolute: true,
820
+ ignore: ["**/node_modules/**"]
821
+ });
822
+ await Promise.all(componentJsonFiles.map(async (componentJsonPath) => {
823
+ const cssRelativePath = (await fs.readJson(componentJsonPath))?.tailwind?.css;
824
+ if (typeof cssRelativePath !== "string" || cssRelativePath.length === 0) return;
825
+ const cssPath = path.resolve(path.dirname(componentJsonPath), cssRelativePath);
826
+ if (!await fs.pathExists(cssPath)) return;
827
+ await patchCssFileManagedTailwindImport(cssPath);
828
+ }));
829
+ }
830
+ async function syncAstroManagedFontCss(projectPath, fontVariable) {
831
+ const componentJsonFiles = await fg("**/components.json", {
832
+ cwd: projectPath,
833
+ ignore: [
834
+ "**/node_modules/**",
835
+ "**/dist/**",
836
+ "**/.astro/**"
837
+ ]
838
+ });
839
+ const cssFiles = /* @__PURE__ */ new Set();
840
+ for (const relativePath of componentJsonFiles) {
841
+ const absolutePath = path.resolve(projectPath, relativePath);
842
+ const cssPath = (await fs.readJson(absolutePath))?.tailwind?.css;
843
+ if (typeof cssPath === "string" && cssPath.length > 0) cssFiles.add(path.resolve(path.dirname(absolutePath), cssPath));
844
+ }
845
+ await Promise.all(Array.from(cssFiles).map((filepath) => patchCssFileWithAstroFont(filepath, fontVariable)));
846
+ }
847
+ async function patchTemplateI18nFile(filepath, config) {
848
+ if (!config.rtl) return;
849
+ const nextLanguage = getDocumentLanguage(config);
850
+ const nextSource = buildTemplateI18nSource(getTemplateI18nVariant(filepath), nextLanguage);
851
+ if (!await fs.pathExists(filepath)) {
852
+ await fs.ensureDir(path.dirname(filepath));
853
+ await fs.writeFile(filepath, nextSource, "utf8");
854
+ return;
855
+ }
856
+ const current = await fs.readFile(filepath, "utf8");
857
+ const next = current.includes("export const CURRENT_LANGUAGE") ? current.replace(/export const CURRENT_LANGUAGE: TemplateLanguage = "[^"]+";/, `export const CURRENT_LANGUAGE: TemplateLanguage = "${nextLanguage}";`) : nextSource;
858
+ if (next !== current) await fs.writeFile(filepath, next, "utf8");
859
+ }
860
+ async function patchLayoutFile(filepath, config) {
861
+ if (!await fs.pathExists(filepath)) return;
862
+ const current = await fs.readFile(filepath, "utf8");
863
+ if (current.includes("from \"@/i18n/ui\"")) return;
864
+ if (config.rtl) {
865
+ const next = transformTemplateLayoutToI18n(current);
866
+ if (next !== current) await fs.writeFile(filepath, next, "utf8");
867
+ return;
868
+ }
869
+ const nextLanguage = getDocumentLanguage(config);
870
+ const nextDirection = getDocumentDirection(config);
871
+ let next = current.replace(/<html([^>]*?)lang="[^"]*"([^>]*)>/, "<html$1$2>").replace(/<html([^>]*?)dir="[^"]*"([^>]*)>/, "<html$1$2>");
872
+ next = next.replace(/<html([^>]*)>/, `<html$1 lang="${nextLanguage}"${config.rtl ? ` dir="${nextDirection}"` : ""}>`);
873
+ if (next !== current) await fs.writeFile(filepath, next, "utf8");
874
+ }
875
+ async function patchStarterPageFile(filepath, config) {
876
+ if (!config.rtl || !await fs.pathExists(filepath)) return;
877
+ const current = await fs.readFile(filepath, "utf8");
878
+ const next = transformStarterPageToI18n(current);
879
+ if (next !== current) await fs.writeFile(filepath, next, "utf8");
880
+ }
881
+ async function applyDesignSystemToProject(projectPath, config, options = {}) {
882
+ const componentJsonFiles = await fg("**/components.json", {
883
+ cwd: projectPath,
884
+ ignore: [
885
+ "**/node_modules/**",
886
+ "**/dist/**",
887
+ "**/.astro/**"
888
+ ]
889
+ });
890
+ const cssFiles = /* @__PURE__ */ new Set();
891
+ for (const relativePath of componentJsonFiles) {
892
+ const absolutePath = path.resolve(projectPath, relativePath);
893
+ await patchComponentsJson(absolutePath, config);
894
+ const cssPath = (await fs.readJson(absolutePath))?.tailwind?.css;
895
+ if (typeof cssPath === "string" && cssPath.length > 0) {
896
+ const absoluteCssPath = path.resolve(path.dirname(absolutePath), cssPath);
897
+ cssFiles.add(absoluteCssPath);
898
+ }
899
+ }
900
+ await Promise.all(Array.from(cssFiles).map((filepath) => patchCssFileWithTheme(filepath, config, options.themeVars)));
901
+ await syncPackageIconDependencies(projectPath, config);
902
+ await Promise.all([path.resolve(projectPath, "src/i18n/ui.ts"), path.resolve(projectPath, "apps/web/src/i18n/ui.ts")].map((filepath) => patchTemplateI18nFile(filepath, config)));
903
+ await Promise.all([path.resolve(projectPath, "src/layouts/Layout.astro"), path.resolve(projectPath, "apps/web/src/layouts/Layout.astro")].map((filepath) => patchLayoutFile(filepath, config)));
904
+ await Promise.all([path.resolve(projectPath, "src/pages/index.astro"), path.resolve(projectPath, "apps/web/src/pages/index.astro")].map((filepath) => patchStarterPageFile(filepath, config)));
905
+ await syncDesignSystemFontsInProject(projectPath, config);
906
+ }
907
+ async function applyDesignSystemThemeToProject(projectPath, config, options = {}) {
908
+ const componentJsonFiles = await fg("**/components.json", {
909
+ cwd: projectPath,
910
+ ignore: [
911
+ "**/node_modules/**",
912
+ "**/dist/**",
913
+ "**/.astro/**"
914
+ ]
915
+ });
916
+ const cssFiles = /* @__PURE__ */ new Set();
917
+ for (const relativePath of componentJsonFiles) {
918
+ const absolutePath = path.resolve(projectPath, relativePath);
919
+ await patchComponentsJsonWithTheme(absolutePath, config);
920
+ const cssPath = (await fs.readJson(absolutePath))?.tailwind?.css;
921
+ if (typeof cssPath === "string" && cssPath.length > 0) cssFiles.add(path.resolve(path.dirname(absolutePath), cssPath));
922
+ }
923
+ await Promise.all(Array.from(cssFiles).map((filepath) => patchCssFileWithThemeOnly(filepath, config, options.themeVars)));
924
+ await syncPackageIconDependencies(projectPath, config);
925
+ }
926
+ async function applyDesignSystemFontToProject(projectPath, config) {
927
+ await syncDesignSystemFontsInProject(projectPath, config);
928
+ }
929
+ async function syncDesignSystemFontsInProject(projectPath, config) {
930
+ const managedFonts = [toManagedAstroFont(config.font), config.fontHeading !== "inherit" ? toManagedAstroFont(`font-heading-${config.fontHeading}`) : null].filter((font) => font !== null);
931
+ const managedFont = managedFonts.find((font) => font.cssVariable !== "--font-heading");
932
+ if (managedFonts.length > 0 && managedFont) {
933
+ await syncAstroFontsInProject(projectPath, managedFonts, managedFont.cssVariable);
934
+ await syncAstroManagedFontCss(projectPath, managedFont.cssVariable);
935
+ await cleanupAstroFontPackages(projectPath);
936
+ }
937
+ }
938
+
939
+ //#endregion
940
+ //#region src/utils/icon-transform.ts
941
+ const FRONTMATTER_PATTERN = /^---\s*\n([\s\S]*?)\n---/;
942
+ function rewriteAstroIcons(content, iconLibrary) {
943
+ if (!(iconLibrary in ICON_LIBRARY_COLLECTIONS)) return content;
944
+ const library = iconLibrary;
945
+ const shouldRewriteLucideImports = library !== "lucide";
946
+ const frontmatterMatch = content.match(FRONTMATTER_PATTERN);
947
+ if (!frontmatterMatch) return content;
948
+ let frontmatter = frontmatterMatch[1];
949
+ let body = content.slice(frontmatterMatch[0].length);
950
+ const iconUsages = [];
951
+ const semanticIconImports = [];
952
+ if (shouldRewriteLucideImports) {
953
+ frontmatter = frontmatter.replace(/^\s*import\s+([A-Za-z0-9_$]+)\s+from\s+["']@lucide\/astro\/icons\/([^"']+)["'];?\s*$/gm, (full, localName, iconPath) => {
954
+ const semanticName = getSemanticIconNameFromLucidePath(iconPath);
955
+ if (!semanticName) return full;
956
+ iconUsages.push({
957
+ localName,
958
+ semanticName
959
+ });
960
+ return "";
961
+ });
962
+ frontmatter = frontmatter.replace(/import\s+\{([^}]*)\}\s+from\s+["']@lucide\/astro["'];?/g, (full, importsBlock) => {
963
+ const keptImports = [];
964
+ for (const rawImport of importsBlock.split(",")) {
965
+ const trimmedImport = rawImport.trim();
966
+ if (!trimmedImport) continue;
967
+ const aliasMatch = trimmedImport.match(/^([A-Za-z0-9_$]+)(?:\s+as\s+([A-Za-z0-9_$]+))?$/);
968
+ if (!aliasMatch) {
969
+ keptImports.push(trimmedImport);
970
+ continue;
971
+ }
972
+ const [, importedName, localAlias] = aliasMatch;
973
+ const semanticName = getSemanticIconNameFromLucideExport(importedName);
974
+ if (!semanticName) {
975
+ keptImports.push(trimmedImport);
976
+ continue;
977
+ }
978
+ iconUsages.push({
979
+ localName: localAlias ?? importedName,
980
+ semanticName
981
+ });
982
+ }
983
+ if (!keptImports.length) return "";
984
+ return `import { ${keptImports.join(", ")} } from "@lucide/astro";`;
985
+ });
986
+ }
987
+ frontmatter = frontmatter.replace(/^\s*import\s+([A-Za-z0-9_$]+)\s+from\s+["'][^"']*SemanticIcon\.astro["'];?\s*$/gm, (_full, localName) => {
988
+ semanticIconImports.push(localName);
989
+ return "";
990
+ });
991
+ if (!iconUsages.length && !semanticIconImports.length) return content;
992
+ for (const { localName, semanticName } of iconUsages.sort((left, right) => right.localName.length - left.localName.length)) {
993
+ const selfClosingPattern = new RegExp(`<${localName}(\\s[^>]*?)?\\s*/>`, "g");
994
+ const pairedPattern = new RegExp(`<${localName}(\\s[^>]*?)?>([\\s\\S]*?)<\\/${localName}>`, "g");
995
+ body = body.replace(selfClosingPattern, (_full, attributeString = "") => renderSemanticIconSvgWithAttributeString(library, semanticName, attributeString));
996
+ body = body.replace(pairedPattern, (_full, attributeString = "") => renderSemanticIconSvgWithAttributeString(library, semanticName, attributeString));
997
+ }
998
+ for (const localName of semanticIconImports.sort((left, right) => right.length - left.length)) {
999
+ const selfClosingPattern = new RegExp(`<${localName}(\\s[^>]*?)?\\s*/>`, "g");
1000
+ const pairedPattern = new RegExp(`<${localName}(\\s[^>]*?)?>([\\s\\S]*?)<\\/${localName}>`, "g");
1001
+ body = body.replace(selfClosingPattern, (full, attributeString = "") => renderSemanticIconUsage(full, library, attributeString));
1002
+ body = body.replace(pairedPattern, (full, attributeString = "") => renderSemanticIconUsage(full, library, attributeString));
1003
+ }
1004
+ return `---\n${frontmatter.split("\n").filter((line, index, lines) => {
1005
+ if (line.trim().length > 0) return true;
1006
+ return lines[index - 1]?.trim().length > 0;
1007
+ }).join("\n").trim()}\n---${body}`;
1008
+ }
1009
+ function renderSemanticIconUsage(original, library, attributeString = "") {
1010
+ const semanticName = attributeString.match(/\sname=(["'])([^"']+)\1/)?.[2];
1011
+ if (!semanticName || !SEMANTIC_ICON_NAMES.includes(semanticName)) return original;
1012
+ const normalizedAttributeString = attributeString.replace(/\sname=(["'])[^"']+\1/g, "").replace(/\slibrary=(["'])[^"']+\1/g, "").replace(/\sdata-icon-library=(["'])[^"']+\1/g, "").trim();
1013
+ return renderSemanticIconSvgWithAttributeString(library, semanticName, normalizedAttributeString);
1014
+ }
1015
+
1016
+ //#endregion
1017
+ //#region src/utils/menu-transform.ts
1018
+ const TRANSLUCENT_CLASSES = [
1019
+ "animate-none!",
1020
+ "relative",
1021
+ "bg-popover/70",
1022
+ "before:pointer-events-none",
1023
+ "before:absolute",
1024
+ "before:inset-0",
1025
+ "before:-z-1",
1026
+ "before:rounded-[inherit]",
1027
+ "before:backdrop-blur-2xl",
1028
+ "before:backdrop-saturate-150",
1029
+ "**:data-[slot$=-item]:focus:bg-foreground/10",
1030
+ "**:data-[slot$=-item]:data-highlighted:bg-foreground/10",
1031
+ "**:data-[slot$=-separator]:bg-foreground/5",
1032
+ "**:data-[slot$=-trigger]:focus:bg-foreground/10",
1033
+ "**:data-[slot$=-trigger]:aria-expanded:bg-foreground/10!",
1034
+ "**:data-[variant=destructive]:focus:bg-foreground/10!",
1035
+ "**:data-[variant=destructive]:text-accent-foreground!",
1036
+ "**:data-[variant=destructive]:**:text-accent-foreground!"
1037
+ ];
1038
+ function transformMenuTokens(tokens, menuColor) {
1039
+ const transformed = [];
1040
+ for (const token of tokens) {
1041
+ if (token === "cn-menu-target") {
1042
+ if (menuColor === "inverted") transformed.push("dark");
1043
+ else if (menuColor === "default-translucent") transformed.push(...TRANSLUCENT_CLASSES);
1044
+ else if (menuColor === "inverted-translucent") transformed.push("dark", ...TRANSLUCENT_CLASSES);
1045
+ continue;
1046
+ }
1047
+ if (token === "cn-menu-translucent") {
1048
+ if (menuColor === "default-translucent" || menuColor === "inverted-translucent") transformed.push(...TRANSLUCENT_CLASSES);
1049
+ continue;
1050
+ }
1051
+ transformed.push(token);
1052
+ }
1053
+ return Array.from(new Set(transformed));
1054
+ }
1055
+ function rewriteAstroMenus(content, menuColor) {
1056
+ if (!content.includes("cn-menu-target") && !content.includes("cn-menu-translucent")) return content;
1057
+ return content.replace(/(["'])([^"']*\bcn-menu-(?:target|translucent)\b[^"']*)\1/g, (_match, quote, classes) => {
1058
+ return `${quote}${transformMenuTokens(String(classes).split(/\s+/).filter(Boolean), menuColor).join(" ")}${quote}`;
1059
+ });
1060
+ }
1061
+
1062
+ //#endregion
1063
+ //#region src/utils/astro-imports.ts
1064
+ function updateImportAliases(moduleSpecifier, config, isRemote = false) {
1065
+ const scope = config.aliases.components.split("/")[0];
1066
+ for (const [segment, alias] of [
1067
+ ["lib/utils", config.aliases.utils],
1068
+ ["lib", config.aliases.lib],
1069
+ ["hooks", config.aliases.hooks]
1070
+ ]) {
1071
+ if (!alias) continue;
1072
+ for (const prefix of [`@/${segment}`, `${scope}/${segment}`]) if (moduleSpecifier === prefix || moduleSpecifier.startsWith(`${prefix}/`)) return alias + moduleSpecifier.slice(prefix.length);
1073
+ }
1074
+ if (!moduleSpecifier.startsWith("@/") && !isRemote) return moduleSpecifier;
1075
+ let specifier = moduleSpecifier;
1076
+ if (isRemote && specifier.startsWith("@/")) specifier = specifier.replace(/^@\//, `@/registry/${config.style || "bejamas-juno"}/`);
1077
+ if (!specifier.startsWith("@/registry/")) {
1078
+ const alias = config.aliases.components.split("/")[0];
1079
+ return specifier.replace(/^@\//, `${alias}/`);
1080
+ }
1081
+ if (specifier.match(/^@\/registry\/(.+)\/ui/)) return specifier.replace(/^@\/registry\/(.+)\/ui/, config.aliases.ui ?? `${config.aliases.components}/ui`);
1082
+ if (config.aliases.components && specifier.match(/^@\/registry\/(.+)\/components/)) return specifier.replace(/^@\/registry\/(.+)\/components/, config.aliases.components);
1083
+ if (config.aliases.lib && specifier.match(/^@\/registry\/(.+)\/lib/)) return specifier.replace(/^@\/registry\/(.+)\/lib/, config.aliases.lib);
1084
+ if (config.aliases.hooks && specifier.match(/^@\/registry\/(.+)\/hooks/)) return specifier.replace(/^@\/registry\/(.+)\/hooks/, config.aliases.hooks);
1085
+ return specifier.replace(/^@\/registry\/[^/]+/, config.aliases.components);
1086
+ }
1087
+ function rewriteAstroImports(content, config) {
1088
+ let updated = content;
1089
+ const utilsAlias = config.aliases?.utils;
1090
+ const utilsImport = `${typeof utilsAlias === "string" && utilsAlias.includes("/") ? utilsAlias.split("/")[0] : "@"}/lib/utils`;
1091
+ updated = updated.replace(/import\s+([\s\S]*?)\s+from\s+["']([^"']+)["']/g, (full, importsPart, specifier) => {
1092
+ const next = updateImportAliases(specifier, config, false);
1093
+ let finalSpec = next;
1094
+ if (typeof importsPart === "string" && importsPart.split(/[{},\s]/).some((part) => part === "cn") && config.aliases.utils && (next === utilsImport || next === "@/lib/utils")) finalSpec = utilsImport === next ? next.replace(utilsImport, config.aliases.utils) : config.aliases.utils;
1095
+ if (finalSpec === specifier) return full;
1096
+ return full.replace(specifier, finalSpec);
1097
+ });
1098
+ updated = updated.replace(/(?:export\s+(?:type\s+)?(?:\{[^}]*\}|\*)\s+from\s*|import\s*\(\s*)["']([^"']+)["']/g, (full, specifier) => full.replace(specifier, updateImportAliases(specifier, config)));
1099
+ updated = updated.replace(/import\s+["']([^"']+)["']/g, (full, specifier) => {
1100
+ const next = updateImportAliases(specifier, config, false);
1101
+ if (next === specifier) return full;
1102
+ return full.replace(specifier, next);
1103
+ });
1104
+ updated = rewriteAstroMenus(updated, config.menuColor);
1105
+ return rewriteAstroIcons(updated, config.iconLibrary);
1106
+ }
1107
+ async function fixAstroImports(cwd, isVerbose, targetConfig) {
1108
+ const config = targetConfig ?? await getConfig(cwd);
1109
+ if (!config) return;
1110
+ const searchRoots = /* @__PURE__ */ new Set([
1111
+ config.resolvedPaths.components,
1112
+ config.resolvedPaths.ui,
1113
+ config.resolvedPaths.lib,
1114
+ config.resolvedPaths.hooks
1115
+ ]);
1116
+ for (const root of Array.from(searchRoots)) {
1117
+ if (!root) continue;
1118
+ const astroFiles = await fg("**/*.{astro,ts,js}", {
1119
+ cwd: root,
1120
+ absolute: true,
1121
+ dot: false
1122
+ });
1123
+ for (const filePath of astroFiles) {
1124
+ const original = await fs$1.readFile(filePath, "utf8");
1125
+ const rewritten = rewriteAstroImports(original, config);
1126
+ if (rewritten === original) continue;
1127
+ await fs$1.writeFile(filePath, rewritten, "utf8");
1128
+ if (isVerbose) logger.info(`[bejamas-ui] fixed imports in ${path.relative(cwd, filePath)}`);
1129
+ }
1130
+ }
1131
+ }
1132
+
1133
+ //#endregion
1134
+ //#region src/utils/get-package-manager.ts
1135
+ async function getPackageManager(targetDir, { withFallback } = { withFallback: false }) {
1136
+ const packageManager = await detect({
1137
+ programmatic: true,
1138
+ cwd: targetDir
1139
+ });
1140
+ if (packageManager === "yarn@berry") return "yarn";
1141
+ if (packageManager === "pnpm@6") return "pnpm";
1142
+ if (packageManager === "bun") return "bun";
1143
+ if (packageManager === "deno") return "deno";
1144
+ if (!withFallback) return packageManager ?? "npm";
1145
+ const userAgent = process.env.npm_config_user_agent || "";
1146
+ if (userAgent.startsWith("yarn")) return "yarn";
1147
+ if (userAgent.startsWith("pnpm")) return "pnpm";
1148
+ if (userAgent.startsWith("bun")) return "bun";
1149
+ return "npm";
1150
+ }
1151
+
1152
+ //#endregion
1153
+ //#region src/registry/errors.ts
1154
+ const RegistryErrorCode = {
1155
+ NETWORK_ERROR: "NETWORK_ERROR",
1156
+ NOT_FOUND: "NOT_FOUND",
1157
+ UNAUTHORIZED: "UNAUTHORIZED",
1158
+ FORBIDDEN: "FORBIDDEN",
1159
+ FETCH_ERROR: "FETCH_ERROR",
1160
+ NOT_CONFIGURED: "NOT_CONFIGURED",
1161
+ INVALID_CONFIG: "INVALID_CONFIG",
1162
+ MISSING_ENV_VARS: "MISSING_ENV_VARS",
1163
+ LOCAL_FILE_ERROR: "LOCAL_FILE_ERROR",
1164
+ PARSE_ERROR: "PARSE_ERROR",
1165
+ VALIDATION_ERROR: "VALIDATION_ERROR",
1166
+ UNKNOWN_ERROR: "UNKNOWN_ERROR"
1167
+ };
1168
+ var RegistryError = class extends Error {
1169
+ constructor(message, options = {}) {
1170
+ super(message);
1171
+ this.name = "RegistryError";
1172
+ this.code = options.code || RegistryErrorCode.UNKNOWN_ERROR;
1173
+ this.statusCode = options.statusCode;
1174
+ this.cause = options.cause;
1175
+ this.context = options.context;
1176
+ this.suggestion = options.suggestion;
1177
+ this.timestamp = /* @__PURE__ */ new Date();
1178
+ if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
1179
+ }
1180
+ toJSON() {
1181
+ return {
1182
+ name: this.name,
1183
+ message: this.message,
1184
+ code: this.code,
1185
+ statusCode: this.statusCode,
1186
+ context: this.context,
1187
+ suggestion: this.suggestion,
1188
+ timestamp: this.timestamp,
1189
+ stack: this.stack
1190
+ };
1191
+ }
1192
+ };
1193
+
1194
+ //#endregion
1195
+ //#region src/utils/handle-error.ts
1196
+ function handleError(error) {
1197
+ logger.break();
1198
+ logger.error(`Something went wrong. Please check the error below for more details.`);
1199
+ logger.error(`If the problem persists, please open an issue on GitHub: https://github.com/bejamas/ui/issues`);
1200
+ logger.error("");
1201
+ if (typeof error === "string") {
1202
+ logger.error(error);
1203
+ logger.break();
1204
+ process.exit(1);
1205
+ }
1206
+ if (error instanceof RegistryError) {
1207
+ if (error.message) {
1208
+ logger.error(error.cause ? "Error:" : "Message:");
1209
+ logger.error(error.message);
1210
+ }
1211
+ if (error.cause) {
1212
+ logger.error("\nMessage:");
1213
+ logger.error(error.cause);
1214
+ }
1215
+ if (error.suggestion) {
1216
+ logger.error("\nSuggestion:");
1217
+ logger.error(error.suggestion);
1218
+ }
1219
+ logger.break();
1220
+ process.exit(1);
1221
+ }
1222
+ if (error instanceof z.ZodError) {
1223
+ logger.error("Validation failed:");
1224
+ for (const [key, value] of Object.entries(error.flatten().fieldErrors)) logger.error(`- ${highlighter.info(key)}: ${value}`);
1225
+ logger.break();
1226
+ process.exit(1);
1227
+ }
1228
+ if (error instanceof Error) {
1229
+ logger.error(error.message);
1230
+ logger.break();
1231
+ process.exit(1);
1232
+ }
1233
+ logger.break();
1234
+ process.exit(1);
1235
+ }
1236
+
1237
+ //#endregion
1238
+ //#region src/utils/create-project.ts
1239
+ const TEMPLATES = {
1240
+ astro: "astro",
1241
+ "astro-monorepo": "astro-monorepo",
1242
+ "astro-with-component-docs-monorepo": "astro-with-component-docs-monorepo"
1243
+ };
1244
+ const __filename = fileURLToPath(import.meta.url);
1245
+ const __dirname = path$1.dirname(__filename);
1246
+ const TEMPLATE_DIRNAME = {
1247
+ astro: "astro",
1248
+ "astro-monorepo": "monorepo-astro",
1249
+ "astro-with-component-docs-monorepo": "monorepo-astro-with-docs"
1250
+ };
1251
+ const TEMPLATE_EXCLUDED_BASENAMES = /* @__PURE__ */ new Set(["node_modules", ".astro"]);
1252
+ const DEFAULT_TEMPLATE_REPOSITORY = "bejamas/ui";
1253
+ const DEFAULT_TEMPLATE_REF = "main";
1254
+ function resolveLocalTemplatesDir(env = process.env) {
1255
+ const override = env.BEJAMAS_LOCAL_TEMPLATES_DIR?.trim();
1256
+ if (override) return path$1.resolve(override);
1257
+ for (const relativePath of [
1258
+ "../../../../templates",
1259
+ "../../../templates",
1260
+ "../../templates"
1261
+ ]) {
1262
+ const candidate = path$1.resolve(__dirname, relativePath);
1263
+ if (fs.existsSync(candidate)) return candidate;
1264
+ }
1265
+ return path$1.resolve(__dirname, "../../../../templates");
1266
+ }
1267
+ const LOCAL_TEMPLATES_DIR = resolveLocalTemplatesDir();
1268
+ function resolvePackageMetadataPath() {
1269
+ for (const relativePath of ["../package.json", "../../package.json"]) {
1270
+ const candidate = path$1.resolve(__dirname, relativePath);
1271
+ if (fs.existsSync(candidate)) return candidate;
1272
+ }
1273
+ return path$1.resolve(__dirname, "../package.json");
1274
+ }
1275
+ function readPackageMetadata() {
1276
+ try {
1277
+ return fs.readJsonSync(resolvePackageMetadataPath());
1278
+ } catch {
1279
+ return {};
1280
+ }
1281
+ }
1282
+ const BEJAMAS_PACKAGE_METADATA = readPackageMetadata();
1283
+ function shouldCopyTemplateEntry(source) {
1284
+ return !TEMPLATE_EXCLUDED_BASENAMES.has(path$1.basename(source));
1285
+ }
1286
+ async function copyTemplateIntoProject(templateSource, projectPath) {
1287
+ await fs.copy(templateSource, projectPath, { filter: shouldCopyTemplateEntry });
1288
+ }
1289
+ function resolveRemoteTemplateRefs(metadata = BEJAMAS_PACKAGE_METADATA, env = process.env) {
1290
+ const refs = [
1291
+ env.BEJAMAS_TEMPLATE_REF?.trim(),
1292
+ metadata.gitHead?.trim(),
1293
+ metadata.version && !metadata.version.includes("-") ? `bejamas@${metadata.version}` : void 0,
1294
+ DEFAULT_TEMPLATE_REF
1295
+ ].filter((ref) => Boolean(ref));
1296
+ return [...new Set(refs)];
1297
+ }
1298
+ function buildTemplateArchiveUrl(ref, repository = DEFAULT_TEMPLATE_REPOSITORY) {
1299
+ return `https://api.github.com/repos/${repository}/tarball/${encodeURIComponent(ref)}`;
1300
+ }
1301
+ function getGitHubRequestHeaders(env = process.env) {
1302
+ const headers = { "User-Agent": `bejamas/${BEJAMAS_PACKAGE_METADATA.version ?? "dev"}` };
1303
+ const token = env.BEJAMAS_GITHUB_TOKEN?.trim() || env.GITHUB_TOKEN?.trim();
1304
+ if (token) headers.Authorization = `Bearer ${token}`;
1305
+ return headers;
1306
+ }
1307
+ async function downloadTemplateArchive(ref, targetDir) {
1308
+ const archiveUrl = buildTemplateArchiveUrl(ref);
1309
+ const archivePath = path$1.join(targetDir, "repo.tar.gz");
1310
+ const response = await fetch(archiveUrl, {
1311
+ headers: getGitHubRequestHeaders(),
1312
+ signal: AbortSignal.timeout(3e4)
1313
+ });
1314
+ if (!response.ok) throw new Error(`GitHub returned ${response.status} ${response.statusText} for ${ref}.`);
1315
+ await fs.ensureDir(targetDir);
1316
+ await fs.outputFile(archivePath, Buffer.from(await response.arrayBuffer()));
1317
+ await tar.x({
1318
+ cwd: targetDir,
1319
+ file: archivePath,
1320
+ strict: true
1321
+ });
1322
+ const extractedRoot = (await fs.readdir(targetDir)).filter((entry) => entry !== "repo.tar.gz").find((entry) => fs.statSync(path$1.join(targetDir, entry)).isDirectory());
1323
+ if (!extractedRoot) throw new Error(`Downloaded archive for ${ref} did not extract correctly.`);
1324
+ return path$1.join(targetDir, extractedRoot);
1325
+ }
1326
+ async function copyTemplateFromGitHub(templateDirName, projectPath) {
1327
+ const refs = resolveRemoteTemplateRefs();
1328
+ const failures = [];
1329
+ for (const ref of refs) {
1330
+ const tempDir = path$1.join(os.tmpdir(), `bejamas-template-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`);
1331
+ try {
1332
+ const extractedRoot = await downloadTemplateArchive(ref, tempDir);
1333
+ const templateSource = path$1.join(extractedRoot, "templates", templateDirName);
1334
+ if (!await fs.pathExists(templateSource)) throw new Error(`Template templates/${templateDirName} was not found.`);
1335
+ await copyTemplateIntoProject(templateSource, projectPath);
1336
+ return ref;
1337
+ } catch (error) {
1338
+ failures.push(`${ref}: ${error instanceof Error ? error.message : String(error)}`);
1339
+ } finally {
1340
+ await fs.remove(tempDir);
1341
+ }
1342
+ }
1343
+ throw new Error([
1344
+ `Unable to download templates/${templateDirName} from GitHub.`,
1345
+ `Tried refs: ${refs.join(", ")}.`,
1346
+ failures.join("\n")
1347
+ ].join("\n"));
1348
+ }
1349
+ async function applyLocalPackageOverrides(projectPath) {
1350
+ const bejamasPackageOverride = process.env.BEJAMAS_PACKAGE_OVERRIDE;
1351
+ if (!bejamasPackageOverride) return;
1352
+ const packageJsonPaths = await fg("**/package.json", {
1353
+ cwd: projectPath,
1354
+ absolute: true,
1355
+ ignore: ["**/node_modules/**"]
1356
+ });
1357
+ const normalizedOverride = bejamasPackageOverride.replace(/\\/g, "/");
1358
+ await Promise.all(packageJsonPaths.map(async (packageJsonPath) => {
1359
+ const packageJson = await fs.readJson(packageJsonPath);
1360
+ let changed = false;
1361
+ for (const field of ["dependencies", "devDependencies"]) if (packageJson[field]?.bejamas) {
1362
+ packageJson[field].bejamas = `file:${normalizedOverride}`;
1363
+ changed = true;
1364
+ }
1365
+ if (changed) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1366
+ }));
1367
+ }
1368
+ async function createProject(options) {
1369
+ options = {
1370
+ srcDir: false,
1371
+ ...options
1372
+ };
1373
+ let template = options.template && TEMPLATES[options.template] ? options.template : "astro";
1374
+ let projectName = "my-app";
1375
+ const isRemoteComponent = options.components?.length === 1 && !!options.components[0].match(/\/chat\/b\//);
1376
+ if (!options.force) {
1377
+ const { type, name } = await prompts([{
1378
+ type: options.template || isRemoteComponent ? null : "select",
1379
+ name: "type",
1380
+ message: `The path ${highlighter.info(options.cwd)} does not contain a package.json file.\n Would you like to start a new project?`,
1381
+ choices: [
1382
+ {
1383
+ title: "Astro",
1384
+ value: "astro"
1385
+ },
1386
+ {
1387
+ title: "Astro (Monorepo)",
1388
+ value: "astro-monorepo"
1389
+ },
1390
+ {
1391
+ title: "Astro with Component Docs (Monorepo)",
1392
+ value: "astro-with-component-docs-monorepo"
1393
+ }
1394
+ ],
1395
+ initial: 0
1396
+ }, {
1397
+ type: "text",
1398
+ name: "name",
1399
+ message: "What is your project named?",
1400
+ initial: (_prev, values) => {
1401
+ return (options.template && TEMPLATES[options.template] && options.template || values.type || template)?.endsWith("monorepo") ? "my-monorepo" : "my-app";
1402
+ },
1403
+ format: (value) => value.trim(),
1404
+ validate: (value) => value.length > 128 ? `Name should be less than 128 characters.` : true
1405
+ }]);
1406
+ template = type ?? template;
1407
+ projectName = name;
1408
+ }
1409
+ const packageManager = await getPackageManager(options.cwd, { withFallback: true });
1410
+ const projectPath = `${options.cwd}/${projectName}`;
1411
+ try {
1412
+ await fs.access(options.cwd, fs.constants.W_OK);
1413
+ } catch (error) {
1414
+ logger.break();
1415
+ logger.error(`The path ${highlighter.info(options.cwd)} is not writable.`);
1416
+ logger.error(`It is likely you do not have write permissions for this folder or the path ${highlighter.info(options.cwd)} does not exist.`);
1417
+ logger.break();
1418
+ process.exit(1);
1419
+ }
1420
+ if (fs.existsSync(path$1.resolve(options.cwd, projectName, "package.json"))) {
1421
+ logger.break();
1422
+ logger.error(`A project with the name ${highlighter.info(projectName)} already exists.`);
1423
+ logger.error(`Please choose a different name and try again.`);
1424
+ logger.break();
1425
+ process.exit(1);
1426
+ }
1427
+ await createProjectFromTemplate(projectPath, {
1428
+ templateKey: template,
1429
+ packageManager,
1430
+ cwd: options.cwd
1431
+ });
1432
+ return {
1433
+ projectPath,
1434
+ projectName,
1435
+ template
1436
+ };
1437
+ }
1438
+ async function createProjectFromTemplate(projectPath, options) {
1439
+ const createSpinner = spinner(`Creating a new project from template. This may take a few minutes.`).start();
1440
+ try {
1441
+ dotenv.config({ quiet: true });
1442
+ const templateDirName = TEMPLATE_DIRNAME[options.templateKey];
1443
+ const templateSource = path$1.resolve(LOCAL_TEMPLATES_DIR, templateDirName);
1444
+ if (await fs.pathExists(templateSource)) await copyTemplateIntoProject(templateSource, projectPath);
1445
+ else {
1446
+ createSpinner.text = `Downloading the ${highlighter.info(options.templateKey)} template from GitHub.`;
1447
+ await copyTemplateFromGitHub(templateDirName, projectPath);
1448
+ }
1449
+ await removeEmptyTemplateI18nDirs(projectPath);
1450
+ await applyLocalPackageOverrides(projectPath);
1451
+ await execa(options.packageManager, ["install"], { cwd: projectPath });
1452
+ try {
1453
+ const { stdout } = await execa("git", ["rev-parse", "--is-inside-work-tree"], { cwd: projectPath });
1454
+ if (!(stdout.trim() === "true")) {
1455
+ await execa("git", ["init"], { cwd: projectPath });
1456
+ await execa("git", ["add", "-A"], { cwd: projectPath });
1457
+ await execa("git", [
1458
+ "commit",
1459
+ "-m",
1460
+ "Initial commit"
1461
+ ], { cwd: projectPath });
1462
+ }
1463
+ } catch (_) {}
1464
+ createSpinner?.succeed("Creating a new project from template.");
1465
+ } catch (error) {
1466
+ createSpinner?.fail("Something went wrong creating a new project from template.");
1467
+ handleError(error);
1468
+ }
1469
+ }
1470
+ async function removeEmptyTemplateI18nDirs(projectPath) {
1471
+ const candidates = [path$1.resolve(projectPath, "src/i18n"), path$1.resolve(projectPath, "apps/web/src/i18n")];
1472
+ await Promise.all(candidates.map(async (candidate) => {
1473
+ if (!await fs.pathExists(candidate)) return;
1474
+ if ((await fs.readdir(candidate)).length === 0) await fs.remove(candidate);
1475
+ }));
1476
+ }
1477
+
1478
+ //#endregion
1479
+ //#region src/utils/installed-ui-components.ts
1480
+ function toRegistryName(filename) {
1481
+ return filename.replace(/\.[^.]+$/, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
1482
+ }
1483
+ async function pathExists$1(filepath) {
1484
+ try {
1485
+ await promises.access(filepath);
1486
+ return true;
1487
+ } catch {
1488
+ return false;
1489
+ }
1490
+ }
1491
+ async function detectInstalledUiComponents(uiDir) {
1492
+ if (!await pathExists$1(uiDir)) return [];
1493
+ const entries = await promises.readdir(uiDir, { withFileTypes: true });
1494
+ const components = /* @__PURE__ */ new Set();
1495
+ for (const entry of entries) {
1496
+ if (entry.name.startsWith(".")) continue;
1497
+ const entryPath = path.resolve(uiDir, entry.name);
1498
+ if (entry.isDirectory()) {
1499
+ if ((await promises.readdir(entryPath, { withFileTypes: true })).some((child) => child.isFile() && (/\.(astro|tsx|jsx)$/.test(child.name) || /^(index\.(ts|js|tsx|jsx))$/.test(child.name)))) components.add(entry.name);
1500
+ continue;
1501
+ }
1502
+ if (entry.isFile() && /\.(astro|tsx|jsx)$/.test(entry.name) && !entry.name.startsWith("index.")) components.add(toRegistryName(entry.name));
1503
+ }
1504
+ return Array.from(components).sort();
1505
+ }
1506
+ async function getInstalledUiComponents(cwd) {
1507
+ const config = await getConfig(cwd);
1508
+ if (!config) return [];
1509
+ return detectInstalledUiComponents(config.resolvedPaths.ui);
1510
+ }
1511
+
1512
+ //#endregion
1513
+ //#region src/utils/reorganize-components.ts
1514
+ async function fetchRegistryItem(componentName, registryUrl, style = "bejamas-juno") {
1515
+ const url = `${registryUrl}/styles/${style}/${componentName}.json`;
1516
+ try {
1517
+ const response = await fetch(url);
1518
+ if (!response.ok) {
1519
+ const fallbackUrl = `${registryUrl}/${componentName}.json`;
1520
+ const fallbackResponse = await fetch(fallbackUrl);
1521
+ if (!fallbackResponse.ok) return null;
1522
+ return await fallbackResponse.json();
1523
+ }
1524
+ return await response.json();
1525
+ } catch {
1526
+ return null;
1527
+ }
1528
+ }
1529
+ function getSubfolderFromPaths(files) {
1530
+ if (!files || files.length === 0) return null;
1531
+ const uiFiles = files.filter((file) => file.type === "registry:ui");
1532
+ if (uiFiles.length === 0) return null;
1533
+ const subfolders = /* @__PURE__ */ new Set();
1534
+ for (const file of uiFiles) {
1535
+ const parts = file.path.split("/");
1536
+ const uiIndex = parts.indexOf("ui");
1537
+ if (uiIndex !== -1 && parts.length > uiIndex + 2) subfolders.add(parts[uiIndex + 1]);
1538
+ }
1539
+ if (subfolders.size === 1) return Array.from(subfolders)[0];
1540
+ if (uiFiles.length > 0) {
1541
+ const dirname = path.dirname(uiFiles[0].path);
1542
+ const folderName = path.basename(dirname);
1543
+ if (folderName && folderName !== "ui") return folderName;
1544
+ }
1545
+ return null;
1546
+ }
1547
+ async function pathExists(filePath) {
1548
+ try {
1549
+ await fs$1.access(filePath);
1550
+ return true;
1551
+ } catch {
1552
+ return false;
1553
+ }
1554
+ }
1555
+ function resolveShadcnUiRelativePath(filePath, uiDir) {
1556
+ const normalizedFilePath = filePath.replace(/^\/|\/$/g, "");
1557
+ const lastTargetSegment = path.basename(uiDir.replace(/^\/|\/$/g, ""));
1558
+ if (!lastTargetSegment) return path.basename(normalizedFilePath);
1559
+ const fileSegments = normalizedFilePath.split("/");
1560
+ const commonDirIndex = fileSegments.findIndex((segment) => segment === lastTargetSegment);
1561
+ if (commonDirIndex === -1) return fileSegments[fileSegments.length - 1];
1562
+ return fileSegments.slice(commonDirIndex + 1).join("/");
1563
+ }
1564
+ /**
1565
+ * Current upstream shadcn workspace installs flatten `ui/foo/Bar.astro` when
1566
+ * the resolved ui target ends in a segment like `components` instead of `ui`.
1567
+ * We keep reorganization only for that compatibility case.
1568
+ */
1569
+ function shouldReorganizeRegistryUiFiles(files, uiDir) {
1570
+ if (!uiDir) return false;
1571
+ if (!getSubfolderFromPaths(files)) return false;
1572
+ return (files ?? []).filter((file) => file.type === "registry:ui").some((file) => {
1573
+ return !resolveShadcnUiRelativePath(file.path, uiDir).includes("/");
1574
+ });
1575
+ }
1576
+ /**
1577
+ * Reorganizes one multi-file registry:ui item from flat output into its
1578
+ * expected subfolder. This is the filesystem-level compatibility shim.
1579
+ */
1580
+ async function reorganizeRegistryUiFiles(files, uiDir, verbose, overwriteExisting = false) {
1581
+ const result = {
1582
+ totalMoved: 0,
1583
+ movedFiles: [],
1584
+ skippedFiles: []
1585
+ };
1586
+ if (!uiDir || !files || files.length === 0) return result;
1587
+ const subfolder = getSubfolderFromPaths(files);
1588
+ if (!subfolder) return result;
1589
+ const uiFiles = files.filter((file) => file.type === "registry:ui");
1590
+ const targetDir = path.join(uiDir, subfolder);
1591
+ for (const file of uiFiles) {
1592
+ const filename = path.basename(file.path);
1593
+ if (filename === "index.ts") continue;
1594
+ const flatPath = path.join(uiDir, filename);
1595
+ const targetPath = path.join(targetDir, filename);
1596
+ if (!await pathExists(flatPath)) continue;
1597
+ if (await pathExists(targetPath)) {
1598
+ if (overwriteExisting) {
1599
+ await fs$1.mkdir(targetDir, { recursive: true });
1600
+ await fs$1.unlink(targetPath);
1601
+ await fs$1.rename(flatPath, targetPath);
1602
+ result.totalMoved++;
1603
+ result.movedFiles.push(`${subfolder}/${filename}`);
1604
+ if (verbose) logger.info(`[bejamas-ui] Replaced ${subfolder}/${filename} with the reinstalled version`);
1605
+ continue;
1606
+ }
1607
+ try {
1608
+ await fs$1.unlink(flatPath);
1609
+ result.skippedFiles.push(`${subfolder}/${filename}`);
1610
+ if (verbose) logger.info(`[bejamas-ui] Removed flat duplicate: ${filename} (${subfolder}/${filename} exists)`);
1611
+ } catch {
1612
+ result.skippedFiles.push(`${subfolder}/${filename}`);
1613
+ }
1614
+ continue;
1615
+ }
1616
+ await fs$1.mkdir(targetDir, { recursive: true });
1617
+ await fs$1.rename(flatPath, targetPath);
1618
+ result.totalMoved++;
1619
+ result.movedFiles.push(`${subfolder}/${filename}`);
1620
+ if (verbose) logger.info(`[bejamas-ui] Moved ${filename} -> ${subfolder}/${filename}`);
1621
+ }
1622
+ if (await pathExists(targetDir)) await repairComponentBarrel(targetDir, files, overwriteExisting);
1623
+ return result;
1624
+ }
1625
+ async function repairComponentBarrel(targetDir, files, overwriteExisting = false) {
1626
+ const localFiles = (await fs$1.readdir(targetDir)).filter((name) => name.endsWith(".astro")).sort();
1627
+ if (!localFiles.length) return;
1628
+ const indexPath = path.join(targetDir, "index.ts");
1629
+ const existing = await fs$1.readFile(indexPath, "utf8").catch(() => "");
1630
+ const registryBarrel = files.find((file) => path.basename(file.path) === "index.ts")?.content ?? "";
1631
+ const cleaned = (overwriteExisting || !existing ? registryBarrel : existing).replace(/export\s*\{[^}]*\}\s*from\s*["'](\.\/[^"']+\.astro)["'];?/g, (statement, specifier) => localFiles.includes(specifier.slice(2)) ? statement : "").trim();
1632
+ const next = [cleaned, ...localFiles.filter((file) => {
1633
+ const name = path.basename(file, ".astro");
1634
+ return !new RegExp(`default\\s+as\\s+${name}\\b`).test(cleaned);
1635
+ }).map((file) => `export { default as ${path.basename(file, ".astro")} } from "./${file}";`)].filter(Boolean).join("\n") + "\n";
1636
+ if (next !== existing) await fs$1.writeFile(indexPath, next);
1637
+ }
1638
+ /** Resolve dependencies as well as the requested item before repairing output. */
1639
+ async function fetchRegistryTree(components, registryUrl, style = "bejamas-juno") {
1640
+ const seen = /* @__PURE__ */ new Set();
1641
+ const items = [];
1642
+ async function visit(name) {
1643
+ if (seen.has(name)) return;
1644
+ seen.add(name);
1645
+ if (!/^[a-z0-9-]+$/.test(name)) return;
1646
+ const item = await fetchRegistryItem(name, registryUrl, style);
1647
+ if (!item) throw new Error(`Unable to load registry item ${name} for installation validation.`);
1648
+ for (const dependency of item.registryDependencies ?? []) await visit(dependency);
1649
+ items.push(item);
1650
+ }
1651
+ for (const name of components) await visit(name);
1652
+ return items;
1653
+ }
1654
+ async function reorganizeComponents(components, uiDir, registryUrl, verbose, style = "bejamas-juno", overwriteExisting = false) {
1655
+ const result = {
1656
+ totalMoved: 0,
1657
+ movedFiles: [],
1658
+ skippedFiles: []
1659
+ };
1660
+ if (!uiDir || components.length === 0) return result;
1661
+ const items = await fetchRegistryTree(components, registryUrl, style);
1662
+ for (const item of items) {
1663
+ if (!shouldReorganizeRegistryUiFiles(item.files, uiDir)) continue;
1664
+ const componentResult = await reorganizeRegistryUiFiles(item.files, uiDir, verbose, overwriteExisting);
1665
+ result.totalMoved += componentResult.totalMoved;
1666
+ result.movedFiles.push(...componentResult.movedFiles);
1667
+ result.skippedFiles.push(...componentResult.skippedFiles);
1668
+ }
1669
+ const flatIndex = path.join(uiDir, "index.ts");
1670
+ const flatContent = await fs$1.readFile(flatIndex, "utf8").catch(() => null);
1671
+ 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);
1672
+ return result;
1673
+ }
1674
+ /** Update legacy template exports once the installed UI uses nested barrels. */
1675
+ async function repairUiPackageExports(uiDir, packageRoot) {
1676
+ const entries = await fs$1.readdir(uiDir, { withFileTypes: true }).catch(() => []);
1677
+ if (!entries.some((entry) => entry.isDirectory()) || entries.some((entry) => entry.name.endsWith(".astro"))) return;
1678
+ const packagePath = path.join(packageRoot, "package.json");
1679
+ const manifest = JSON.parse(await fs$1.readFile(packagePath, "utf8"));
1680
+ if (!manifest.exports || typeof manifest.exports !== "object") return;
1681
+ const relativeUi = path.relative(packageRoot, uiDir).split(path.sep).join("/");
1682
+ if (relativeUi.startsWith("../")) return;
1683
+ const prefix = `./${relativeUi}`;
1684
+ let changed = false;
1685
+ for (const [specifier, target] of Object.entries(manifest.exports)) if (target === `${prefix}/*.astro` || target === `${prefix}/*.ts`) {
1686
+ manifest.exports[specifier] = `${prefix}/*/index.ts`;
1687
+ changed = true;
1688
+ }
1689
+ if (changed) await fs$1.writeFile(packagePath, JSON.stringify(manifest, null, 2) + "\n");
1690
+ }
1691
+
1692
+ //#endregion
1693
+ //#region src/utils/shadcn-cli.ts
1694
+ const PINNED_SHADCN_VERSION = "4.21.0";
1695
+ const PINNED_SHADCN_PACKAGE = `shadcn@${PINNED_SHADCN_VERSION}`;
1696
+ const PINNED_SHADCN_EXEC_PREFIX = path.join(os$1.tmpdir(), "bejamas-shadcn", PINNED_SHADCN_VERSION);
1697
+ async function ensurePinnedShadcnExecPrefix() {
1698
+ await fs$1.mkdir(PINNED_SHADCN_EXEC_PREFIX, { recursive: true });
1699
+ return PINNED_SHADCN_EXEC_PREFIX;
1700
+ }
1701
+ function getNpmExecutable() {
1702
+ return process.platform === "win32" ? "npm.cmd" : "npm";
1703
+ }
1704
+ function buildPinnedShadcnInvocation(shadcnArgs) {
1705
+ return {
1706
+ cmd: getNpmExecutable(),
1707
+ args: [
1708
+ "exec",
1709
+ "--yes",
1710
+ "--prefix",
1711
+ PINNED_SHADCN_EXEC_PREFIX,
1712
+ `--package=${PINNED_SHADCN_PACKAGE}`,
1713
+ "--",
1714
+ "shadcn",
1715
+ ...shadcnArgs
1716
+ ],
1717
+ source: "isolated"
1718
+ };
1719
+ }
1720
+
1721
+ //#endregion
1722
+ //#region src/utils/shadcn-command.ts
1723
+ function extractPassthroughArgs(rawArgv, commandName) {
1724
+ const commandIndex = rawArgv.findIndex((arg) => arg === commandName);
1725
+ if (commandIndex === -1) return [];
1726
+ const rest = rawArgv.slice(commandIndex + 1);
1727
+ const doubleDashIndex = rest.indexOf("--");
1728
+ if (doubleDashIndex === -1) return [];
1729
+ return rest.slice(doubleDashIndex + 1);
1730
+ }
1731
+ async function runShadcnCommand({ cwd, args, env }) {
1732
+ await ensurePinnedShadcnExecPrefix();
1733
+ const invocation = buildPinnedShadcnInvocation(args);
1734
+ await execa(invocation.cmd, invocation.args, {
1735
+ cwd,
1736
+ env: {
1737
+ ...process.env,
1738
+ ...env
1739
+ },
1740
+ stdio: "inherit"
1741
+ });
1742
+ }
1743
+
1744
+ //#endregion
1745
+ //#region src/commands/init.ts
1746
+ function resolveDesignSystemConfig(options) {
1747
+ const rtlLanguage = options.rtl && options.lang ? options.lang : DEFAULT_DESIGN_SYSTEM_CONFIG.rtlLanguage;
1748
+ if (options.preset && isPresetCode(options.preset)) {
1749
+ const decoded = decodePreset(options.preset);
1750
+ if (decoded) return normalizeDesignSystemConfig(designSystemConfigSchema.parse({
1751
+ ...DEFAULT_DESIGN_SYSTEM_CONFIG,
1752
+ ...decoded,
1753
+ template: options.template ?? DEFAULT_DESIGN_SYSTEM_CONFIG.template,
1754
+ rtl: options.rtl ?? false,
1755
+ rtlLanguage
1756
+ }));
1757
+ }
1758
+ const baseColor = options.baseColor ?? DEFAULT_DESIGN_SYSTEM_CONFIG.baseColor;
1759
+ return normalizeDesignSystemConfig(designSystemConfigSchema.parse({
1760
+ ...DEFAULT_DESIGN_SYSTEM_CONFIG,
1761
+ template: options.template ?? DEFAULT_DESIGN_SYSTEM_CONFIG.template,
1762
+ rtl: options.rtl ?? false,
1763
+ rtlLanguage,
1764
+ baseColor,
1765
+ theme: baseColor === DEFAULT_DESIGN_SYSTEM_CONFIG.baseColor ? DEFAULT_DESIGN_SYSTEM_CONFIG.theme : baseColor
1766
+ }));
1767
+ }
1768
+ function buildInitUrl(config, themeRef, env = process.env) {
1769
+ const params = new URLSearchParams({
1770
+ preset: encodePreset(config),
1771
+ template: config.template
1772
+ });
1773
+ if (config.rtl) {
1774
+ params.set("rtl", "true");
1775
+ params.set("lang", config.rtlLanguage);
1776
+ }
1777
+ if (themeRef) params.set("themeRef", themeRef);
1778
+ return `${buildUiUrl("/init", env)}?${params.toString()}`;
1779
+ }
1780
+ function buildThemeVarsFromRegistryItem(item) {
1781
+ return item?.cssVars ?? null;
1782
+ }
1783
+ async function fetchInitThemeVars(initUrl) {
1784
+ const response = await fetch(initUrl);
1785
+ if (!response.ok) return null;
1786
+ return buildThemeVarsFromRegistryItem(await response.json());
1787
+ }
1788
+ const initOptionsSchema = z.object({
1789
+ cwd: z.string(),
1790
+ components: z.array(z.string()).optional(),
1791
+ yes: z.boolean(),
1792
+ reinstall: z.boolean().optional(),
1793
+ defaults: z.boolean(),
1794
+ force: z.boolean(),
1795
+ silent: z.boolean(),
1796
+ isNewProject: z.boolean(),
1797
+ srcDir: z.boolean().optional(),
1798
+ cssVariables: z.boolean(),
1799
+ template: z.string().optional().refine((val) => {
1800
+ if (val) return TEMPLATES[val];
1801
+ return true;
1802
+ }, { message: "Invalid template. Please use 'astro', 'astro-monorepo', or 'astro-with-component-docs-monorepo'." }),
1803
+ preset: z.string().optional(),
1804
+ baseColor: z.string().optional().refine((val) => {
1805
+ if (val) return BASE_COLORS.find((color) => color.name === val);
1806
+ return true;
1807
+ }, { message: `Invalid base color. Please use '${BASE_COLORS.map((color) => color.name).join("', '")}'` }),
1808
+ baseStyle: z.boolean(),
1809
+ rtl: z.boolean().default(false),
1810
+ lang: z.enum(RTL_LANGUAGE_VALUES).optional(),
1811
+ themeRef: z.string().optional()
1812
+ });
1813
+ function shouldReinstallExistingComponents(options) {
1814
+ return options.reinstall ?? Boolean(options.preset);
1815
+ }
1816
+ function ensureShadcnReinstallFlag(forwardedOptions, shouldReinstall) {
1817
+ if (!shouldReinstall || forwardedOptions.includes("--reinstall") || forwardedOptions.includes("--no-reinstall")) return forwardedOptions;
1818
+ const passthroughIndex = forwardedOptions.indexOf("--");
1819
+ if (passthroughIndex === -1) return [...forwardedOptions, "--reinstall"];
1820
+ return [
1821
+ ...forwardedOptions.slice(0, passthroughIndex),
1822
+ "--reinstall",
1823
+ ...forwardedOptions.slice(passthroughIndex)
1824
+ ];
1825
+ }
1826
+ function extractOptionsForShadcnInit(rawArgv, cmd) {
1827
+ if (typeof cmd.getOptionValueSource === "function") {
1828
+ const opts = cmd.optsWithGlobals();
1829
+ const forwarded = [];
1830
+ const getSource = (key) => cmd.getOptionValueSource(key);
1831
+ const addBoolean = (key, flag, negateFlag) => {
1832
+ if (getSource(key) !== "cli") return;
1833
+ const value = opts[key];
1834
+ if (typeof value !== "boolean") return;
1835
+ if (value) forwarded.push(flag);
1836
+ else if (negateFlag) forwarded.push(negateFlag);
1837
+ };
1838
+ addBoolean("yes", "--yes");
1839
+ addBoolean("force", "--force");
1840
+ addBoolean("silent", "--silent");
1841
+ addBoolean("reinstall", "--reinstall", "--no-reinstall");
1842
+ const initIndex = rawArgv.findIndex((arg) => arg === "init");
1843
+ if (initIndex !== -1) {
1844
+ const rest = rawArgv.slice(initIndex + 1);
1845
+ const doubleDashIndex = rest.indexOf("--");
1846
+ if (doubleDashIndex !== -1) forwarded.push(...rest.slice(doubleDashIndex));
1847
+ }
1848
+ return forwarded;
1849
+ }
1850
+ const initIndex = rawArgv.findIndex((arg) => arg === "init");
1851
+ if (initIndex === -1) return [];
1852
+ const rest = rawArgv.slice(initIndex + 1);
1853
+ const forwarded = [];
1854
+ const filteredFlags = /* @__PURE__ */ new Set([
1855
+ "-v",
1856
+ "--verbose",
1857
+ "-t",
1858
+ "--template",
1859
+ "-b",
1860
+ "--base-color",
1861
+ "-p",
1862
+ "--preset",
1863
+ "--theme-ref",
1864
+ "-c",
1865
+ "--cwd",
1866
+ "-d",
1867
+ "--defaults",
1868
+ "--src-dir",
1869
+ "--no-src-dir",
1870
+ "--css-variables",
1871
+ "--no-css-variables",
1872
+ "--no-base-style",
1873
+ "--rtl",
1874
+ "--lang"
1875
+ ]);
1876
+ for (let index = 0; index < rest.length; index += 1) {
1877
+ const token = rest[index];
1878
+ if (token === "--") {
1879
+ forwarded.push("--", ...rest.slice(index + 1));
1880
+ break;
1881
+ }
1882
+ if (!token.startsWith("-")) continue;
1883
+ if (filteredFlags.has(token)) continue;
1884
+ forwarded.push(token);
1885
+ }
1886
+ return forwarded;
1887
+ }
1888
+ 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) => {
1889
+ try {
1890
+ await runInit({
1891
+ ...opts,
1892
+ forwardedOptions: extractOptionsForShadcnInit(process.argv.slice(2), cmd)
1893
+ });
1894
+ } catch (error) {
1895
+ logger.break();
1896
+ handleError(error);
1897
+ } finally {
1898
+ clearRegistryContext();
1899
+ }
1900
+ });
1901
+ async function runInit(options) {
1902
+ const designConfig = resolveDesignSystemConfig(options);
1903
+ let newProjectTemplate;
1904
+ if (!options.skipPreflight) {
1905
+ if ((await preFlightInit(options)).errors["1"]) {
1906
+ const { projectPath, template } = await createProject(options);
1907
+ if (!projectPath) process.exit(1);
1908
+ options.cwd = projectPath;
1909
+ options.isNewProject = true;
1910
+ newProjectTemplate = template;
1911
+ }
1912
+ }
1913
+ if (newProjectTemplate) {
1914
+ const projectPath = {
1915
+ "astro-monorepo": "apps/web",
1916
+ "astro-with-component-docs-monorepo": "apps/web",
1917
+ astro: ""
1918
+ };
1919
+ await applyDesignSystemToProject(options.cwd, {
1920
+ ...designConfig,
1921
+ template: newProjectTemplate
1922
+ }, { themeVars: options.themeRef ? await fetchInitThemeVars(buildInitUrl({
1923
+ ...designConfig,
1924
+ template: newProjectTemplate
1925
+ }, options.themeRef)) ?? void 0 : void 0 });
1926
+ options.cwd = path.resolve(options.cwd, projectPath[newProjectTemplate]);
1927
+ logger.log(`${highlighter.success("Success!")} Project initialization completed.\nYou may now add components.`);
1928
+ return await getConfig(options.cwd);
1929
+ }
1930
+ try {
1931
+ return await runExistingProjectInit(options, designConfig);
1932
+ } catch {
1933
+ process.exit(1);
1934
+ }
1935
+ }
1936
+ async function runExistingProjectInit(options, designConfig = resolveDesignSystemConfig(options)) {
1937
+ const env = {
1938
+ ...process.env,
1939
+ REGISTRY_URL: resolveRegistryUrl()
1940
+ };
1941
+ const initUrl = buildInitUrl(designConfig, options.themeRef);
1942
+ const themeVars = options.themeRef ? await fetchInitThemeVars(initUrl) ?? void 0 : void 0;
1943
+ const shouldReinstall = shouldReinstallExistingComponents(options);
1944
+ const reinstallComponents = shouldReinstall ? await getInstalledUiComponents(options.cwd) : [];
1945
+ const forwardedOptions = ensureShadcnReinstallFlag(options.forwardedOptions ?? [], shouldReinstall);
1946
+ await runShadcnCommand({
1947
+ cwd: options.cwd,
1948
+ args: [
1949
+ "init",
1950
+ initUrl,
1951
+ ...reinstallComponents,
1952
+ ...forwardedOptions
1953
+ ],
1954
+ env
1955
+ });
1956
+ await syncManagedTailwindCss(options.cwd);
1957
+ await syncDesignSystemFontsInProject(options.cwd, designConfig);
1958
+ await applyDesignSystemToProject(options.cwd, designConfig, { themeVars });
1959
+ if (reinstallComponents.length > 0) {
1960
+ const config = await getConfig(options.cwd);
1961
+ const uiDir = config?.resolvedPaths.ui ?? "";
1962
+ const activeStyle = config?.style ?? "bejamas-juno";
1963
+ if (uiDir) await reorganizeComponents(reinstallComponents, uiDir, resolveRegistryUrl(), false, activeStyle, true);
1964
+ await fixAstroImports(options.cwd, false);
1965
+ }
1966
+ return await getConfig(options.cwd);
1967
+ }
1968
+
1969
+ //#endregion
1970
+ //#region src/commands/docs.ts
1971
+ const docs = new Command().name("docs").description("proxy to shadcn docs").argument("<components...>", "component names").option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-b, --base <base>", "the base to use either 'base' or 'radix'. defaults to project base.").option("--json", "output as JSON.", false).action(async (components, opts) => {
1972
+ const cwd = path.resolve(opts.cwd ?? process.cwd());
1973
+ const rawArgv = process.argv.slice(2);
1974
+ const args = [
1975
+ "docs",
1976
+ ...components,
1977
+ "--cwd",
1978
+ cwd
1979
+ ];
1980
+ if (opts.base) args.push("--base", opts.base);
1981
+ if (opts.json) args.push("--json");
1982
+ const passthroughArgs = extractPassthroughArgs(rawArgv, "docs");
1983
+ if (passthroughArgs.length > 0) args.push(...passthroughArgs);
1984
+ try {
1985
+ await runShadcnCommand({
1986
+ cwd,
1987
+ args
1988
+ });
1989
+ } catch {
1990
+ process.exit(1);
1991
+ }
1992
+ });
1993
+
1994
+ //#endregion
1995
+ //#region src/utils/tsconfig-utils.ts
1996
+ /**
1997
+ * Read and parse tsconfig.json from the project root
1998
+ */
1999
+ function readTsConfig(projectRoot) {
2000
+ try {
2001
+ const tsconfigPath = resolve(projectRoot, "tsconfig.json");
2002
+ if (!existsSync(tsconfigPath)) return null;
2003
+ const raw = readFileSync(tsconfigPath, "utf-8");
2004
+ return JSON.parse(raw);
2005
+ } catch {
2006
+ return null;
2007
+ }
2008
+ }
2009
+ /**
2010
+ * Resolve an alias path (like @/components) using tsconfig.json paths
2011
+ */
2012
+ function resolveAliasPathUsingTsConfig(inputPath, projectRoot) {
2013
+ const cfg = readTsConfig(projectRoot);
2014
+ if (!cfg || !cfg.compilerOptions) return null;
2015
+ const baseUrl = cfg.compilerOptions.baseUrl || ".";
2016
+ const paths = cfg.compilerOptions.paths || {};
2017
+ for (const [key, values] of Object.entries(paths)) {
2018
+ const pattern = key.replace(/\*/g, "(.*)");
2019
+ const re = new RegExp(`^${pattern}$`);
2020
+ const match = inputPath.match(re);
2021
+ if (!match) continue;
2022
+ const wildcard = match[1] || "";
2023
+ const first = Array.isArray(values) ? values[0] : values;
2024
+ if (!first) continue;
2025
+ const target = String(first).replace(/\*/g, wildcard);
2026
+ return resolve(projectRoot, baseUrl, target);
2027
+ }
2028
+ return null;
2029
+ }
2030
+
2031
+ //#endregion
2032
+ //#region src/commands/docs-build.ts
2033
+ async function generateDocs({ cwd, outDir, verbose }) {
2034
+ const DEBUG = process.env.BEJAMAS_DEBUG === "1" || process.env.BEJAMAS_DEBUG === "true" || verbose;
2035
+ try {
2036
+ const shellCwd = process.cwd();
2037
+ if (cwd && cwd.length) process.env.BEJAMAS_UI_ROOT = resolve(shellCwd, cwd);
2038
+ if (outDir && outDir.length) process.env.BEJAMAS_DOCS_OUT_DIR = outDir;
2039
+ let projectRoot = shellCwd;
2040
+ let probe = shellCwd;
2041
+ for (let i = 0; i < 6 && probe; i += 1) {
2042
+ const candidate = resolve(probe, "components.json");
2043
+ if (existsSync(candidate)) {
2044
+ projectRoot = probe;
2045
+ try {
2046
+ const raw = readFileSync(candidate, "utf-8");
2047
+ const config = JSON.parse(raw);
2048
+ if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.aliases?.ui) {
2049
+ const mapped = String(config.aliases.ui);
2050
+ let uiAbs = null;
2051
+ if (mapped.startsWith("./") || mapped.startsWith("../") || isAbsolute(mapped)) uiAbs = resolve(projectRoot, mapped);
2052
+ else uiAbs = resolveAliasPathUsingTsConfig(mapped, projectRoot);
2053
+ if (!uiAbs && mapped.startsWith("@/")) uiAbs = resolve(projectRoot, "src", mapped.slice(2));
2054
+ if (uiAbs) process.env.BEJAMAS_UI_ROOT = uiAbs;
2055
+ }
2056
+ if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.tailwind?.css) {
2057
+ const cssRaw = String(config.tailwind.css);
2058
+ let cssAbs = null;
2059
+ if (cssRaw.startsWith("./") || cssRaw.startsWith("../") || isAbsolute(cssRaw)) cssAbs = resolve(projectRoot, cssRaw);
2060
+ else cssAbs = resolveAliasPathUsingTsConfig(cssRaw, projectRoot);
2061
+ if (!cssAbs && cssRaw.startsWith("@/")) cssAbs = resolve(projectRoot, "src", cssRaw.slice(2));
2062
+ if (cssAbs) {
2063
+ const uiRootFromCss = resolve(cssAbs, "..", "..", "..");
2064
+ process.env.BEJAMAS_UI_ROOT = uiRootFromCss;
2065
+ }
2066
+ }
2067
+ if (!outDir && config?.aliases?.docs) {
2068
+ const mapped = String(config.aliases.docs);
2069
+ let outResolved = null;
2070
+ if (mapped.startsWith("./") || mapped.startsWith("../") || isAbsolute(mapped)) outResolved = mapped;
2071
+ else {
2072
+ const abs = resolveAliasPathUsingTsConfig(mapped, projectRoot);
2073
+ if (abs) outResolved = relative(projectRoot, abs);
2074
+ }
2075
+ if (!outResolved && mapped.startsWith("@/")) outResolved = mapped.replace(/^@\//, "src/");
2076
+ const finalOut = outResolved ?? mapped;
2077
+ if (finalOut && !process.env.BEJAMAS_DOCS_OUT_DIR) {
2078
+ process.env.BEJAMAS_DOCS_OUT_DIR = finalOut;
2079
+ process.env.BEJAMAS_DOCS_CWD = projectRoot;
2080
+ }
2081
+ }
2082
+ } catch {}
2083
+ break;
2084
+ }
2085
+ const parent = resolve(probe, "..");
2086
+ probe = parent === probe ? null : parent;
2087
+ }
2088
+ if (!process.env.BEJAMAS_DOCS_CWD) process.env.BEJAMAS_DOCS_CWD = shellCwd;
2089
+ if (!process.env.BEJAMAS_UI_ROOT) {
2090
+ const defaultGuess = (() => {
2091
+ let current = shellCwd;
2092
+ for (let i = 0; i < 6; i += 1) {
2093
+ const cand = resolve(current, "packages/ui/package.json");
2094
+ if (existsSync(cand)) {
2095
+ const abs = resolve(current, "packages/ui");
2096
+ return relative(shellCwd, abs) || abs;
2097
+ }
2098
+ const parent = resolve(current, "..");
2099
+ if (parent === current) break;
2100
+ current = parent;
2101
+ }
2102
+ const nm = resolve(shellCwd, "node_modules/@bejamas/ui/package.json");
2103
+ if (existsSync(nm)) {
2104
+ const abs = resolve(shellCwd, "node_modules/@bejamas/ui");
2105
+ return relative(shellCwd, abs) || abs;
2106
+ }
2107
+ return "packages/ui";
2108
+ })();
2109
+ const { uiRoot } = await prompts({
2110
+ type: "text",
2111
+ name: "uiRoot",
2112
+ message: "Path to @bejamas/ui package root:",
2113
+ initial: defaultGuess,
2114
+ validate: (val) => {
2115
+ const p = resolve(shellCwd, val);
2116
+ return existsSync(resolve(p, "package.json")) ? true : `No package.json found in ${p}`;
2117
+ }
2118
+ });
2119
+ if (!uiRoot) {
2120
+ logger.error("@bejamas/ui root is required to generate docs.");
2121
+ process.exit(1);
2122
+ }
2123
+ process.env.BEJAMAS_UI_ROOT = resolve(shellCwd, uiRoot);
2124
+ }
2125
+ if (!process.env.BEJAMAS_DOCS_OUT_DIR) {
2126
+ const { out } = await prompts({
2127
+ type: "text",
2128
+ name: "out",
2129
+ message: "Where should we output docs (relative to project root)?",
2130
+ initial: "src/content/docs/components"
2131
+ });
2132
+ if (!out) {
2133
+ logger.error("An output directory is required to generate docs.");
2134
+ process.exit(1);
2135
+ }
2136
+ process.env.BEJAMAS_DOCS_OUT_DIR = out;
2137
+ process.env.BEJAMAS_DOCS_CWD = process.env.BEJAMAS_DOCS_CWD || shellCwd;
2138
+ }
2139
+ process.env.BEJAMAS_SKIP_AUTO_RUN = "1";
2140
+ logger.info(`Generating docs...`);
2141
+ if (DEBUG) {
2142
+ logger.info(`Generator entry: @/src/docs/generate-mdx/index`);
2143
+ if (process.env.BEJAMAS_UI_ROOT) logger.info(`UI root: ${process.env.BEJAMAS_UI_ROOT}`);
2144
+ if (process.env.BEJAMAS_DOCS_CWD) logger.info(`Docs CWD: ${process.env.BEJAMAS_DOCS_CWD}`);
2145
+ if (process.env.BEJAMAS_DOCS_OUT_DIR) logger.info(`Docs out: ${process.env.BEJAMAS_DOCS_OUT_DIR}`);
2146
+ }
2147
+ const mod = await import("./generate-mdx-BSRQJPrF.js");
2148
+ if (typeof mod.runDocsGenerator === "function") await mod.runDocsGenerator();
2149
+ else throw new Error("Failed to load docs generator. Export 'runDocsGenerator' not found.");
2150
+ } catch (err) {
2151
+ logger.error(err?.message || String(err));
2152
+ process.exit(1);
2153
+ }
2154
+ }
2155
+ const docsBuild = new Command().name("docs:build").description("generate docs from @bejamas/ui components").option("-c, --cwd <cwd>", "path to UI working directory").option("-o, --out <outDir>", "output directory for generated MDX files").action(async (opts) => {
2156
+ await generateDocs({
2157
+ cwd: opts.cwd,
2158
+ outDir: opts.out,
2159
+ verbose: Boolean(opts.verbose)
2160
+ });
2161
+ });
2162
+
2163
+ //#endregion
2164
+ //#region src/commands/docs-check.ts
2165
+ const REQUIRED_FIELDS = [
2166
+ "name",
2167
+ "title",
2168
+ "description"
2169
+ ];
2170
+ const RECOMMENDED_FIELDS = [
2171
+ "primaryExampleMDX",
2172
+ "usageMDX",
2173
+ "figmaUrl"
2174
+ ];
2175
+ const FIELD_LABELS = {
2176
+ name: "@component",
2177
+ title: "@title",
2178
+ description: "@description",
2179
+ primaryExampleMDX: "@preview",
2180
+ usageMDX: "@usage",
2181
+ figmaUrl: "@figmaUrl"
2182
+ };
2183
+ function checkComponentDocs(filePath, fileName) {
2184
+ const content = readFileSync(filePath, "utf-8");
2185
+ const frontmatter = extractFrontmatter(content);
2186
+ const meta = parseJsDocMetadata(frontmatter);
2187
+ const componentName = fileName.replace(/\.astro$/i, "");
2188
+ const missingRequired = [];
2189
+ const missingRecommended = [];
2190
+ for (const field of REQUIRED_FIELDS) {
2191
+ const value = meta[field];
2192
+ if (!value || typeof value === "string" && !value.trim()) missingRequired.push(FIELD_LABELS[field] || field);
2193
+ }
2194
+ for (const field of RECOMMENDED_FIELDS) {
2195
+ const value = meta[field];
2196
+ if (!value || typeof value === "string" && !value.trim()) missingRecommended.push(FIELD_LABELS[field] || field);
2197
+ }
2198
+ let status;
2199
+ if (missingRequired.length > 0) status = "missing";
2200
+ else if (missingRecommended.length > 0) status = "incomplete";
2201
+ else status = "complete";
2202
+ return {
2203
+ name: componentName,
2204
+ file: fileName,
2205
+ status,
2206
+ missingRequired,
2207
+ missingRecommended
2208
+ };
2209
+ }
2210
+ async function checkDocs({ cwd, json }) {
2211
+ try {
2212
+ const shellCwd = process.cwd();
2213
+ let projectRoot = shellCwd;
2214
+ let probe = shellCwd;
2215
+ for (let i = 0; i < 6 && probe; i += 1) {
2216
+ const candidate = resolve(probe, "components.json");
2217
+ if (existsSync(candidate)) {
2218
+ projectRoot = probe;
2219
+ try {
2220
+ const raw = readFileSync(candidate, "utf-8");
2221
+ const config = JSON.parse(raw);
2222
+ if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.aliases?.ui) {
2223
+ const mapped = String(config.aliases.ui);
2224
+ let uiAbs = null;
2225
+ if (mapped.startsWith("./") || mapped.startsWith("../") || isAbsolute(mapped)) uiAbs = resolve(projectRoot, mapped);
2226
+ else uiAbs = resolveAliasPathUsingTsConfig(mapped, projectRoot);
2227
+ if (!uiAbs && mapped.startsWith("@/")) uiAbs = resolve(projectRoot, "src", mapped.slice(2));
2228
+ if (uiAbs) process.env.BEJAMAS_UI_ROOT = uiAbs;
2229
+ }
2230
+ if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.tailwind?.css) {
2231
+ const cssRaw = String(config.tailwind.css);
2232
+ let cssAbs = null;
2233
+ if (cssRaw.startsWith("./") || cssRaw.startsWith("../") || isAbsolute(cssRaw)) cssAbs = resolve(projectRoot, cssRaw);
2234
+ else cssAbs = resolveAliasPathUsingTsConfig(cssRaw, projectRoot);
2235
+ if (!cssAbs && cssRaw.startsWith("@/")) cssAbs = resolve(projectRoot, "src", cssRaw.slice(2));
2236
+ if (cssAbs) {
2237
+ const uiRootFromCss = resolve(cssAbs, "..", "..", "..");
2238
+ process.env.BEJAMAS_UI_ROOT = uiRootFromCss;
2239
+ }
2240
+ }
2241
+ } catch {}
2242
+ break;
2243
+ }
2244
+ const parent = resolve(probe, "..");
2245
+ probe = parent === probe ? null : parent;
2246
+ }
2247
+ if (cwd && cwd.length) process.env.BEJAMAS_UI_ROOT = resolve(cwd);
2248
+ let uiRoot;
2249
+ try {
2250
+ uiRoot = resolveUiRoot(shellCwd);
2251
+ } catch {
2252
+ logger.error("Unable to locate @bejamas/ui. Use --cwd to specify the UI package path.");
2253
+ process.exit(1);
2254
+ }
2255
+ const componentsDir = join(uiRoot, "src", "components");
2256
+ if (!existsSync(componentsDir)) {
2257
+ logger.error(`Components directory not found: ${componentsDir}\n\nExpected structure: <uiRoot>/src/components/*.astro\nUse --cwd to specify a different UI package root.`);
2258
+ process.exit(1);
2259
+ }
2260
+ const files = readdirSync(componentsDir, { withFileTypes: true }).filter((e) => e.isFile() && extname(e.name).toLowerCase() === ".astro").map((e) => e.name).sort();
2261
+ if (files.length === 0) {
2262
+ logger.warn("No .astro component files found.");
2263
+ process.exit(0);
2264
+ }
2265
+ const results = [];
2266
+ for (const file of files) {
2267
+ const status = checkComponentDocs(join(componentsDir, file), file);
2268
+ results.push(status);
2269
+ }
2270
+ const complete = results.filter((r) => r.status === "complete");
2271
+ const incomplete = results.filter((r) => r.status === "incomplete");
2272
+ const missing = results.filter((r) => r.status === "missing");
2273
+ const checkResult = {
2274
+ total: results.length,
2275
+ complete,
2276
+ incomplete,
2277
+ missing
2278
+ };
2279
+ if (json) {
2280
+ console.log(JSON.stringify(checkResult, null, 2));
2281
+ if (missing.length > 0) process.exit(1);
2282
+ return;
2283
+ }
2284
+ const termWidth = Math.min(80, process.stdout.columns || 80);
2285
+ const headerLine = "━".repeat(termWidth);
2286
+ logger.break();
2287
+ console.log(dim("┌" + "─".repeat(termWidth - 2) + "┐"));
2288
+ console.log(dim("│") + bold(cyan(" docs:check")) + dim(" — Component Documentation Status") + " ".repeat(Math.max(0, termWidth - 47)) + dim("│"));
2289
+ console.log(dim("└" + "─".repeat(termWidth - 2) + "┘"));
2290
+ logger.break();
2291
+ const formatTag = (tag) => magenta(tag);
2292
+ const formatComponentName = (name) => bold(white(name));
2293
+ if (complete.length > 0) {
2294
+ console.log(green(`✓ Complete (${complete.length} component${complete.length === 1 ? "" : "s"}):`));
2295
+ const names = complete.map((c) => formatComponentName(c.name)).join(dim(", "));
2296
+ console.log(` ${names}`);
2297
+ logger.break();
2298
+ }
2299
+ if (incomplete.length > 0) {
2300
+ console.log(yellow(`⚠ Incomplete (${incomplete.length} component${incomplete.length === 1 ? "" : "s"}):`));
2301
+ for (const comp of incomplete) {
2302
+ const missingFields = comp.missingRecommended.map(formatTag).join(dim(", "));
2303
+ console.log(` ${formatComponentName(comp.name)} ${dim("-")} ${dim("missing:")} ${missingFields}`);
2304
+ }
2305
+ logger.break();
2306
+ }
2307
+ if (missing.length > 0) {
2308
+ console.log(red(`✗ Missing Docs (${missing.length} component${missing.length === 1 ? "" : "s"}):`));
2309
+ for (const comp of missing) {
2310
+ const missingFields = comp.missingRequired.map(formatTag).join(dim(", "));
2311
+ console.log(` ${formatComponentName(comp.name)} ${dim("-")} ${dim("missing:")} ${missingFields}`);
2312
+ }
2313
+ logger.break();
2314
+ }
2315
+ console.log(dim(headerLine));
2316
+ const completeText = green(`${complete.length}/${results.length} complete`);
2317
+ const incompleteText = incomplete.length > 0 ? yellow(`${incomplete.length} incomplete`) : dim(`${incomplete.length} incomplete`);
2318
+ const missingText = missing.length > 0 ? red(`${missing.length} missing docs`) : dim(`${missing.length} missing docs`);
2319
+ console.log(`${bold("Summary:")} ${completeText} ${dim("|")} ${incompleteText} ${dim("|")} ${missingText}`);
2320
+ logger.break();
2321
+ if (missing.length > 0) process.exit(1);
2322
+ } catch (err) {
2323
+ logger.error(err?.message || String(err));
2324
+ process.exit(1);
2325
+ }
2326
+ }
2327
+ const docsCheck = new Command().name("docs:check").description("check documentation status for all components").option("-c, --cwd <cwd>", "path to UI working directory").option("--json", "output results as JSON").action(async (opts) => {
2328
+ await checkDocs({
2329
+ cwd: opts.cwd,
2330
+ json: Boolean(opts.json)
2331
+ });
2332
+ });
2333
+
2334
+ //#endregion
2335
+ //#region ../registry/src/lib/headless-dependencies.ts
2336
+ /** Headless behaviors imported by emitted frontmatter, client scripts or helpers. */
2337
+ function getHeadlessDependencies(files) {
2338
+ const dependencies = /* @__PURE__ */ new Set();
2339
+ for (const file of files) {
2340
+ const source = (file.content ?? "").replace(/\/\*[\s\S]*?\*\//g, "");
2341
+ for (const match of source.matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*)["'](@data-slot\/[^/"']+)(?:\/[^"']*)?["']/g)) dependencies.add(match[1]);
2342
+ }
2343
+ return [...dependencies].sort();
2344
+ }
2345
+
2346
+ //#endregion
2347
+ //#region src/utils/registry-dependencies.ts
2348
+ function missingRegistryDependencies(manifest, items) {
2349
+ const declared = {
2350
+ ...manifest.devDependencies,
2351
+ ...manifest.dependencies
2352
+ };
2353
+ const candidates = /* @__PURE__ */ new Map();
2354
+ for (const item of items) for (const specifier of item.dependencies ?? []) {
2355
+ const name = specifier.match(/^(@[^/]+\/[^@/]+|[^@/]+)/)?.[1];
2356
+ if (name && !declared[name]) candidates.set(name, specifier);
2357
+ }
2358
+ for (const item of items) for (const name of getHeadlessDependencies(item.files ?? [])) if (!declared[name] && !candidates.has(name)) candidates.set(name, name);
2359
+ return [...candidates.values()].sort();
2360
+ }
2361
+ async function ensureRegistryDependencies(cwd, items) {
2362
+ const missing = missingRegistryDependencies(JSON.parse(await fs$1.readFile(path.join(cwd, "package.json"), "utf8")), items);
2363
+ if (!missing.length) return;
2364
+ const manager = await getPackageManager(cwd);
2365
+ await execa(manager, [manager === "npm" ? "install" : "add", ...missing], {
2366
+ cwd,
2367
+ stdio: "inherit"
2368
+ });
2369
+ }
2370
+
2371
+ //#endregion
2372
+ //#region src/commands/add.ts
2373
+ function extractOptionsForShadcn(rawArgv, cmd) {
2374
+ if (typeof cmd.getOptionValueSource === "function") {
2375
+ const opts = cmd.optsWithGlobals();
2376
+ const forwarded = [];
2377
+ const getSource = (key) => cmd.getOptionValueSource(key);
2378
+ const addBoolean = (key, flag, negateFlag) => {
2379
+ if (getSource(key) !== "cli") return;
2380
+ const value = opts[key];
2381
+ if (typeof value !== "boolean") return;
2382
+ if (value) forwarded.push(flag);
2383
+ else if (negateFlag) forwarded.push(negateFlag);
2384
+ };
2385
+ const addString = (key, flag) => {
2386
+ if (getSource(key) !== "cli") return;
2387
+ const value = opts[key];
2388
+ if (typeof value === "string") forwarded.push(flag, value);
2389
+ };
2390
+ const addOptionalString = (key, flag) => {
2391
+ if (getSource(key) !== "cli") return;
2392
+ const value = opts[key];
2393
+ if (value === true) {
2394
+ forwarded.push(flag);
2395
+ return;
2396
+ }
2397
+ if (typeof value === "string") forwarded.push(flag, value);
2398
+ };
2399
+ addBoolean("yes", "--yes");
2400
+ addBoolean("overwrite", "--overwrite");
2401
+ addBoolean("dryRun", "--dry-run");
2402
+ addString("cwd", "--cwd");
2403
+ addBoolean("all", "--all");
2404
+ addString("path", "--path");
2405
+ addBoolean("silent", "--silent");
2406
+ addBoolean("srcDir", "--src-dir", "--no-src-dir");
2407
+ addOptionalString("diff", "--diff");
2408
+ addOptionalString("view", "--view");
2409
+ const addIndex = rawArgv.findIndex((arg) => arg === "add");
2410
+ if (addIndex !== -1) {
2411
+ const rest = rawArgv.slice(addIndex + 1);
2412
+ const doubleDashIndex = rest.indexOf("--");
2413
+ if (doubleDashIndex !== -1) forwarded.push(...rest.slice(doubleDashIndex));
2414
+ }
2415
+ return forwarded;
2416
+ }
2417
+ const addIndex = rawArgv.findIndex((arg) => arg === "add");
2418
+ if (addIndex === -1) return [];
2419
+ const rest = rawArgv.slice(addIndex + 1);
2420
+ const forwarded = [];
2421
+ const optionsWithValues = /* @__PURE__ */ new Set([
2422
+ "-c",
2423
+ "--cwd",
2424
+ "-p",
2425
+ "--path",
2426
+ "--diff",
2427
+ "--view"
2428
+ ]);
2429
+ const filteredFlags = /* @__PURE__ */ new Set(["-v", "--verbose"]);
2430
+ for (let index = 0; index < rest.length; index += 1) {
2431
+ const token = rest[index];
2432
+ if (token === "--") {
2433
+ forwarded.push("--", ...rest.slice(index + 1));
2434
+ break;
2435
+ }
2436
+ if (!token.startsWith("-")) continue;
2437
+ if (filteredFlags.has(token)) continue;
2438
+ forwarded.push(token);
2439
+ if (token.includes("=")) continue;
2440
+ if (optionsWithValues.has(token)) {
2441
+ const next = rest[index + 1];
2442
+ if (next) {
2443
+ forwarded.push(next);
2444
+ index += 1;
2445
+ }
2446
+ }
2447
+ }
2448
+ return forwarded;
2449
+ }
2450
+ function hasInspectionFlags(forwardedOptions) {
2451
+ return forwardedOptions.includes("--dry-run") || forwardedOptions.includes("--diff") || forwardedOptions.includes("--view");
2452
+ }
2453
+ function formatSkippedFilesHeading(count, overwriteUsed) {
2454
+ const noun = `file${count === 1 ? "" : "s"}`;
2455
+ if (overwriteUsed) return `Skipped ${count} ${noun}: (files might be identical)`;
2456
+ return `Skipped ${count} ${noun}: (files might be identical, use --overwrite to overwrite)`;
2457
+ }
2458
+ function ensureTrailingNewline(output) {
2459
+ return output.endsWith("\n") ? output : `${output}\n`;
2460
+ }
2461
+ function writeCapturedShadcnOutput(stdout, stderr) {
2462
+ if (stdout) process.stdout.write(ensureTrailingNewline(stdout));
2463
+ if (stderr) process.stderr.write(ensureTrailingNewline(stderr));
2464
+ }
2465
+ async function buildSubfolderMap(components, uiDir, registryUrl, style) {
2466
+ const filenameToSubfolders = /* @__PURE__ */ new Map();
2467
+ let requiresReorganization = false;
2468
+ for (const componentName of components) {
2469
+ const registryItems = await fetchRegistryTree([componentName], registryUrl, style);
2470
+ for (const registryItem of registryItems) {
2471
+ if (shouldReorganizeRegistryUiFiles(registryItem.files, uiDir)) requiresReorganization = true;
2472
+ const subfolder = getSubfolderFromPaths(registryItem.files);
2473
+ if (!subfolder) continue;
2474
+ for (const file of registryItem.files) if (file.type === "registry:ui") {
2475
+ const filename = path.basename(file.path);
2476
+ const subfolders = filenameToSubfolders.get(filename) || [];
2477
+ subfolders.push(subfolder);
2478
+ filenameToSubfolders.set(filename, subfolders);
2479
+ }
2480
+ }
2481
+ }
2482
+ const uniqueMap = /* @__PURE__ */ new Map();
2483
+ const sharedFilenames = /* @__PURE__ */ new Set();
2484
+ filenameToSubfolders.forEach((subfolders, filename) => {
2485
+ if (subfolders.length === 1) uniqueMap.set(filename, `${subfolders[0]}/${filename}`);
2486
+ else sharedFilenames.add(filename);
2487
+ });
2488
+ return {
2489
+ uniqueMap,
2490
+ sharedFilenames,
2491
+ requiresReorganization
2492
+ };
2493
+ }
2494
+ function rewritePaths(paths, mapResult) {
2495
+ const { uniqueMap, sharedFilenames } = mapResult;
2496
+ let currentSubfolder = null;
2497
+ return paths.map((filePath) => {
2498
+ const filename = path.basename(filePath);
2499
+ const parentDir = path.basename(path.dirname(filePath));
2500
+ const uniqueMapping = uniqueMap.get(filename);
2501
+ if (uniqueMapping) {
2502
+ const expectedSubfolder = path.dirname(uniqueMapping);
2503
+ currentSubfolder = expectedSubfolder;
2504
+ if (parentDir !== expectedSubfolder) return `${path.dirname(filePath)}/${uniqueMapping}`;
2505
+ return filePath;
2506
+ }
2507
+ if (sharedFilenames.has(filename) && currentSubfolder) {
2508
+ if (parentDir !== currentSubfolder) return `${path.dirname(filePath)}/${currentSubfolder}/${filename}`;
2509
+ }
2510
+ return filePath;
2511
+ });
2512
+ }
2513
+ async function fetchAvailableComponents(registryUrl) {
2514
+ const indexUrl = `${registryUrl}/index.json`;
2515
+ const response = await fetch(indexUrl);
2516
+ if (!response.ok) throw new Error(`Failed to fetch registry index: ${response.statusText}`);
2517
+ const data = await response.json();
2518
+ return Array.isArray(data) ? data : [];
2519
+ }
2520
+ async function promptForComponents(registryUrl) {
2521
+ const checkingSpinner = spinner("Checking registry.").start();
2522
+ let components;
2523
+ try {
2524
+ components = await fetchAvailableComponents(registryUrl);
2525
+ checkingSpinner.succeed();
2526
+ } catch {
2527
+ checkingSpinner.fail();
2528
+ logger.error("Failed to fetch available components from registry.");
2529
+ return null;
2530
+ }
2531
+ if (components.length === 0) {
2532
+ logger.warn("No components available in registry.");
2533
+ return null;
2534
+ }
2535
+ const choices = components.filter((component) => !component.type || component.type === "registry:ui").map((component) => ({
2536
+ title: component.name,
2537
+ value: component.name
2538
+ }));
2539
+ const { selected } = await prompts({
2540
+ type: "autocompleteMultiselect",
2541
+ name: "selected",
2542
+ message: "Which components would you like to add?",
2543
+ choices,
2544
+ hint: "- Space to select. Return to submit.",
2545
+ instructions: false
2546
+ });
2547
+ if (!selected) return null;
2548
+ return selected;
2549
+ }
2550
+ function parseShadcnOutput(stdout, stderr) {
2551
+ const result = {
2552
+ created: [],
2553
+ updated: [],
2554
+ skipped: []
2555
+ };
2556
+ const cleanStderr = stderr.replace(/\x1b\[[0-9;]*m/g, "");
2557
+ const cleanStdout = stdout.replace(/\x1b\[[0-9;]*m/g, "");
2558
+ const createdMatch = cleanStderr.match(/Created\s+(\d+)\s+file/i);
2559
+ const updatedMatch = cleanStderr.match(/Updated\s+(\d+)\s+file/i);
2560
+ const skippedMatch = cleanStderr.match(/Skipped\s+(\d+)\s+file/i);
2561
+ const createdCount = createdMatch ? parseInt(createdMatch[1], 10) : 0;
2562
+ const updatedCount = updatedMatch ? parseInt(updatedMatch[1], 10) : 0;
2563
+ const skippedCount = skippedMatch ? parseInt(skippedMatch[1], 10) : 0;
2564
+ const allPaths = [];
2565
+ for (const line of cleanStdout.split("\n")) {
2566
+ const match = line.match(/^\s+-\s+(.+)$/);
2567
+ if (match) allPaths.push(match[1].trim());
2568
+ }
2569
+ for (const line of cleanStderr.split("\n")) {
2570
+ const match = line.match(/^\s+-\s+(.+)$/);
2571
+ if (match) {
2572
+ const filePath = match[1].trim();
2573
+ if (!allPaths.includes(filePath)) allPaths.push(filePath);
2574
+ }
2575
+ }
2576
+ let index = 0;
2577
+ for (let count = 0; count < createdCount && index < allPaths.length; count += 1) {
2578
+ result.created.push(allPaths[index]);
2579
+ index += 1;
2580
+ }
2581
+ for (let count = 0; count < updatedCount && index < allPaths.length; count += 1) {
2582
+ result.updated.push(allPaths[index]);
2583
+ index += 1;
2584
+ }
2585
+ for (let count = 0; count < skippedCount && index < allPaths.length; count += 1) {
2586
+ result.skipped.push(allPaths[index]);
2587
+ index += 1;
2588
+ }
2589
+ return result;
2590
+ }
2591
+ async function addComponents(cwd, packages, forwardedOptions, isVerbose, isSilent, inspectionMode) {
2592
+ const env = {
2593
+ ...process.env,
2594
+ REGISTRY_URL: resolveRegistryUrl()
2595
+ };
2596
+ await ensurePinnedShadcnExecPrefix();
2597
+ const shadcnArgs = buildShadcnAddArgs(packages, forwardedOptions);
2598
+ const invocation = buildPinnedShadcnInvocation(shadcnArgs);
2599
+ if (isVerbose) logger.info(`[bejamas-ui] ${invocation.cmd} ${invocation.args.join(" ")}`);
2600
+ const registrySpinner = spinner("Checking registry.", { silent: isSilent });
2601
+ registrySpinner.start();
2602
+ try {
2603
+ const result = await execa(invocation.cmd, invocation.args, {
2604
+ cwd,
2605
+ env,
2606
+ input: "n\nn\nn\nn\nn\nn\nn\nn\nn\nn\n",
2607
+ stdout: "pipe",
2608
+ stderr: "pipe",
2609
+ reject: false
2610
+ });
2611
+ registrySpinner.succeed();
2612
+ spinner("Installing components.", { silent: isSilent }).succeed();
2613
+ const stdout = result.stdout || "";
2614
+ const stderr = result.stderr || "";
2615
+ if (isVerbose) {
2616
+ logger.info(`[bejamas-ui] Raw stdout: ${stdout}`);
2617
+ logger.info(`[bejamas-ui] Raw stderr: ${stderr}`);
2618
+ }
2619
+ if (inspectionMode) writeCapturedShadcnOutput(stdout, stderr);
2620
+ const parsed = inspectionMode ? {
2621
+ created: [],
2622
+ updated: [],
2623
+ skipped: []
2624
+ } : parseShadcnOutput(stdout, stderr);
2625
+ if (result.exitCode !== 0) {
2626
+ if (!inspectionMode) writeCapturedShadcnOutput(stdout, stderr);
2627
+ process.exit(result.exitCode);
2628
+ }
2629
+ return parsed;
2630
+ } catch {
2631
+ registrySpinner.fail();
2632
+ logger.error("Failed to add components");
2633
+ process.exit(1);
2634
+ }
2635
+ }
2636
+ function buildShadcnAddArgs(packages, forwardedOptions) {
2637
+ const autoFlags = [];
2638
+ if (!forwardedOptions.includes("--yes")) autoFlags.push("--yes");
2639
+ return [
2640
+ "add",
2641
+ ...packages,
2642
+ ...autoFlags,
2643
+ ...forwardedOptions
2644
+ ];
2645
+ }
2646
+ const add = new Command().name("add").description("Add components via the Bejamas-managed shadcn registry flow").argument("[components...]", "Component package names to add").option("-y, --yes", "skip confirmation prompt.", false).option("-o, --overwrite", "overwrite existing files.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-a, --all", "add all available components", false).option("-p, --path <path>", "the path to add the component to.").option("-s, --silent", "mute output.", false).option("--dry-run", "preview changes without writing files.", false).option("--diff [path]", "show diff for a file.").option("--view [path]", "show file contents.").option("--src-dir", "use the src directory when creating a new project.", false).option("--no-src-dir", "do not use the src directory when creating a new project.").action(async function action(packages, _opts, cmd) {
2647
+ const root = cmd?.parent;
2648
+ const verbose = Boolean(root?.opts?.().verbose);
2649
+ const forwardedOptions = extractOptionsForShadcn(process.argv.slice(2), cmd);
2650
+ const opts = typeof cmd.optsWithGlobals === "function" ? cmd.optsWithGlobals() : cmd.opts?.() ?? {};
2651
+ const inspectionMode = hasInspectionFlags(forwardedOptions);
2652
+ const overwriteUsed = forwardedOptions.includes("--overwrite") || forwardedOptions.includes("-o");
2653
+ const cwd = opts.cwd || process.cwd();
2654
+ let componentsToAdd = packages || [];
2655
+ const wantsAll = Boolean(opts.all);
2656
+ const isSilent = opts.silent || false;
2657
+ const registryUrl = resolveRegistryUrl();
2658
+ if (wantsAll && componentsToAdd.length === 0) {
2659
+ const fetchingSpinner = spinner("Fetching available components.", { silent: isSilent }).start();
2660
+ try {
2661
+ componentsToAdd = (await fetchAvailableComponents(registryUrl)).filter((component) => !component.type || component.type === "registry:ui").map((component) => component.name);
2662
+ fetchingSpinner.succeed();
2663
+ } catch {
2664
+ fetchingSpinner.fail();
2665
+ logger.error("Failed to fetch available components from registry.");
2666
+ process.exit(1);
2667
+ }
2668
+ }
2669
+ if (componentsToAdd.length === 0) {
2670
+ const selected = await promptForComponents(registryUrl);
2671
+ if (!selected || selected.length === 0) return;
2672
+ componentsToAdd = selected;
2673
+ }
2674
+ const config = await getConfig(cwd);
2675
+ let uiDir = config?.resolvedPaths?.ui || "";
2676
+ let uiConfig = config;
2677
+ if (config) {
2678
+ const workspaceConfig = await getWorkspaceConfig(config);
2679
+ if (workspaceConfig?.ui) {
2680
+ uiConfig = workspaceConfig.ui;
2681
+ uiDir = uiConfig.resolvedPaths?.ui || uiDir;
2682
+ }
2683
+ }
2684
+ if (verbose) {
2685
+ logger.info(`[bejamas-ui] cwd: ${cwd}`);
2686
+ logger.info(`[bejamas-ui] uiDir: ${uiDir}`);
2687
+ logger.info(`[bejamas-ui] aliases.ui: ${uiConfig?.aliases?.ui || "not set"}`);
2688
+ }
2689
+ const activeStyle = uiConfig?.style || config?.style || "bejamas-juno";
2690
+ const totalComponents = componentsToAdd.length;
2691
+ for (let index = 0; index < componentsToAdd.length; index += 1) {
2692
+ const component = componentsToAdd[index];
2693
+ if (totalComponents > 1 && !isSilent) {
2694
+ logger.break();
2695
+ logger.info(highlighter.info(`[${index + 1}/${totalComponents}]`) + ` Adding ${highlighter.success(component)}...`);
2696
+ }
2697
+ const subfolderMapResult = inspectionMode ? {
2698
+ uniqueMap: /* @__PURE__ */ new Map(),
2699
+ sharedFilenames: /* @__PURE__ */ new Set(),
2700
+ requiresReorganization: false
2701
+ } : await buildSubfolderMap([component], uiDir, registryUrl, activeStyle);
2702
+ const parsed = await addComponents(cwd, [component], forwardedOptions, verbose, isSilent, inspectionMode);
2703
+ if (!inspectionMode) {
2704
+ await syncManagedTailwindCss(cwd);
2705
+ const registryItem = await fetchRegistryItem(component, registryUrl, activeStyle);
2706
+ if (registryItem?.type === "registry:font") {
2707
+ const nextFont = toManagedAstroFont(registryItem.name);
2708
+ if (nextFont) {
2709
+ const currentFonts = await readManagedAstroFontsFromProject(cwd);
2710
+ const nextFonts = mergeManagedAstroFonts(currentFonts, nextFont);
2711
+ await syncAstroFontsInProject(cwd, nextFonts, nextFont.cssVariable);
2712
+ await syncAstroManagedFontCss(cwd, nextFont.cssVariable);
2713
+ await cleanupAstroFontPackages(cwd);
2714
+ }
2715
+ }
2716
+ }
2717
+ let skippedCount = 0;
2718
+ if (!inspectionMode && uiDir && subfolderMapResult.requiresReorganization) skippedCount = (await reorganizeComponents([component], uiDir, registryUrl, verbose, activeStyle, overwriteUsed)).skippedFiles.length;
2719
+ if (!isSilent && !inspectionMode) {
2720
+ const actuallyCreated = Math.max(0, parsed.created.length - skippedCount);
2721
+ if (actuallyCreated > 0) {
2722
+ const createdPaths = rewritePaths(parsed.created.slice(0, actuallyCreated), subfolderMapResult);
2723
+ logger.success(`Created ${createdPaths.length} file${createdPaths.length > 1 ? "s" : ""}:`);
2724
+ for (const file of createdPaths) logger.log(` ${highlighter.info("-")} ${file}`);
2725
+ }
2726
+ if (parsed.updated.length > 0) {
2727
+ const updatedPaths = rewritePaths(Array.from(new Set(parsed.updated)), subfolderMapResult);
2728
+ logger.info(`Updated ${updatedPaths.length} file${updatedPaths.length > 1 ? "s" : ""}:`);
2729
+ for (const file of updatedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
2730
+ }
2731
+ if (skippedCount > 0) logger.info(`Skipped ${skippedCount} file${skippedCount > 1 ? "s" : ""}: (already exists)`);
2732
+ if (parsed.skipped.length > 0) {
2733
+ const skippedPaths = rewritePaths(parsed.skipped, subfolderMapResult);
2734
+ logger.info(formatSkippedFilesHeading(skippedPaths.length, overwriteUsed));
2735
+ for (const file of skippedPaths) logger.log(` ${highlighter.info("-")} ${file}`);
2736
+ }
2737
+ if (actuallyCreated === 0 && parsed.updated.length === 0 && skippedCount === 0 && parsed.skipped.length === 0) logger.info("Already up to date.");
2738
+ }
2739
+ }
2740
+ if (!inspectionMode) {
2741
+ await fixAstroImports(cwd, verbose, uiConfig);
2742
+ if (uiConfig) {
2743
+ await repairUiPackageExports(uiDir, uiConfig.resolvedPaths.cwd);
2744
+ const items = await fetchRegistryTree(componentsToAdd, registryUrl, activeStyle);
2745
+ await ensureRegistryDependencies(uiConfig.resolvedPaths.cwd, items);
2746
+ }
2747
+ }
2748
+ });
2749
+
2750
+ //#endregion
2751
+ //#region src/utils/file-helper.ts
2752
+ const FILE_BACKUP_SUFFIX = ".bak";
2753
+ var FileBackupError = class extends Error {
2754
+ constructor(filePath) {
2755
+ super(`Could not back up ${filePath}.`);
2756
+ this.name = "FileBackupError";
2757
+ this.filePath = filePath;
2758
+ }
2759
+ };
2760
+ function createFileBackup(filePath) {
2761
+ if (!fs.existsSync(filePath)) return null;
2762
+ const backupPath = `${filePath}${FILE_BACKUP_SUFFIX}`;
2763
+ if (fs.existsSync(backupPath)) return null;
2764
+ try {
2765
+ fs.copyFileSync(filePath, backupPath);
2766
+ return backupPath;
2767
+ } catch {
2768
+ return null;
2769
+ }
2770
+ }
2771
+ function restoreFileBackup(filePath) {
2772
+ const backupPath = `${filePath}${FILE_BACKUP_SUFFIX}`;
2773
+ if (!fs.existsSync(backupPath)) return false;
2774
+ try {
2775
+ fs.copyFileSync(backupPath, filePath);
2776
+ fs.unlinkSync(backupPath);
2777
+ return true;
2778
+ } catch (error) {
2779
+ console.error(`Warning: Could not restore backup file ${backupPath}: ${error}`);
2780
+ return false;
2781
+ }
2782
+ }
2783
+ function deleteFileBackup(filePath) {
2784
+ const backupPath = `${filePath}${FILE_BACKUP_SUFFIX}`;
2785
+ if (!fs.existsSync(backupPath)) return false;
2786
+ try {
2787
+ fs.unlinkSync(backupPath);
2788
+ return true;
2789
+ } catch {
2790
+ return false;
2791
+ }
2792
+ }
2793
+
2794
+ //#endregion
2795
+ //#region src/utils/get-monorepo-info.ts
2796
+ const FRAMEWORK_CONFIG_FILES = [
2797
+ "next.config.*",
2798
+ "vite.config.*",
2799
+ "astro.config.*",
2800
+ "remix.config.*",
2801
+ "nuxt.config.*",
2802
+ "svelte.config.*",
2803
+ "gatsby-config.*",
2804
+ "angular.json"
2805
+ ];
2806
+ async function isMonorepoRoot(cwd) {
2807
+ if (fs.existsSync(path.resolve(cwd, "pnpm-workspace.yaml"))) return true;
2808
+ const packageJsonPath = path.resolve(cwd, "package.json");
2809
+ if (fs.existsSync(packageJsonPath)) try {
2810
+ if ((await fs.readJson(packageJsonPath)).workspaces) return true;
2811
+ } catch {
2812
+ return false;
2813
+ }
2814
+ if (fs.existsSync(path.resolve(cwd, "lerna.json"))) return true;
2815
+ if (fs.existsSync(path.resolve(cwd, "nx.json"))) return true;
2816
+ return false;
2817
+ }
2818
+ async function getMonorepoTargets(cwd) {
2819
+ const patterns = await getWorkspacePatterns(cwd);
2820
+ if (!patterns.length) return [];
2821
+ const dirs = await fg(patterns, {
2822
+ cwd,
2823
+ onlyDirectories: true,
2824
+ ignore: ["**/node_modules/**"]
2825
+ });
2826
+ const targets = [];
2827
+ for (const dir of dirs) {
2828
+ const fullPath = path.resolve(cwd, dir);
2829
+ if (!fs.existsSync(path.resolve(fullPath, "package.json"))) continue;
2830
+ const hasComponentsJson = fs.existsSync(path.resolve(fullPath, "components.json"));
2831
+ const hasFrameworkConfig = FRAMEWORK_CONFIG_FILES.some((pattern) => fg.sync(pattern, {
2832
+ cwd: fullPath,
2833
+ dot: true
2834
+ }).length > 0);
2835
+ if (hasComponentsJson || hasFrameworkConfig) targets.push({
2836
+ name: dir,
2837
+ hasConfig: hasComponentsJson
2838
+ });
2839
+ }
2840
+ return targets;
2841
+ }
2842
+ function formatMonorepoMessage(command, targets, options = {}) {
2843
+ const binary = options.binary ?? "bejamas";
2844
+ const cwdFlag = options.cwdFlag ?? "-c";
2845
+ logger.break();
2846
+ logger.log(`It looks like you are running ${highlighter.info(command)} from a monorepo root.`);
2847
+ logger.log(`To use ${binary} in a specific workspace, use the ${highlighter.info(cwdFlag)} flag:`);
2848
+ logger.break();
2849
+ for (const target of targets) logger.log(` ${binary} ${command} ${cwdFlag} ${target.name}`);
2850
+ logger.break();
2851
+ }
2852
+ async function getWorkspacePatterns(cwd) {
2853
+ const patterns = [];
2854
+ const pnpmWorkspacePath = path.resolve(cwd, "pnpm-workspace.yaml");
2855
+ if (fs.existsSync(pnpmWorkspacePath)) {
2856
+ const content = await fs.readFile(pnpmWorkspacePath, "utf8");
2857
+ const matches = Array.from(content.matchAll(/^\s*-\s*["']?([^"'\n#]+)["']?\s*$/gm));
2858
+ for (const match of matches) patterns.push(match[1].trim());
2859
+ }
2860
+ const packageJsonPath = path.resolve(cwd, "package.json");
2861
+ if (fs.existsSync(packageJsonPath)) try {
2862
+ const packageJson = await fs.readJson(packageJsonPath);
2863
+ const workspaces = Array.isArray(packageJson.workspaces) ? packageJson.workspaces : packageJson.workspaces?.packages;
2864
+ if (Array.isArray(workspaces)) patterns.push(...workspaces.filter((pattern) => !pattern.startsWith("!")));
2865
+ } catch {
2866
+ return Array.from(new Set(patterns));
2867
+ }
2868
+ return Array.from(new Set(patterns));
2869
+ }
2870
+
2871
+ //#endregion
2872
+ //#region src/commands/apply.ts
2873
+ const applyOptionsSchema = z.object({
2874
+ cwd: z.string(),
2875
+ positionalPreset: z.string().optional(),
2876
+ preset: z.string().optional(),
2877
+ only: z.union([z.boolean(), z.string()]).optional(),
2878
+ yes: z.boolean(),
2879
+ silent: z.boolean()
2880
+ });
2881
+ const APPLY_ONLY_VALUES = ["theme", "font"];
2882
+ var ApplyOnlyError = class extends Error {
2883
+ constructor(message) {
2884
+ super(message);
2885
+ this.name = "ApplyOnlyError";
2886
+ }
2887
+ };
2888
+ var ApplyPresetError = class extends Error {
2889
+ constructor(message) {
2890
+ super(message);
2891
+ this.name = "ApplyPresetError";
2892
+ }
2893
+ };
2894
+ var ApplyWorkspaceSyncError = class extends Error {
2895
+ constructor(message) {
2896
+ super(message);
2897
+ this.name = "ApplyWorkspaceSyncError";
2898
+ }
2899
+ };
2900
+ const apply = new Command().name("apply").description("apply a preset to an existing project").argument("[preset]", "the preset to apply").option("--preset <preset>", "preset configuration to apply").option("--only [parts]", "apply only parts of a preset: theme, font").option("-y, --yes", "skip confirmation prompt.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-s, --silent", "mute output.", false).action(async (positionalPreset, opts) => {
2901
+ try {
2902
+ const options = applyOptionsSchema.parse({
2903
+ ...opts,
2904
+ cwd: path.resolve(opts.cwd),
2905
+ positionalPreset
2906
+ });
2907
+ const rawPreset = resolveApplyPreset(options);
2908
+ const explicitOnly = resolveApplyOnly(options.only);
2909
+ validateApplyOnlyPreset({
2910
+ preset: rawPreset,
2911
+ only: explicitOnly
2912
+ });
2913
+ const preflight = await preFlightApply(options);
2914
+ if (preflight.errors["1"]) {
2915
+ logger.break();
2916
+ logger.error(`The ${highlighter.info("apply")} command only works in an existing project.`);
2917
+ logger.error(`Run ${highlighter.info(getInitCommand(rawPreset))} first.`);
2918
+ logger.break();
2919
+ process.exit(1);
2920
+ }
2921
+ if (preflight.errors["3"]) {
2922
+ logger.break();
2923
+ logger.error(`No ${highlighter.info("components.json")} found at ${highlighter.info(options.cwd)}.`);
2924
+ logger.error(`Run ${highlighter.info(getInitCommand(rawPreset))} first.`);
2925
+ logger.break();
2926
+ process.exit(1);
2927
+ }
2928
+ const existingConfig = preflight.config;
2929
+ if (!existingConfig) process.exit(1);
2930
+ if (!rawPreset) {
2931
+ await printPresetBuilderGuidance(options);
2932
+ process.exit(0);
2933
+ }
2934
+ const resolvedPreset = resolveApplyPresetSource(rawPreset);
2935
+ const only = explicitOnly ?? resolveApplyOnly(resolvedPreset.only ?? getPresetUrlOnly(rawPreset));
2936
+ const designConfig = resolveApplyDesignSystemConfig(resolvedPreset, existingConfig);
2937
+ await confirmApply({
2938
+ cwd: options.cwd,
2939
+ only,
2940
+ silent: options.silent,
2941
+ yes: options.yes
2942
+ });
2943
+ await withFileBackups(await getApplyConfigPaths(existingConfig), async () => {
2944
+ if (only) await runPartialApply(options.cwd, designConfig, resolvedPreset, only);
2945
+ else await runExistingProjectInit({
2946
+ cwd: options.cwd,
2947
+ components: void 0,
2948
+ yes: true,
2949
+ reinstall: true,
2950
+ defaults: false,
2951
+ force: false,
2952
+ silent: options.silent,
2953
+ isNewProject: false,
2954
+ srcDir: false,
2955
+ cssVariables: true,
2956
+ template: "astro",
2957
+ preset: resolvedPreset.preset,
2958
+ baseColor: void 0,
2959
+ baseStyle: true,
2960
+ rtl: existingConfig.rtl ?? false,
2961
+ lang: void 0,
2962
+ themeRef: resolvedPreset.themeRef,
2963
+ forwardedOptions: ["--yes", "--force"]
2964
+ }, designConfig);
2965
+ const config = await getConfig(options.cwd);
2966
+ if (!config) throw new Error("Could not read components.json after applying preset.");
2967
+ await syncApplyWorkspaceConfigs(config, { only });
2968
+ });
2969
+ if (!options.silent) {
2970
+ logger.break();
2971
+ logger.log("Preset applied successfully.");
2972
+ logger.break();
2973
+ }
2974
+ } catch (error) {
2975
+ if (error instanceof ApplyOnlyError || error instanceof ApplyPresetError) {
2976
+ for (const line of error.message.split("\n")) logger.error(line);
2977
+ logger.break();
2978
+ process.exit(1);
2979
+ }
2980
+ if (error instanceof FileBackupError) {
2981
+ logger.error(`Could not back up ${highlighter.info("components.json")}. Aborting.`);
2982
+ logger.break();
2983
+ process.exit(1);
2984
+ }
2985
+ if (error instanceof ApplyWorkspaceSyncError) {
2986
+ logger.error(error.message);
2987
+ logger.break();
2988
+ process.exit(1);
2989
+ }
2990
+ logger.break();
2991
+ handleError(error);
2992
+ } finally {
2993
+ clearRegistryContext();
2994
+ }
2995
+ });
2996
+ function resolveApplyPreset(options) {
2997
+ const positionalPreset = options.positionalPreset?.trim();
2998
+ const flagPreset = options.preset?.trim();
2999
+ 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.`);
3000
+ return flagPreset ?? positionalPreset;
3001
+ }
3002
+ function getPresetUrlOnly(preset) {
3003
+ if (!isUrl(preset)) return;
3004
+ return new URL(preset).searchParams.get("only") ?? void 0;
3005
+ }
3006
+ function resolveApplyOnly(value) {
3007
+ if (value === void 0 || value === false) return;
3008
+ if (value === true) throw new ApplyOnlyError([
3009
+ "Missing value for --only.",
3010
+ `Use one or more of: ${APPLY_ONLY_VALUES.join(", ")}.`,
3011
+ "Example: bejamas apply <preset> --only theme,font."
3012
+ ].join("\n"));
3013
+ return parseApplyOnlyParts(value);
3014
+ }
3015
+ function parseApplyOnlyParts(value) {
3016
+ const aliases = {
3017
+ theme: "theme",
3018
+ font: "font",
3019
+ fonts: "font"
3020
+ };
3021
+ const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
3022
+ const invalid = parts.filter((part) => !aliases[part]);
3023
+ if (!parts.length || invalid.length) throw new ApplyOnlyError([
3024
+ `Invalid value for --only: ${value}.`,
3025
+ `Use one or more of: ${APPLY_ONLY_VALUES.join(", ")}.`,
3026
+ "Example: bejamas apply <preset> --only theme,font."
3027
+ ].join("\n"));
3028
+ return Array.from(new Set(parts.map((part) => aliases[part])));
3029
+ }
3030
+ function validateApplyOnlyPreset(options) {
3031
+ if (!options.only || options.preset) return;
3032
+ throw new ApplyOnlyError(["Missing preset for --only.", "Use: bejamas apply <preset> --only theme,font."].join("\n"));
3033
+ }
3034
+ function resolveApplyPresetSource(preset) {
3035
+ if (isPresetCode(preset)) {
3036
+ validatePresetCode(preset);
3037
+ return { preset };
3038
+ }
3039
+ 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.`);
3040
+ const url = new URL(preset);
3041
+ if (!isSupportedPresetUrl(url)) throw new ApplyPresetError(`Invalid preset URL: ${highlighter.info(preset)}. Use a Bejamas create or init URL containing a preset code.`);
3042
+ const code = url.searchParams.get("preset")?.trim();
3043
+ if (!code) throw new ApplyPresetError(`Invalid preset URL: ${highlighter.info(preset)}. Expected a URL containing a preset query parameter.`);
3044
+ validatePresetCode(code);
3045
+ return {
3046
+ preset: code,
3047
+ themeRef: url.searchParams.get("themeRef") ?? void 0,
3048
+ only: url.searchParams.get("only") ?? void 0
3049
+ };
3050
+ }
3051
+ async function preFlightApply(options) {
3052
+ const errors = {};
3053
+ if (!fs.existsSync(options.cwd) || !fs.existsSync(path.resolve(options.cwd, "package.json"))) {
3054
+ errors["1"] = true;
3055
+ return {
3056
+ errors,
3057
+ config: null
3058
+ };
3059
+ }
3060
+ if (!fs.existsSync(path.resolve(options.cwd, "components.json"))) {
3061
+ if (await isMonorepoRoot(options.cwd)) {
3062
+ const targets = await getMonorepoTargets(options.cwd);
3063
+ const applyTargets = [];
3064
+ for (const target of targets) {
3065
+ const projectInfo = await getProjectInfo(path.resolve(options.cwd, target.name));
3066
+ if (target.hasConfig || projectInfo?.isAstro) applyTargets.push(target);
3067
+ }
3068
+ if (applyTargets.length > 0) {
3069
+ formatMonorepoMessage("apply --preset <preset>", applyTargets, {
3070
+ binary: "bejamas",
3071
+ cwdFlag: "-c"
3072
+ });
3073
+ process.exit(1);
3074
+ }
3075
+ }
3076
+ errors["3"] = true;
3077
+ return {
3078
+ errors,
3079
+ config: null
3080
+ };
3081
+ }
3082
+ try {
3083
+ return {
3084
+ errors,
3085
+ config: await getConfig(options.cwd)
3086
+ };
3087
+ } catch {
3088
+ logger.break();
3089
+ logger.error(`An invalid ${highlighter.info("components.json")} file was found at ${highlighter.info(options.cwd)}.\nBefore you can apply a preset, you must create a valid ${highlighter.info("components.json")} file by running the ${highlighter.info("init")} command.`);
3090
+ logger.break();
3091
+ process.exit(1);
3092
+ }
3093
+ }
3094
+ async function printPresetBuilderGuidance(options) {
3095
+ if (options.silent) return;
3096
+ const createUrl = buildUiUrl("/create");
3097
+ logger.break();
3098
+ logger.log(`Build your custom preset on ${highlighter.info(createUrl)}.`);
3099
+ logger.log(`Then run ${highlighter.info("bejamas apply --preset <preset>")} with the preset code or preset URL from ui.bejamas.com.`);
3100
+ logger.break();
3101
+ }
3102
+ async function confirmApply(options) {
3103
+ if (options.yes) return;
3104
+ logger.break();
3105
+ if (!options.only) logger.warn(highlighter.warn("Applying a new preset will overwrite existing UI components, fonts, and CSS variables."));
3106
+ else logger.warn(highlighter.warn("Applying the selected preset parts will update your project configuration and styles."));
3107
+ logger.warn("Commit or stash your changes before continuing so you can easily go back.");
3108
+ if (!options.only) {
3109
+ const reinstallComponents = await getInstalledUiComponents(options.cwd);
3110
+ logger.break();
3111
+ logger.log(" The following components will be re-installed:");
3112
+ if (reinstallComponents.length) for (let index = 0; index < reinstallComponents.length; index += 8) logger.log(` - ${reinstallComponents.slice(index, index + 8).join(", ")}`);
3113
+ else logger.log(" - No installed UI components were detected.");
3114
+ }
3115
+ logger.break();
3116
+ const { proceed } = await prompts({
3117
+ type: "confirm",
3118
+ name: "proceed",
3119
+ message: "Would you like to continue?",
3120
+ initial: false
3121
+ });
3122
+ if (!proceed) {
3123
+ logger.break();
3124
+ process.exit(1);
3125
+ }
3126
+ }
3127
+ async function runPartialApply(cwd, designConfig, preset, only) {
3128
+ if (only.includes("theme")) {
3129
+ const initUrl = buildInitUrl(designConfig, preset.themeRef);
3130
+ const themeVars = preset.themeRef ? await fetchInitThemeVars(initUrl) ?? void 0 : void 0;
3131
+ await applyDesignSystemThemeToProject(cwd, designConfig, { themeVars });
3132
+ await fixAstroImports(cwd, false);
3133
+ }
3134
+ if (only.includes("font")) await applyDesignSystemFontToProject(cwd, designConfig);
3135
+ }
3136
+ function resolveApplyDesignSystemConfig(preset, existingConfig) {
3137
+ return resolveDesignSystemConfig({
3138
+ preset: preset.preset,
3139
+ template: "astro",
3140
+ rtl: existingConfig.rtl ?? false,
3141
+ baseColor: void 0,
3142
+ lang: void 0
3143
+ });
3144
+ }
3145
+ function validatePresetCode(preset) {
3146
+ if (!isPresetCode(preset) || !decodePreset(preset)) throw new ApplyPresetError(`Invalid preset code: ${highlighter.info(preset)}.`);
3147
+ }
3148
+ async function getApplyConfigPaths(config) {
3149
+ const configPaths = /* @__PURE__ */ new Set([path.resolve(config.resolvedPaths.cwd, "components.json")]);
3150
+ const workspaceConfig = await getWorkspaceConfig(config);
3151
+ if (workspaceConfig) for (const linkedConfig of Object.values(workspaceConfig)) configPaths.add(path.resolve(linkedConfig.resolvedPaths.cwd, "components.json"));
3152
+ const existingPaths = [];
3153
+ for (const configPath of configPaths) if (await fs.pathExists(configPath)) existingPaths.push(configPath);
3154
+ return existingPaths.sort();
3155
+ }
3156
+ async function withFileBackups(filePaths, task) {
3157
+ const backedUpPaths = [];
3158
+ const restoreBackupOnExit = () => {
3159
+ for (const filePath of [...backedUpPaths].reverse()) restoreFileBackup(filePath);
3160
+ };
3161
+ try {
3162
+ for (const filePath of filePaths) {
3163
+ if (!createFileBackup(filePath)) throw new FileBackupError(filePath);
3164
+ backedUpPaths.push(filePath);
3165
+ }
3166
+ process.on("exit", restoreBackupOnExit);
3167
+ const result = await task();
3168
+ process.removeListener("exit", restoreBackupOnExit);
3169
+ for (const filePath of backedUpPaths) deleteFileBackup(filePath);
3170
+ return result;
3171
+ } catch (error) {
3172
+ process.removeListener("exit", restoreBackupOnExit);
3173
+ for (const filePath of [...backedUpPaths].reverse()) restoreFileBackup(filePath);
3174
+ throw error;
3175
+ }
3176
+ }
3177
+ async function syncApplyWorkspaceConfigs(config, options) {
3178
+ if (options.only && !options.only.includes("theme")) return;
3179
+ const linkedConfigs = await getApplyWorkspaceConfigs(config);
3180
+ if (!linkedConfigs.length) return;
3181
+ const patch = {
3182
+ style: config.style,
3183
+ tailwind: {
3184
+ baseColor: config.tailwind.baseColor,
3185
+ cssVariables: config.tailwind.cssVariables
3186
+ },
3187
+ ...config.iconLibrary ? { iconLibrary: config.iconLibrary } : {},
3188
+ ...config.rtl !== void 0 ? { rtl: config.rtl } : {},
3189
+ ...config.menuColor ? { menuColor: config.menuColor } : {},
3190
+ ...config.menuAccent ? { menuAccent: config.menuAccent } : {}
3191
+ };
3192
+ try {
3193
+ for (const linkedConfig of linkedConfigs) {
3194
+ const configPath = path.resolve(linkedConfig.resolvedPaths.cwd, "components.json");
3195
+ if (!await fs.pathExists(configPath)) continue;
3196
+ const existingConfig = await fs.readJson(configPath);
3197
+ await fs.writeJson(configPath, {
3198
+ ...existingConfig,
3199
+ ...patch,
3200
+ tailwind: {
3201
+ ...existingConfig.tailwind,
3202
+ ...patch.tailwind
3203
+ }
3204
+ }, { spaces: 2 });
3205
+ }
3206
+ } catch (error) {
3207
+ throw new ApplyWorkspaceSyncError(`Failed to sync linked workspace configs.${error instanceof Error ? ` ${error.message}` : ""}`);
3208
+ }
3209
+ }
3210
+ async function getApplyWorkspaceConfigs(config) {
3211
+ const workspaceConfig = await getWorkspaceConfig(config);
3212
+ if (!workspaceConfig) return [];
3213
+ const linkedConfigs = /* @__PURE__ */ new Map();
3214
+ for (const linkedConfig of Object.values(workspaceConfig)) {
3215
+ if (linkedConfig.resolvedPaths.cwd === config.resolvedPaths.cwd) continue;
3216
+ linkedConfigs.set(linkedConfig.resolvedPaths.cwd, linkedConfig);
3217
+ }
3218
+ return Array.from(linkedConfigs.values()).sort((a, b) => a.resolvedPaths.cwd.localeCompare(b.resolvedPaths.cwd));
3219
+ }
3220
+ function quoteShellArg(value) {
3221
+ return /[^A-Za-z0-9_./:-]/.test(value) ? JSON.stringify(value) : value;
3222
+ }
3223
+ function getInitCommand(preset) {
3224
+ if (!preset) return "bejamas init";
3225
+ return `bejamas init --preset ${quoteShellArg(preset)}`;
3226
+ }
3227
+ function isUrl(value) {
3228
+ try {
3229
+ new URL(value);
3230
+ return true;
3231
+ } catch {
3232
+ return false;
3233
+ }
3234
+ }
3235
+ function isSupportedPresetUrl(url) {
3236
+ const pathname = url.pathname.replace(/\/+$/, "");
3237
+ return pathname === "/create" || pathname === "/init";
3238
+ }
3239
+
3240
+ //#endregion
3241
+ //#region src/commands/info.ts
3242
+ const info = new Command().name("info").description("proxy to shadcn info").option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("--json", "output as JSON.", false).action(async (opts) => {
3243
+ const cwd = path.resolve(opts.cwd ?? process.cwd());
3244
+ const rawArgv = process.argv.slice(2);
3245
+ const args = [
3246
+ "info",
3247
+ "--cwd",
3248
+ cwd
3249
+ ];
3250
+ if (opts.json) args.push("--json");
3251
+ const passthroughArgs = extractPassthroughArgs(rawArgv, "info");
3252
+ if (passthroughArgs.length > 0) args.push(...passthroughArgs);
3253
+ try {
3254
+ await runShadcnCommand({
3255
+ cwd,
3256
+ args
3257
+ });
3258
+ } catch {
3259
+ process.exit(1);
3260
+ }
3261
+ });
3262
+
3263
+ //#endregion
3264
+ //#region src/utils/preset-resolve.ts
3265
+ const PRESET_BASE_COLOR_SET = new Set(PRESET_BASE_COLORS);
3266
+ const PRESET_FONT_SET = new Set(PRESET_FONTS);
3267
+ const PRESET_FONT_HEADING_SET = new Set(PRESET_FONT_HEADINGS);
3268
+ const PRESET_ICON_LIBRARY_SET = new Set(PRESET_ICON_LIBRARIES);
3269
+ const PRESET_MENU_ACCENT_SET = new Set(PRESET_MENU_ACCENTS);
3270
+ const PRESET_MENU_COLOR_SET = new Set(PRESET_MENU_COLORS);
3271
+ const PRESET_STYLE_SET = new Set(PRESET_STYLES);
3272
+ new Set(PRESET_THEMES);
3273
+ const ROOT_FONT_VARIABLES = [
3274
+ "--font-sans",
3275
+ "--font-serif",
3276
+ "--font-mono"
3277
+ ];
3278
+ const ROOT_FONT_VARIABLE_SET = new Set(ROOT_FONT_VARIABLES);
3279
+ const FONT_VARIABLE_SET = /* @__PURE__ */ new Set([...ROOT_FONT_VARIABLES, "--font-heading"]);
3280
+ async function resolveProjectPreset(config) {
3281
+ const style = normalizePresetStyle(config.style);
3282
+ if (!style) return {
3283
+ code: null,
3284
+ fallbacks: [],
3285
+ values: null
3286
+ };
3287
+ const cssState = await readCssState(config.resolvedPaths.tailwindCss);
3288
+ const baseColor = asPresetValue(PRESET_BASE_COLOR_SET, config.tailwind.baseColor) ?? DEFAULT_PRESET_CONFIG.baseColor;
3289
+ const iconLibrary = asPresetValue(PRESET_ICON_LIBRARY_SET, config.iconLibrary) ?? DEFAULT_PRESET_CONFIG.iconLibrary;
3290
+ const menuAccent = asPresetValue(PRESET_MENU_ACCENT_SET, config.menuAccent) ?? DEFAULT_PRESET_CONFIG.menuAccent;
3291
+ const menuColor = asPresetValue(PRESET_MENU_COLOR_SET, config.menuColor) ?? DEFAULT_PRESET_CONFIG.menuColor;
3292
+ const resolvedTheme = matchTheme(cssState, {
3293
+ baseColor,
3294
+ menuAccent,
3295
+ menuColor,
3296
+ style
3297
+ });
3298
+ const theme = resolvedTheme ?? getFallbackTheme(baseColor);
3299
+ const resolvedRadius = matchRadius(cssState.rootVars["--radius"], style);
3300
+ const radius = resolvedRadius ?? DEFAULT_PRESET_CONFIG.radius;
3301
+ const resolvedFont = await resolveBodyFont(config, cssState);
3302
+ const font = resolvedFont ?? DEFAULT_PRESET_CONFIG.font;
3303
+ const resolvedFontHeading = await resolveHeadingFont(config, cssState, font);
3304
+ const values = {
3305
+ style,
3306
+ baseColor,
3307
+ theme,
3308
+ iconLibrary,
3309
+ font,
3310
+ fontHeading: normalizeFontHeading(resolvedFontHeading ?? DEFAULT_PRESET_CONFIG.fontHeading, font),
3311
+ radius,
3312
+ menuAccent,
3313
+ menuColor
3314
+ };
3315
+ const fallbacks = [
3316
+ !asPresetValue(PRESET_BASE_COLOR_SET, config.tailwind.baseColor) && "baseColor",
3317
+ !resolvedTheme && "theme",
3318
+ !asPresetValue(PRESET_ICON_LIBRARY_SET, config.iconLibrary) && "iconLibrary",
3319
+ !resolvedFont && "font",
3320
+ !resolvedFontHeading && "fontHeading",
3321
+ !resolvedRadius && "radius",
3322
+ !asPresetValue(PRESET_MENU_ACCENT_SET, config.menuAccent) && "menuAccent",
3323
+ !asPresetValue(PRESET_MENU_COLOR_SET, config.menuColor) && "menuColor"
3324
+ ].filter(Boolean);
3325
+ return {
3326
+ code: encodePreset(values),
3327
+ fallbacks,
3328
+ values
3329
+ };
3330
+ }
3331
+ async function readCssState(tailwindCssPath) {
3332
+ const fallback = {
3333
+ darkVars: {},
3334
+ imports: [],
3335
+ rootVars: {},
3336
+ source: "",
3337
+ themeVars: {}
3338
+ };
3339
+ if (!tailwindCssPath || !await fs.pathExists(tailwindCssPath)) return fallback;
3340
+ try {
3341
+ const source = await fs.readFile(tailwindCssPath, "utf8");
3342
+ return {
3343
+ darkVars: collectBlockDeclarations(source, ".dark"),
3344
+ imports: collectImports(source),
3345
+ rootVars: collectBlockDeclarations(source, ":root"),
3346
+ source,
3347
+ themeVars: collectAtRuleDeclarations(source, "@theme inline")
3348
+ };
3349
+ } catch {
3350
+ return fallback;
3351
+ }
3352
+ }
3353
+ function collectImports(source) {
3354
+ const imports = [];
3355
+ const pattern = /@import\s+(?:url\((['"]?)(.+?)\1\)|(['"])(.+?)\3)\s*;/g;
3356
+ let match;
3357
+ while (match = pattern.exec(source)) {
3358
+ const value = match[2] ?? match[4];
3359
+ if (value) imports.push(value);
3360
+ }
3361
+ return imports;
3362
+ }
3363
+ function collectBlockDeclarations(source, selector) {
3364
+ const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{([\\s\\S]*?)\\}`, "m");
3365
+ const match = source.match(pattern);
3366
+ return match ? collectDeclarations(match[1]) : {};
3367
+ }
3368
+ function collectAtRuleDeclarations(source, atRule) {
3369
+ const pattern = new RegExp(`${escapeRegExp(atRule)}\\s*\\{([\\s\\S]*?)\\}`, "m");
3370
+ const match = source.match(pattern);
3371
+ return match ? collectDeclarations(match[1]) : {};
3372
+ }
3373
+ function collectDeclarations(source) {
3374
+ const declarations = {};
3375
+ const pattern = /(--[a-z0-9-]+)\s*:\s*([^;]+);/gi;
3376
+ let match;
3377
+ while (match = pattern.exec(source)) declarations[match[1]] = match[2].trim();
3378
+ return declarations;
3379
+ }
3380
+ function matchTheme(state, options) {
3381
+ const lightPrimary = state.rootVars["--primary"];
3382
+ if (!lightPrimary) return null;
3383
+ for (const theme of PRESET_THEMES) {
3384
+ let registryTheme;
3385
+ try {
3386
+ registryTheme = buildRegistryTheme({
3387
+ ...DEFAULT_DESIGN_SYSTEM_FOR_MATCHING,
3388
+ ...options,
3389
+ theme
3390
+ }).cssVars;
3391
+ } catch {
3392
+ continue;
3393
+ }
3394
+ const expectedLight = registryTheme.light?.primary;
3395
+ const expectedDark = registryTheme.dark?.primary;
3396
+ if (!valuesMatch(lightPrimary, expectedLight)) continue;
3397
+ const darkPrimary = state.darkVars["--primary"];
3398
+ if (darkPrimary && expectedDark && !valuesMatch(darkPrimary, expectedDark)) continue;
3399
+ return theme;
3400
+ }
3401
+ return null;
3402
+ }
3403
+ function getFallbackTheme(baseColor) {
3404
+ return baseColor === DEFAULT_PRESET_CONFIG.baseColor ? DEFAULT_PRESET_CONFIG.theme : baseColor;
3405
+ }
3406
+ function matchRadius(value, style) {
3407
+ if (!value) return null;
3408
+ const normalizedValue = normalizeCssValue(value);
3409
+ const styleDefaultRadius = getStyleDefaultRadius(style);
3410
+ if (styleDefaultRadius && normalizedValue === normalizeCssValue(getRadiusValue(styleDefaultRadius))) return "default";
3411
+ for (const radius of PRESET_RADII) if (normalizedValue === normalizeCssValue(getRadiusValue(radius))) return radius === "default" ? "medium" : radius;
3412
+ return null;
3413
+ }
3414
+ async function resolveBodyFont(config, state) {
3415
+ const activeVariable = resolveActiveBodyFontVariable(state);
3416
+ const managedFonts = await readManagedAstroFontsFromProject(config.resolvedPaths.cwd);
3417
+ const matchedManagedFont = matchManagedFont(activeVariable ? managedFonts.find((font) => font.cssVariable === activeVariable) : managedFonts.find((font) => ROOT_FONT_VARIABLE_SET.has(font.cssVariable)), "body");
3418
+ if (matchedManagedFont) return matchedManagedFont;
3419
+ const matchedImport = activeVariable ? matchFontByImports(state.imports, activeVariable) : null;
3420
+ if (matchedImport) return matchedImport;
3421
+ for (const variable of ROOT_FONT_VARIABLES) {
3422
+ const matched = matchFontFromVariable(state, variable);
3423
+ if (matched) return matched;
3424
+ }
3425
+ return null;
3426
+ }
3427
+ async function resolveHeadingFont(config, state, bodyFont) {
3428
+ const managedHeadingFont = matchManagedFont((await readManagedAstroFontsFromProject(config.resolvedPaths.cwd)).find((font) => font.cssVariable === "--font-heading"), "heading");
3429
+ if (managedHeadingFont) return managedHeadingFont === bodyFont ? "inherit" : managedHeadingFont;
3430
+ const resolved = resolveFontValue(state, "--font-heading");
3431
+ const matched = resolved ? parseFontFromFamily(resolved) : null;
3432
+ if (matched) return matched === bodyFont ? "inherit" : matched;
3433
+ const value = getCssVariableValue(state, "--font-heading");
3434
+ const reference = value ? getVarReference(value) : null;
3435
+ if (reference && ROOT_FONT_VARIABLE_SET.has(reference)) {
3436
+ const matchedRootFont = matchFontFromVariable(state, reference);
3437
+ if (!matchedRootFont || matchedRootFont === bodyFont) return "inherit";
3438
+ return matchedRootFont;
3439
+ }
3440
+ return "inherit";
3441
+ }
3442
+ function normalizePresetStyle(style) {
3443
+ if (!style) return null;
3444
+ const normalized = style.replace(/^(bejamas|base|radix)-/, "");
3445
+ if (!PRESET_STYLE_SET.has(normalized)) return null;
3446
+ return normalized;
3447
+ }
3448
+ function normalizeFontHeading(fontHeading, bodyFont) {
3449
+ const normalized = fontHeading === bodyFont ? "inherit" : fontHeading;
3450
+ return PRESET_FONT_HEADING_SET.has(normalized) ? normalized : DEFAULT_PRESET_CONFIG.fontHeading;
3451
+ }
3452
+ function resolveActiveBodyFontVariable(state) {
3453
+ const htmlOrBodyFont = state.source.match(/(?:html|body)\s*\{[\s\S]*?@apply[^;]*\bfont-(sans|serif|mono)\b[\s\S]*?\}/m)?.[1];
3454
+ if (htmlOrBodyFont) return `--font-${htmlOrBodyFont}`;
3455
+ for (const variable of ROOT_FONT_VARIABLES) if (state.themeVars[variable]) return variable;
3456
+ return null;
3457
+ }
3458
+ function matchManagedFont(managedFont, slot) {
3459
+ if (!managedFont) return null;
3460
+ for (const font of PRESET_FONTS) {
3461
+ const expected = slot === "heading" ? toManagedAstroFont(`font-heading-${font}`) : toManagedAstroFont(font);
3462
+ if (expected && expected.cssVariable === managedFont.cssVariable && normalizeFontName(expected.name) === normalizeFontName(managedFont.name)) return font;
3463
+ }
3464
+ return null;
3465
+ }
3466
+ function matchFontByImports(imports, variable) {
3467
+ const matches = imports.flatMap((input) => {
3468
+ const font = parseFontFromDependency(input);
3469
+ const fontValue = font ? getFontValue(font) : null;
3470
+ return font && fontValue?.font.variable === variable ? [font] : [];
3471
+ });
3472
+ return matches.length === 1 ? matches[0] : null;
3473
+ }
3474
+ function matchFontFromVariable(state, variable) {
3475
+ const resolved = resolveFontValue(state, variable);
3476
+ return resolved ? parseFontFromFamily(resolved) : null;
3477
+ }
3478
+ function resolveFontValue(state, variable, seen = /* @__PURE__ */ new Set()) {
3479
+ if (seen.has(variable)) return null;
3480
+ seen.add(variable);
3481
+ const value = getCssVariableValue(state, variable);
3482
+ if (!value) return null;
3483
+ const reference = getVarReference(value);
3484
+ if (!reference) return value;
3485
+ if (FONT_VARIABLE_SET.has(reference)) return resolveFontValue(state, reference, seen);
3486
+ return null;
3487
+ }
3488
+ function getCssVariableValue(state, variable) {
3489
+ const themeValue = state.themeVars[variable];
3490
+ if (themeValue && getVarReference(themeValue) !== variable) return themeValue;
3491
+ return state.rootVars[variable] ?? themeValue ?? null;
3492
+ }
3493
+ function getVarReference(value) {
3494
+ return normalizeCssValue(value).match(/^var\((--[a-z0-9-]+)\)$/)?.[1] ?? null;
3495
+ }
3496
+ function parseFontFromDependency(value) {
3497
+ if (!value) return null;
3498
+ const prefix = "@fontsource-variable/";
3499
+ const normalized = normalizeCssValue(value);
3500
+ return normalized.startsWith(prefix) ? toPresetFont(normalized.slice(21)) : null;
3501
+ }
3502
+ function parseFontFromFamily(value) {
3503
+ if (!value) return null;
3504
+ const primaryFamily = normalizeFontName(value.split(",")[0] ?? "");
3505
+ for (const font of PRESET_FONTS) {
3506
+ const bodyFont = getFontValue(font);
3507
+ const headingFont = getHeadingFontValue(font);
3508
+ if ([
3509
+ font,
3510
+ bodyFont?.title,
3511
+ bodyFont?.font.family.split(",")[0],
3512
+ bodyFont?.font.import,
3513
+ headingFont?.title?.replace(/\s+\(Heading\)$/, ""),
3514
+ headingFont?.font.family.split(",")[0],
3515
+ headingFont?.font.import
3516
+ ].some((candidate) => candidate && normalizeFontName(candidate) === primaryFamily)) return font;
3517
+ }
3518
+ return null;
3519
+ }
3520
+ function toPresetFont(value) {
3521
+ const normalized = normalizeCssValue(value);
3522
+ return PRESET_FONT_SET.has(normalized) ? normalized : null;
3523
+ }
3524
+ function normalizeFontName(value) {
3525
+ return value.trim().replace(/^['"]|['"]$/g, "").replace(/\s+variable$/i, "").replace(/_/g, "-").replace(/\s+/g, "-").toLowerCase();
3526
+ }
3527
+ function normalizeCssValue(value) {
3528
+ if (!value) return "";
3529
+ return value.trim().replace(/-?\d*\.\d+/g, (match) => String(Number(match))).replace(/\s+/g, " ").replace(/\s*,\s*/g, ",").replace(/\s*\/\s*/g, "/").replace(/"/g, "'").toLowerCase();
3530
+ }
3531
+ function valuesMatch(actual, expected) {
3532
+ return normalizeCssValue(actual) === normalizeCssValue(expected);
3533
+ }
3534
+ function asPresetValue(set, value) {
3535
+ return value && set.has(value) ? value : null;
3536
+ }
3537
+ function escapeRegExp(value) {
3538
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3539
+ }
3540
+ const DEFAULT_DESIGN_SYSTEM_FOR_MATCHING = {
3541
+ ...DEFAULT_PRESET_CONFIG,
3542
+ template: "astro",
3543
+ rtl: false,
3544
+ rtlLanguage: "ar"
3545
+ };
3546
+
3547
+ //#endregion
3548
+ //#region src/commands/preset.ts
3549
+ const PRESET_CHART_COLOR_SET = new Set(PRESET_CHART_COLORS);
3550
+ function getPresetUrl(code) {
3551
+ return buildUiUrl(`/create?preset=${code}`);
3552
+ }
3553
+ function decodePresetCode(code) {
3554
+ const decoded = decodePreset(code);
3555
+ if (!decoded) throw new Error(`Invalid preset code: ${code}`);
3556
+ const derived = [];
3557
+ const chartColor = resolveDecodedChartColor(decoded);
3558
+ if (!decoded.chartColor && chartColor) derived.push("chartColor");
3559
+ return {
3560
+ code,
3561
+ derived,
3562
+ url: getPresetUrl(code),
3563
+ values: {
3564
+ ...decoded,
3565
+ ...chartColor ? { chartColor } : {}
3566
+ },
3567
+ version: code[0]
3568
+ };
3569
+ }
3570
+ function printPresetDecode(result) {
3571
+ printPresetInfo({
3572
+ code: result.code,
3573
+ fallbacks: result.derived,
3574
+ values: result.values
3575
+ }, { fallbackNote: " * Compatibility value for older preset versions." });
3576
+ }
3577
+ function printPresetInfo(preset, options = {}) {
3578
+ logger.log(highlighter.info("Preset"));
3579
+ if (!preset.code || !preset.values) {
3580
+ printEntries({ code: "-" });
3581
+ return;
3582
+ }
3583
+ const fallbacks = preset.fallbacks ?? [];
3584
+ const formatPresetValue = (key, value) => {
3585
+ const suffix = fallbacks.includes(key) ? "*" : "";
3586
+ return `${value ?? "-"}${suffix}`;
3587
+ };
3588
+ printEntries({
3589
+ code: preset.code,
3590
+ version: preset.code[0],
3591
+ style: preset.values.style,
3592
+ baseColor: formatPresetValue("baseColor", preset.values.baseColor),
3593
+ theme: formatPresetValue("theme", preset.values.theme),
3594
+ chartColor: formatPresetValue("chartColor", preset.values.chartColor),
3595
+ iconLibrary: formatPresetValue("iconLibrary", preset.values.iconLibrary),
3596
+ font: formatPresetValue("font", preset.values.font),
3597
+ fontHeading: formatPresetValue("fontHeading", preset.values.fontHeading),
3598
+ radius: formatPresetValue("radius", preset.values.radius),
3599
+ menuAccent: formatPresetValue("menuAccent", preset.values.menuAccent),
3600
+ menuColor: formatPresetValue("menuColor", preset.values.menuColor),
3601
+ url: getPresetUrl(preset.code)
3602
+ });
3603
+ if (fallbacks.length > 0) {
3604
+ logger.log("");
3605
+ logger.log(options.fallbackNote ?? " * Uses preset defaults for values that could not be inferred from the project.");
3606
+ }
3607
+ }
3608
+ const decode = new Command().name("decode").description("decode a preset code").argument("<code>", "the preset code to decode").option("--json", "output as JSON.", false).action((code, opts) => {
3609
+ try {
3610
+ const result = decodePresetCode(code);
3611
+ if (opts.json) {
3612
+ console.log(JSON.stringify(result, null, 2));
3613
+ return;
3614
+ }
3615
+ printPresetDecode(result);
3616
+ } catch (error) {
3617
+ handlePresetError(error);
3618
+ }
3619
+ });
3620
+ const url = new Command().name("url").description("get the create URL for a preset code").argument("<code>", "the preset code").action((code) => {
3621
+ try {
3622
+ logger.log(decodePresetCode(code).url);
3623
+ } catch (error) {
3624
+ handlePresetError(error);
3625
+ }
3626
+ });
3627
+ const openCommand = new Command().name("open").description("open a preset code in the browser").argument("<code>", "the preset code").action(async (code) => {
3628
+ let presetUrl;
3629
+ try {
3630
+ presetUrl = decodePresetCode(code).url;
3631
+ } catch (error) {
3632
+ handlePresetError(error);
3633
+ return;
3634
+ }
3635
+ logger.break();
3636
+ logger.log(` Opening ${presetUrl} in your browser.`);
3637
+ logger.break();
3638
+ try {
3639
+ const { default: open } = await import("open");
3640
+ await open(presetUrl);
3641
+ } catch (error) {
3642
+ const message = error instanceof Error ? error.message : String(error);
3643
+ handlePresetError(/* @__PURE__ */ new Error(`Failed to open preset URL: ${message}`));
3644
+ }
3645
+ });
3646
+ const resolve$1 = new Command().name("resolve").alias("info").description("resolve a preset from your project").option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("--json", "output as JSON.", false).action(async (opts) => {
3647
+ try {
3648
+ const cwd = path.resolve(opts.cwd);
3649
+ if (!existsSync(path.resolve(cwd, "components.json")) && await isMonorepoRoot(cwd)) {
3650
+ const targets = await getMonorepoTargets(cwd);
3651
+ if (targets.length > 0) {
3652
+ if (opts.json) console.log(JSON.stringify({
3653
+ error: "monorepo_root",
3654
+ message: "You are running preset resolve from a monorepo root. Use the -c flag to specify a workspace.",
3655
+ targets: targets.map((target) => target.name)
3656
+ }, null, 2));
3657
+ else formatMonorepoMessage("preset resolve", targets, {
3658
+ binary: "bejamas",
3659
+ cwdFlag: "-c"
3660
+ });
3661
+ process.exit(1);
3662
+ }
3663
+ }
3664
+ const config = await getConfig(cwd);
3665
+ if (!config) {
3666
+ if (opts.json) {
3667
+ console.log(JSON.stringify(null, null, 2));
3668
+ return;
3669
+ }
3670
+ logger.log("No components.json found.");
3671
+ return;
3672
+ }
3673
+ const resolvedPreset = await resolveProjectPreset(config);
3674
+ if (opts.json) {
3675
+ console.log(JSON.stringify(resolvedPreset.code ? resolvedPreset : null, null, 2));
3676
+ return;
3677
+ }
3678
+ printPresetInfo(resolvedPreset);
3679
+ } catch (error) {
3680
+ handleError(error);
3681
+ }
3682
+ });
3683
+ const preset = new Command().name("preset").description("manage presets").addCommand(decode).addCommand(resolve$1).addCommand(url).addCommand(openCommand).action(() => {
3684
+ preset.outputHelp();
3685
+ });
3686
+ function resolveDecodedChartColor(decoded) {
3687
+ if (decoded.chartColor) return decoded.chartColor;
3688
+ const legacyChartColor = V1_CHART_COLOR_MAP[decoded.theme];
3689
+ if (legacyChartColor) return legacyChartColor;
3690
+ if (PRESET_CHART_COLOR_SET.has(decoded.theme)) return decoded.theme;
3691
+ }
3692
+ function printEntries(entries) {
3693
+ const maxKeyLength = Math.max(...Object.keys(entries).map((key) => key.length));
3694
+ for (const [key, value] of Object.entries(entries)) logger.log(` ${key.padEnd(maxKeyLength + 2)}${value}`);
3695
+ }
3696
+ function handlePresetError(error) {
3697
+ if (error instanceof Error) logger.error(error.message);
3698
+ process.exit(1);
3699
+ }
3700
+
3701
+ //#endregion
3702
+ //#region src/index.ts
3703
+ const pkg = createRequire(import.meta.url)("../package.json");
3704
+ 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");
3705
+ program.addCommand(init);
3706
+ program.addCommand(add);
3707
+ program.addCommand(apply);
3708
+ program.addCommand(preset);
3709
+ program.addCommand(info);
3710
+ program.addCommand(docs);
3711
+ program.addCommand(docsBuild);
3712
+ program.addCommand(docsCheck);
3713
+ program.parse(process.argv);
3714
+
3715
+ //#endregion
3716
+ export { program as default };
3717
+ //# sourceMappingURL=index.js.map