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