bejamas 0.0.0-canary.0cf9645
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 +43 -0
- package/dist/generate-mdx-Bq9erbjr.js +654 -0
- package/dist/generate-mdx-Bq9erbjr.js.map +1 -0
- package/dist/index.js +783 -0
- package/dist/index.js.map +1 -0
- package/dist/spinner-9iMQF079.js +46 -0
- package/dist/spinner-9iMQF079.js.map +1 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { highlighter, logger, spinner } from "./spinner-9iMQF079.js";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import fsExtra from "fs-extra";
|
|
7
|
+
import os from "os";
|
|
8
|
+
import dotenv from "dotenv";
|
|
9
|
+
import { detect } from "@antfu/ni";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { execa } from "execa";
|
|
12
|
+
import prompts from "prompts";
|
|
13
|
+
import { configSchema, rawConfigSchema } from "shadcn/schema";
|
|
14
|
+
import fg from "fast-glob";
|
|
15
|
+
import { createMatchPath, loadConfig } from "tsconfig-paths";
|
|
16
|
+
import { cosmiconfig } from "cosmiconfig";
|
|
17
|
+
import { dirname as dirname$1, isAbsolute, relative as relative$1, resolve } from "node:path";
|
|
18
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
//#region src/utils/errors.ts
|
|
22
|
+
const MISSING_DIR_OR_EMPTY_PROJECT = "1";
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/preflights/preflight-init.ts
|
|
26
|
+
async function preFlightInit(options) {
|
|
27
|
+
const errors = {};
|
|
28
|
+
if (!fsExtra.existsSync(options.cwd) || !fsExtra.existsSync(path.resolve(options.cwd, "package.json"))) {
|
|
29
|
+
errors[MISSING_DIR_OR_EMPTY_PROJECT] = true;
|
|
30
|
+
return {
|
|
31
|
+
errors,
|
|
32
|
+
projectInfo: null
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
errors,
|
|
37
|
+
projectInfo: null
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/registry/constants.ts
|
|
43
|
+
const REGISTRY_URL = process.env.REGISTRY_URL ?? "http://localhost:4321/r";
|
|
44
|
+
const BASE_COLORS = [
|
|
45
|
+
{
|
|
46
|
+
name: "neutral",
|
|
47
|
+
label: "Neutral"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "gray",
|
|
51
|
+
label: "Gray"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "zinc",
|
|
55
|
+
label: "Zinc"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "stone",
|
|
59
|
+
label: "Stone"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "slate",
|
|
63
|
+
label: "Slate"
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
const BUILTIN_REGISTRIES = { "@bejamas": `${REGISTRY_URL}/{name}.json` };
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/registry/context.ts
|
|
70
|
+
let context = { headers: {} };
|
|
71
|
+
function clearRegistryContext() {
|
|
72
|
+
context.headers = {};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/utils/get-package-manager.ts
|
|
77
|
+
async function getPackageManager(targetDir, { withFallback } = { withFallback: false }) {
|
|
78
|
+
const packageManager = await detect({
|
|
79
|
+
programmatic: true,
|
|
80
|
+
cwd: targetDir
|
|
81
|
+
});
|
|
82
|
+
if (packageManager === "yarn@berry") return "yarn";
|
|
83
|
+
if (packageManager === "pnpm@6") return "pnpm";
|
|
84
|
+
if (packageManager === "bun") return "bun";
|
|
85
|
+
if (packageManager === "deno") return "deno";
|
|
86
|
+
if (!withFallback) return packageManager ?? "npm";
|
|
87
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
88
|
+
if (userAgent.startsWith("yarn")) return "yarn";
|
|
89
|
+
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
90
|
+
if (userAgent.startsWith("bun")) return "bun";
|
|
91
|
+
return "npm";
|
|
92
|
+
}
|
|
93
|
+
async function getPackageRunner(cwd) {
|
|
94
|
+
const packageManager = await getPackageManager(cwd);
|
|
95
|
+
if (packageManager === "pnpm") return "pnpm dlx";
|
|
96
|
+
if (packageManager === "bun") return "bunx";
|
|
97
|
+
return "npx";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/registry/errors.ts
|
|
102
|
+
const RegistryErrorCode = {
|
|
103
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
104
|
+
NOT_FOUND: "NOT_FOUND",
|
|
105
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
106
|
+
FORBIDDEN: "FORBIDDEN",
|
|
107
|
+
FETCH_ERROR: "FETCH_ERROR",
|
|
108
|
+
NOT_CONFIGURED: "NOT_CONFIGURED",
|
|
109
|
+
INVALID_CONFIG: "INVALID_CONFIG",
|
|
110
|
+
MISSING_ENV_VARS: "MISSING_ENV_VARS",
|
|
111
|
+
LOCAL_FILE_ERROR: "LOCAL_FILE_ERROR",
|
|
112
|
+
PARSE_ERROR: "PARSE_ERROR",
|
|
113
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
114
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR"
|
|
115
|
+
};
|
|
116
|
+
var RegistryError = class extends Error {
|
|
117
|
+
constructor(message, options = {}) {
|
|
118
|
+
super(message);
|
|
119
|
+
this.name = "RegistryError";
|
|
120
|
+
this.code = options.code || RegistryErrorCode.UNKNOWN_ERROR;
|
|
121
|
+
this.statusCode = options.statusCode;
|
|
122
|
+
this.cause = options.cause;
|
|
123
|
+
this.context = options.context;
|
|
124
|
+
this.suggestion = options.suggestion;
|
|
125
|
+
this.timestamp = /* @__PURE__ */ new Date();
|
|
126
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
127
|
+
}
|
|
128
|
+
toJSON() {
|
|
129
|
+
return {
|
|
130
|
+
name: this.name,
|
|
131
|
+
message: this.message,
|
|
132
|
+
code: this.code,
|
|
133
|
+
statusCode: this.statusCode,
|
|
134
|
+
context: this.context,
|
|
135
|
+
suggestion: this.suggestion,
|
|
136
|
+
timestamp: this.timestamp,
|
|
137
|
+
stack: this.stack
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/utils/handle-error.ts
|
|
144
|
+
function handleError(error) {
|
|
145
|
+
logger.break();
|
|
146
|
+
logger.error(`Something went wrong. Please check the error below for more details.`);
|
|
147
|
+
logger.error(`If the problem persists, please open an issue on GitHub: https://github.com/bejamas/ui/issues`);
|
|
148
|
+
logger.error("");
|
|
149
|
+
if (typeof error === "string") {
|
|
150
|
+
logger.error(error);
|
|
151
|
+
logger.break();
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
if (error instanceof RegistryError) {
|
|
155
|
+
if (error.message) {
|
|
156
|
+
logger.error(error.cause ? "Error:" : "Message:");
|
|
157
|
+
logger.error(error.message);
|
|
158
|
+
}
|
|
159
|
+
if (error.cause) {
|
|
160
|
+
logger.error("\nMessage:");
|
|
161
|
+
logger.error(error.cause);
|
|
162
|
+
}
|
|
163
|
+
if (error.suggestion) {
|
|
164
|
+
logger.error("\nSuggestion:");
|
|
165
|
+
logger.error(error.suggestion);
|
|
166
|
+
}
|
|
167
|
+
logger.break();
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
if (error instanceof z.ZodError) {
|
|
171
|
+
logger.error("Validation failed:");
|
|
172
|
+
for (const [key, value] of Object.entries(error.flatten().fieldErrors)) logger.error(`- ${highlighter.info(key)}: ${value}`);
|
|
173
|
+
logger.break();
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
if (error instanceof Error) {
|
|
177
|
+
logger.error(error.message);
|
|
178
|
+
logger.break();
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
logger.break();
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region src/utils/create-project.ts
|
|
187
|
+
const TEMPLATES = {
|
|
188
|
+
astro: "astro",
|
|
189
|
+
"astro-monorepo": "astro-monorepo",
|
|
190
|
+
"astro-with-component-docs-monorepo": "astro-with-component-docs-monorepo"
|
|
191
|
+
};
|
|
192
|
+
const MONOREPO_TEMPLATE_URL = "https://codeload.github.com/bejamas/ui/tar.gz/main";
|
|
193
|
+
async function createProject(options) {
|
|
194
|
+
options = {
|
|
195
|
+
srcDir: false,
|
|
196
|
+
...options
|
|
197
|
+
};
|
|
198
|
+
let template = options.template && TEMPLATES[options.template] ? options.template : "astro";
|
|
199
|
+
let projectName = "my-app";
|
|
200
|
+
const isRemoteComponent = options.components?.length === 1 && !!options.components[0].match(/\/chat\/b\//);
|
|
201
|
+
if (!options.force) {
|
|
202
|
+
const { type, name } = await prompts([{
|
|
203
|
+
type: options.template || isRemoteComponent ? null : "select",
|
|
204
|
+
name: "type",
|
|
205
|
+
message: `The path ${highlighter.info(options.cwd)} does not contain a package.json file.\n Would you like to start a new project?`,
|
|
206
|
+
choices: [
|
|
207
|
+
{
|
|
208
|
+
title: "Astro",
|
|
209
|
+
value: "astro"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
title: "Astro (Monorepo)",
|
|
213
|
+
value: "astro-monorepo"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
title: "Astro with Component Docs (Monorepo)",
|
|
217
|
+
value: "astro-with-component-docs-monorepo"
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
initial: 0
|
|
221
|
+
}, {
|
|
222
|
+
type: "text",
|
|
223
|
+
name: "name",
|
|
224
|
+
message: "What is your project named?",
|
|
225
|
+
initial: (_prev, values) => {
|
|
226
|
+
return (options.template && TEMPLATES[options.template] && options.template || values.type || template)?.endsWith("monorepo") ? "my-monorepo" : "my-app";
|
|
227
|
+
},
|
|
228
|
+
format: (value) => value.trim(),
|
|
229
|
+
validate: (value) => value.length > 128 ? `Name should be less than 128 characters.` : true
|
|
230
|
+
}]);
|
|
231
|
+
template = type ?? template;
|
|
232
|
+
projectName = name;
|
|
233
|
+
}
|
|
234
|
+
const packageManager = await getPackageManager(options.cwd, { withFallback: true });
|
|
235
|
+
const projectPath = `${options.cwd}/${projectName}`;
|
|
236
|
+
try {
|
|
237
|
+
await fsExtra.access(options.cwd, fsExtra.constants.W_OK);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
logger.break();
|
|
240
|
+
logger.error(`The path ${highlighter.info(options.cwd)} is not writable.`);
|
|
241
|
+
logger.error(`It is likely you do not have write permissions for this folder or the path ${highlighter.info(options.cwd)} does not exist.`);
|
|
242
|
+
logger.break();
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
if (fsExtra.existsSync(path.resolve(options.cwd, projectName, "package.json"))) {
|
|
246
|
+
logger.break();
|
|
247
|
+
logger.error(`A project with the name ${highlighter.info(projectName)} already exists.`);
|
|
248
|
+
logger.error(`Please choose a different name and try again.`);
|
|
249
|
+
logger.break();
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
await createProjectFromTemplate(projectPath, {
|
|
253
|
+
templateKey: template,
|
|
254
|
+
packageManager,
|
|
255
|
+
cwd: options.cwd
|
|
256
|
+
});
|
|
257
|
+
return {
|
|
258
|
+
projectPath,
|
|
259
|
+
projectName,
|
|
260
|
+
template
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
async function createProjectFromTemplate(projectPath, options) {
|
|
264
|
+
const createSpinner = spinner(`Creating a new project from template. This may take a few minutes.`).start();
|
|
265
|
+
const TEMPLATE_TAR_SUBPATH = {
|
|
266
|
+
astro: "ui-main/templates/astro",
|
|
267
|
+
"astro-monorepo": "ui-main/templates/monorepo-astro",
|
|
268
|
+
"astro-with-component-docs-monorepo": "ui-main/templates/monorepo-astro-with-docs"
|
|
269
|
+
};
|
|
270
|
+
try {
|
|
271
|
+
dotenv.config({ quiet: true });
|
|
272
|
+
const templatePath = path.join(os.tmpdir(), `bejamas-template-${Date.now()}`);
|
|
273
|
+
await fsExtra.ensureDir(templatePath);
|
|
274
|
+
const authToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
275
|
+
const usedAuth = Boolean(authToken);
|
|
276
|
+
const headers = { "User-Agent": "bejamas-cli" };
|
|
277
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
278
|
+
const response = await fetch(MONOREPO_TEMPLATE_URL, { headers });
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
if (response.status === 401 || response.status === 403 || !usedAuth && response.status === 404) throw new Error("Unauthorized to access private template. Set GITHUB_TOKEN or GH_TOKEN (in .env or env) with repo access and try again.");
|
|
281
|
+
if (response.status === 404) throw new Error("Failed to download template: not found.");
|
|
282
|
+
throw new Error(`Failed to download template: ${response.status} ${response.statusText}`);
|
|
283
|
+
}
|
|
284
|
+
const tarPath = path.resolve(templatePath, "template.tar.gz");
|
|
285
|
+
await fsExtra.writeFile(tarPath, Buffer.from(await response.arrayBuffer()));
|
|
286
|
+
const tarSubpath = TEMPLATE_TAR_SUBPATH[options.templateKey];
|
|
287
|
+
const leafName = tarSubpath.split("/").pop();
|
|
288
|
+
await execa("tar", [
|
|
289
|
+
"-xzf",
|
|
290
|
+
tarPath,
|
|
291
|
+
"-C",
|
|
292
|
+
templatePath,
|
|
293
|
+
"--strip-components=2",
|
|
294
|
+
tarSubpath
|
|
295
|
+
]);
|
|
296
|
+
const extractedPath = path.resolve(templatePath, leafName);
|
|
297
|
+
await fsExtra.move(extractedPath, projectPath);
|
|
298
|
+
await fsExtra.remove(templatePath);
|
|
299
|
+
await execa(options.packageManager, ["install"], { cwd: projectPath });
|
|
300
|
+
try {
|
|
301
|
+
const { stdout } = await execa("git", ["rev-parse", "--is-inside-work-tree"], { cwd: projectPath });
|
|
302
|
+
if (!(stdout.trim() === "true")) {
|
|
303
|
+
await execa("git", ["init"], { cwd: projectPath });
|
|
304
|
+
await execa("git", ["add", "-A"], { cwd: projectPath });
|
|
305
|
+
await execa("git", [
|
|
306
|
+
"commit",
|
|
307
|
+
"-m",
|
|
308
|
+
"Initial commit"
|
|
309
|
+
], { cwd: projectPath });
|
|
310
|
+
}
|
|
311
|
+
} catch (_) {}
|
|
312
|
+
createSpinner?.succeed("Creating a new project from template.");
|
|
313
|
+
} catch (error) {
|
|
314
|
+
createSpinner?.fail("Something went wrong creating a new project from template.");
|
|
315
|
+
handleError(error);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region src/utils/get-package-info.ts
|
|
321
|
+
function getPackageInfo(cwd = "", shouldThrow = true) {
|
|
322
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
323
|
+
return fsExtra.readJSONSync(packageJsonPath, { throws: shouldThrow });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/utils/get-project-info.ts
|
|
328
|
+
const PROJECT_SHARED_IGNORE = [
|
|
329
|
+
"**/node_modules/**",
|
|
330
|
+
".astro",
|
|
331
|
+
"public",
|
|
332
|
+
"dist",
|
|
333
|
+
"build"
|
|
334
|
+
];
|
|
335
|
+
const TS_CONFIG_SCHEMA = z.object({ compilerOptions: z.object({ paths: z.record(z.string().or(z.array(z.string()))) }) });
|
|
336
|
+
async function getProjectInfo(cwd) {
|
|
337
|
+
const [configFiles, tailwindConfigFile, tailwindCssFile, tailwindVersion, aliasPrefix, packageJson] = await Promise.all([
|
|
338
|
+
fg.glob("**/{next,vite,astro,app}.config.*|gatsby-config.*|composer.json|react-router.config.*", {
|
|
339
|
+
cwd,
|
|
340
|
+
deep: 3,
|
|
341
|
+
ignore: PROJECT_SHARED_IGNORE
|
|
342
|
+
}),
|
|
343
|
+
getTailwindConfigFile(cwd),
|
|
344
|
+
getTailwindCssFile(cwd),
|
|
345
|
+
getTailwindVersion(cwd),
|
|
346
|
+
getTsConfigAliasPrefix(cwd),
|
|
347
|
+
getPackageInfo(cwd, false)
|
|
348
|
+
]);
|
|
349
|
+
const type = {
|
|
350
|
+
isAstro: false,
|
|
351
|
+
tailwindConfigFile,
|
|
352
|
+
tailwindCssFile,
|
|
353
|
+
tailwindVersion,
|
|
354
|
+
aliasPrefix
|
|
355
|
+
};
|
|
356
|
+
if (configFiles.find((file) => file.startsWith("astro.config."))?.length) {
|
|
357
|
+
type.isAstro = true;
|
|
358
|
+
return type;
|
|
359
|
+
}
|
|
360
|
+
return type;
|
|
361
|
+
}
|
|
362
|
+
async function getTailwindVersion(cwd) {
|
|
363
|
+
const [packageInfo, config] = await Promise.all([getPackageInfo(cwd, false), getConfig(cwd)]);
|
|
364
|
+
if (config?.tailwind?.config === "") return "v4";
|
|
365
|
+
if (!packageInfo?.dependencies?.tailwindcss && !packageInfo?.devDependencies?.tailwindcss) return null;
|
|
366
|
+
if (/^(?:\^|~)?3(?:\.\d+)*(?:-.*)?$/.test(packageInfo?.dependencies?.tailwindcss || packageInfo?.devDependencies?.tailwindcss || "")) return "v3";
|
|
367
|
+
return "v4";
|
|
368
|
+
}
|
|
369
|
+
async function getTailwindCssFile(cwd) {
|
|
370
|
+
const [files, tailwindVersion] = await Promise.all([fg.glob(["**/*.css", "**/*.scss"], {
|
|
371
|
+
cwd,
|
|
372
|
+
deep: 5,
|
|
373
|
+
ignore: PROJECT_SHARED_IGNORE
|
|
374
|
+
}), getTailwindVersion(cwd)]);
|
|
375
|
+
if (!files.length) return null;
|
|
376
|
+
for (const file of files) {
|
|
377
|
+
const contents = await fsExtra.readFile(path.resolve(cwd, file), "utf8");
|
|
378
|
+
if (contents.includes(`@import "tailwindcss"`) || contents.includes(`@import 'tailwindcss'`) || contents.includes(`@tailwind base`)) return file;
|
|
379
|
+
}
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
async function getTailwindConfigFile(cwd) {
|
|
383
|
+
const files = await fg.glob("tailwind.config.*", {
|
|
384
|
+
cwd,
|
|
385
|
+
deep: 3,
|
|
386
|
+
ignore: PROJECT_SHARED_IGNORE
|
|
387
|
+
});
|
|
388
|
+
if (!files.length) return null;
|
|
389
|
+
return files[0];
|
|
390
|
+
}
|
|
391
|
+
async function getTsConfigAliasPrefix(cwd) {
|
|
392
|
+
const tsConfig = await loadConfig(cwd);
|
|
393
|
+
if (tsConfig?.resultType === "failed" || !Object.entries(tsConfig?.paths).length) return null;
|
|
394
|
+
for (const [alias, paths] of Object.entries(tsConfig.paths)) if (paths.includes("./*") || paths.includes("./src/*") || paths.includes("./app/*") || paths.includes("./resources/js/*")) return alias.replace(/\/\*$/, "") ?? null;
|
|
395
|
+
return Object.keys(tsConfig?.paths)?.[0].replace(/\/\*$/, "") ?? null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/utils/resolve-import.ts
|
|
400
|
+
async function resolveImport(importPath, config) {
|
|
401
|
+
return createMatchPath(config.absoluteBaseUrl, config.paths)(importPath, void 0, () => true, [
|
|
402
|
+
".ts",
|
|
403
|
+
".tsx",
|
|
404
|
+
".jsx",
|
|
405
|
+
".js",
|
|
406
|
+
".css"
|
|
407
|
+
]);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/utils/get-config.ts
|
|
412
|
+
const explorer = cosmiconfig("components", { searchPlaces: ["components.json"] });
|
|
413
|
+
async function getConfig(cwd) {
|
|
414
|
+
const config = await getRawConfig(cwd);
|
|
415
|
+
if (!config) return null;
|
|
416
|
+
if (!config.iconLibrary) config.iconLibrary = config.style === "new-york" ? "radix" : "lucide";
|
|
417
|
+
return await resolveConfigPaths(cwd, config);
|
|
418
|
+
}
|
|
419
|
+
async function resolveConfigPaths(cwd, config) {
|
|
420
|
+
config.registries = {
|
|
421
|
+
...BUILTIN_REGISTRIES,
|
|
422
|
+
...config.registries || {}
|
|
423
|
+
};
|
|
424
|
+
const tsConfig = await loadConfig(cwd);
|
|
425
|
+
if (tsConfig.resultType === "failed") throw new Error(`Failed to load ${config.tsx ? "tsconfig" : "jsconfig"}.json. ${tsConfig.message ?? ""}`.trim());
|
|
426
|
+
return configSchema.parse({
|
|
427
|
+
...config,
|
|
428
|
+
resolvedPaths: {
|
|
429
|
+
cwd,
|
|
430
|
+
tailwindConfig: config.tailwind.config ? path.resolve(cwd, config.tailwind.config) : "",
|
|
431
|
+
tailwindCss: path.resolve(cwd, config.tailwind.css),
|
|
432
|
+
utils: await resolveImport(config.aliases["utils"], tsConfig),
|
|
433
|
+
components: await resolveImport(config.aliases["components"], tsConfig),
|
|
434
|
+
ui: config.aliases["ui"] ? await resolveImport(config.aliases["ui"], tsConfig) : path.resolve(await resolveImport(config.aliases["components"], tsConfig) ?? cwd, "ui"),
|
|
435
|
+
lib: config.aliases["lib"] ? await resolveImport(config.aliases["lib"], tsConfig) : path.resolve(await resolveImport(config.aliases["utils"], tsConfig) ?? cwd, ".."),
|
|
436
|
+
hooks: config.aliases["hooks"] ? await resolveImport(config.aliases["hooks"], tsConfig) : path.resolve(await resolveImport(config.aliases["components"], tsConfig) ?? cwd, "..", "hooks")
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
async function getRawConfig(cwd) {
|
|
441
|
+
try {
|
|
442
|
+
const configResult = await explorer.search(cwd);
|
|
443
|
+
if (!configResult) return null;
|
|
444
|
+
const config = rawConfigSchema.parse(configResult.config);
|
|
445
|
+
if (config.registries) {
|
|
446
|
+
for (const registryName of Object.keys(config.registries)) if (registryName in BUILTIN_REGISTRIES) throw new Error(`"${registryName}" is a built-in registry and cannot be overridden.`);
|
|
447
|
+
}
|
|
448
|
+
return config;
|
|
449
|
+
} catch (error) {
|
|
450
|
+
const componentPath = `${cwd}/components.json`;
|
|
451
|
+
if (error instanceof Error && error.message.includes("reserved registry")) throw error;
|
|
452
|
+
throw new Error(`Invalid configuration found in ${highlighter.info(componentPath)}.`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region src/commands/init.ts
|
|
458
|
+
const initOptionsSchema = z.object({
|
|
459
|
+
cwd: z.string(),
|
|
460
|
+
components: z.array(z.string()).optional(),
|
|
461
|
+
yes: z.boolean(),
|
|
462
|
+
defaults: z.boolean(),
|
|
463
|
+
force: z.boolean(),
|
|
464
|
+
silent: z.boolean(),
|
|
465
|
+
isNewProject: z.boolean(),
|
|
466
|
+
srcDir: z.boolean().optional(),
|
|
467
|
+
cssVariables: z.boolean(),
|
|
468
|
+
template: z.string().optional().refine((val) => {
|
|
469
|
+
if (val) return TEMPLATES[val];
|
|
470
|
+
return true;
|
|
471
|
+
}, { message: "Invalid template. Please use 'next' or 'next-monorepo'." }),
|
|
472
|
+
baseColor: z.string().optional().refine((val) => {
|
|
473
|
+
if (val) return BASE_COLORS.find((color) => color.name === val);
|
|
474
|
+
return true;
|
|
475
|
+
}, { message: `Invalid base color. Please use '${BASE_COLORS.map((color) => color.name).join("', '")}'` }),
|
|
476
|
+
baseStyle: z.boolean()
|
|
477
|
+
});
|
|
478
|
+
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. (next, next-monorepo)").option("-b, --base-color <base-color>", "the base color to use. (neutral, gray, zinc, stone, slate)", void 0).option("-y, --yes", "skip confirmation prompt.", true).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.").action(async (_components, opts) => {
|
|
479
|
+
try {
|
|
480
|
+
await runInit(opts);
|
|
481
|
+
} catch (error) {
|
|
482
|
+
logger.break();
|
|
483
|
+
handleError(error);
|
|
484
|
+
} finally {
|
|
485
|
+
clearRegistryContext();
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
async function runInit(options) {
|
|
489
|
+
let newProjectTemplate;
|
|
490
|
+
if (!options.skipPreflight) {
|
|
491
|
+
const preflight = await preFlightInit(options);
|
|
492
|
+
if (preflight.errors[MISSING_DIR_OR_EMPTY_PROJECT]) {
|
|
493
|
+
const { projectPath, template } = await createProject(options);
|
|
494
|
+
if (!projectPath) process.exit(1);
|
|
495
|
+
options.cwd = projectPath;
|
|
496
|
+
options.isNewProject = true;
|
|
497
|
+
newProjectTemplate = template;
|
|
498
|
+
}
|
|
499
|
+
preflight.projectInfo;
|
|
500
|
+
} else await getProjectInfo(options.cwd);
|
|
501
|
+
if (newProjectTemplate) {
|
|
502
|
+
options.cwd = path.resolve(options.cwd, {
|
|
503
|
+
"astro-monorepo": "apps/web",
|
|
504
|
+
"astro-with-component-docs-monorepo": "apps/web",
|
|
505
|
+
astro: ""
|
|
506
|
+
}[newProjectTemplate]);
|
|
507
|
+
logger.log(`${highlighter.success("Success!")} Project initialization completed.\nYou may now add components.`);
|
|
508
|
+
return await getConfig(options.cwd);
|
|
509
|
+
}
|
|
510
|
+
const shadcnBin = process.platform === "win32" ? "shadcn.cmd" : "shadcn";
|
|
511
|
+
const localShadcnPath = path.resolve(options.cwd, "node_modules", ".bin", shadcnBin);
|
|
512
|
+
try {
|
|
513
|
+
const env = { ...process.env };
|
|
514
|
+
if (process.env.REGISTRY_URL) env.REGISTRY_URL = process.env.REGISTRY_URL;
|
|
515
|
+
if (await fsExtra.pathExists(localShadcnPath)) await execa(localShadcnPath, [
|
|
516
|
+
"init",
|
|
517
|
+
"--base-color",
|
|
518
|
+
"neutral"
|
|
519
|
+
], {
|
|
520
|
+
stdio: "inherit",
|
|
521
|
+
cwd: options.cwd,
|
|
522
|
+
env
|
|
523
|
+
});
|
|
524
|
+
else await execa("npx", [
|
|
525
|
+
"-y",
|
|
526
|
+
"shadcn@latest",
|
|
527
|
+
"init",
|
|
528
|
+
"--base-color",
|
|
529
|
+
"neutral"
|
|
530
|
+
], {
|
|
531
|
+
stdio: "inherit",
|
|
532
|
+
cwd: options.cwd,
|
|
533
|
+
env
|
|
534
|
+
});
|
|
535
|
+
} catch (err) {
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region src/commands/docs.ts
|
|
542
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
543
|
+
dirname$1(__filename);
|
|
544
|
+
function readTsConfig(projectRoot) {
|
|
545
|
+
try {
|
|
546
|
+
const tsconfigPath = resolve(projectRoot, "tsconfig.json");
|
|
547
|
+
if (!existsSync(tsconfigPath)) return null;
|
|
548
|
+
const raw = readFileSync(tsconfigPath, "utf-8");
|
|
549
|
+
return JSON.parse(raw);
|
|
550
|
+
} catch {
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function resolveAliasPathUsingTsConfig(inputPath, projectRoot) {
|
|
555
|
+
const cfg = readTsConfig(projectRoot);
|
|
556
|
+
if (!cfg || !cfg.compilerOptions) return null;
|
|
557
|
+
const baseUrl = cfg.compilerOptions.baseUrl || ".";
|
|
558
|
+
const paths = cfg.compilerOptions.paths || {};
|
|
559
|
+
for (const [key, values] of Object.entries(paths)) {
|
|
560
|
+
const pattern = key.replace(/\*/g, "(.*)");
|
|
561
|
+
const re = /* @__PURE__ */ new RegExp(`^${pattern}$`);
|
|
562
|
+
const match = inputPath.match(re);
|
|
563
|
+
if (!match) continue;
|
|
564
|
+
const wildcard = match[1] || "";
|
|
565
|
+
const first = Array.isArray(values) ? values[0] : values;
|
|
566
|
+
if (!first) continue;
|
|
567
|
+
const target = String(first).replace(/\*/g, wildcard);
|
|
568
|
+
return resolve(projectRoot, baseUrl, target);
|
|
569
|
+
}
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
async function generateDocs({ cwd, outDir, verbose }) {
|
|
573
|
+
const DEBUG = process.env.BEJAMAS_DEBUG === "1" || process.env.BEJAMAS_DEBUG === "true" || verbose;
|
|
574
|
+
try {
|
|
575
|
+
const shellCwd = process.cwd();
|
|
576
|
+
let projectRoot = shellCwd;
|
|
577
|
+
let probe = shellCwd;
|
|
578
|
+
for (let i = 0; i < 6 && probe; i += 1) {
|
|
579
|
+
const candidate = resolve(probe, "components.json");
|
|
580
|
+
if (existsSync(candidate)) {
|
|
581
|
+
projectRoot = probe;
|
|
582
|
+
try {
|
|
583
|
+
const raw = readFileSync(candidate, "utf-8");
|
|
584
|
+
const config = JSON.parse(raw);
|
|
585
|
+
if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.aliases?.ui) {
|
|
586
|
+
const mapped = String(config.aliases.ui);
|
|
587
|
+
let uiAbs = null;
|
|
588
|
+
if (mapped.startsWith("./") || mapped.startsWith("../") || isAbsolute(mapped)) uiAbs = resolve(projectRoot, mapped);
|
|
589
|
+
else uiAbs = resolveAliasPathUsingTsConfig(mapped, projectRoot);
|
|
590
|
+
if (!uiAbs && mapped.startsWith("@/")) uiAbs = resolve(projectRoot, "src", mapped.slice(2));
|
|
591
|
+
if (uiAbs) process.env.BEJAMAS_UI_ROOT = uiAbs;
|
|
592
|
+
}
|
|
593
|
+
if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.tailwind?.css) {
|
|
594
|
+
const cssRaw = String(config.tailwind.css);
|
|
595
|
+
let cssAbs = null;
|
|
596
|
+
if (cssRaw.startsWith("./") || cssRaw.startsWith("../") || isAbsolute(cssRaw)) cssAbs = resolve(projectRoot, cssRaw);
|
|
597
|
+
else cssAbs = resolveAliasPathUsingTsConfig(cssRaw, projectRoot);
|
|
598
|
+
if (!cssAbs && cssRaw.startsWith("@/")) cssAbs = resolve(projectRoot, "src", cssRaw.slice(2));
|
|
599
|
+
if (cssAbs) {
|
|
600
|
+
const uiRootFromCss = resolve(cssAbs, "..", "..", "..");
|
|
601
|
+
process.env.BEJAMAS_UI_ROOT = uiRootFromCss;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (!outDir && config?.aliases?.docs) {
|
|
605
|
+
const mapped = String(config.aliases.docs);
|
|
606
|
+
let outResolved = null;
|
|
607
|
+
if (mapped.startsWith("./") || mapped.startsWith("../") || isAbsolute(mapped)) outResolved = mapped;
|
|
608
|
+
else {
|
|
609
|
+
const abs = resolveAliasPathUsingTsConfig(mapped, projectRoot);
|
|
610
|
+
if (abs) outResolved = relative$1(projectRoot, abs);
|
|
611
|
+
}
|
|
612
|
+
if (!outResolved && mapped.startsWith("@/")) outResolved = mapped.replace(/^@\//, "src/");
|
|
613
|
+
const finalOut = outResolved ?? mapped;
|
|
614
|
+
if (finalOut && !process.env.BEJAMAS_DOCS_OUT_DIR) {
|
|
615
|
+
process.env.BEJAMAS_DOCS_OUT_DIR = finalOut;
|
|
616
|
+
process.env.BEJAMAS_DOCS_CWD = projectRoot;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
} catch {}
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
const parent = resolve(probe, "..");
|
|
623
|
+
probe = parent === probe ? null : parent;
|
|
624
|
+
}
|
|
625
|
+
if (!process.env.BEJAMAS_DOCS_CWD) process.env.BEJAMAS_DOCS_CWD = shellCwd;
|
|
626
|
+
if (!process.env.BEJAMAS_UI_ROOT) {
|
|
627
|
+
const defaultGuess = (() => {
|
|
628
|
+
let current = shellCwd;
|
|
629
|
+
for (let i = 0; i < 6; i += 1) {
|
|
630
|
+
const cand = resolve(current, "packages/ui/package.json");
|
|
631
|
+
if (existsSync(cand)) {
|
|
632
|
+
const abs = resolve(current, "packages/ui");
|
|
633
|
+
return relative$1(shellCwd, abs) || abs;
|
|
634
|
+
}
|
|
635
|
+
const parent = resolve(current, "..");
|
|
636
|
+
if (parent === current) break;
|
|
637
|
+
current = parent;
|
|
638
|
+
}
|
|
639
|
+
const nm = resolve(shellCwd, "node_modules/@bejamas/ui/package.json");
|
|
640
|
+
if (existsSync(nm)) {
|
|
641
|
+
const abs = resolve(shellCwd, "node_modules/@bejamas/ui");
|
|
642
|
+
return relative$1(shellCwd, abs) || abs;
|
|
643
|
+
}
|
|
644
|
+
return "packages/ui";
|
|
645
|
+
})();
|
|
646
|
+
const { uiRoot } = await prompts({
|
|
647
|
+
type: "text",
|
|
648
|
+
name: "uiRoot",
|
|
649
|
+
message: "Path to @bejamas/ui package root:",
|
|
650
|
+
initial: defaultGuess,
|
|
651
|
+
validate: (val) => {
|
|
652
|
+
const p = resolve(shellCwd, val);
|
|
653
|
+
return existsSync(resolve(p, "package.json")) ? true : `No package.json found in ${p}`;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
if (!uiRoot) {
|
|
657
|
+
logger.error("@bejamas/ui root is required to generate docs.");
|
|
658
|
+
process.exit(1);
|
|
659
|
+
}
|
|
660
|
+
process.env.BEJAMAS_UI_ROOT = resolve(shellCwd, uiRoot);
|
|
661
|
+
}
|
|
662
|
+
if (cwd && cwd.length) process.env.BEJAMAS_UI_ROOT = resolve(cwd);
|
|
663
|
+
if (outDir && outDir.length) process.env.BEJAMAS_DOCS_OUT_DIR = outDir;
|
|
664
|
+
if (!process.env.BEJAMAS_DOCS_OUT_DIR) {
|
|
665
|
+
const { out } = await prompts({
|
|
666
|
+
type: "text",
|
|
667
|
+
name: "out",
|
|
668
|
+
message: "Where should we output docs (relative to project root)?",
|
|
669
|
+
initial: "src/content/docs/components"
|
|
670
|
+
});
|
|
671
|
+
if (!out) {
|
|
672
|
+
logger.error("An output directory is required to generate docs.");
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
675
|
+
process.env.BEJAMAS_DOCS_OUT_DIR = out;
|
|
676
|
+
process.env.BEJAMAS_DOCS_CWD = process.env.BEJAMAS_DOCS_CWD || shellCwd;
|
|
677
|
+
}
|
|
678
|
+
process.env.BEJAMAS_SKIP_AUTO_RUN = "1";
|
|
679
|
+
logger.info(`Generating docs...`);
|
|
680
|
+
if (DEBUG) {
|
|
681
|
+
logger.info(`Generator entry: @/src/docs/generate-mdx/index`);
|
|
682
|
+
if (process.env.BEJAMAS_UI_ROOT) logger.info(`UI root: ${process.env.BEJAMAS_UI_ROOT}`);
|
|
683
|
+
if (process.env.BEJAMAS_DOCS_CWD) logger.info(`Docs CWD: ${process.env.BEJAMAS_DOCS_CWD}`);
|
|
684
|
+
if (process.env.BEJAMAS_DOCS_OUT_DIR) logger.info(`Docs out: ${process.env.BEJAMAS_DOCS_OUT_DIR}`);
|
|
685
|
+
}
|
|
686
|
+
const mod = await import("./generate-mdx-Bq9erbjr.js");
|
|
687
|
+
if (typeof mod.runDocsGenerator === "function") await mod.runDocsGenerator();
|
|
688
|
+
else throw new Error("Failed to load docs generator. Export 'runDocsGenerator' not found.");
|
|
689
|
+
} catch (err) {
|
|
690
|
+
logger.error(err?.message || String(err));
|
|
691
|
+
process.exit(1);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const docs = new Command().name("docs:build").alias("docs").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) => {
|
|
695
|
+
await generateDocs({
|
|
696
|
+
cwd: opts.cwd,
|
|
697
|
+
outDir: opts.out,
|
|
698
|
+
verbose: Boolean(opts.verbose)
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
//#endregion
|
|
703
|
+
//#region src/commands/add.ts
|
|
704
|
+
const DEFAULT_REGISTRY_URL = "https://ui-web-nine.vercel.app/r";
|
|
705
|
+
function extractOptionsForShadcn(rawArgv) {
|
|
706
|
+
const addIndex = rawArgv.findIndex((arg) => arg === "add");
|
|
707
|
+
if (addIndex === -1) return [];
|
|
708
|
+
const rest = rawArgv.slice(addIndex + 1);
|
|
709
|
+
const forwarded = [];
|
|
710
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
711
|
+
const token = rest[i];
|
|
712
|
+
if (token === "--") {
|
|
713
|
+
forwarded.push("--", ...rest.slice(i + 1));
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
if (token.startsWith("-")) {
|
|
717
|
+
if (token === "-v" || token === "--verbose") continue;
|
|
718
|
+
forwarded.push(token);
|
|
719
|
+
const next = rest[i + 1];
|
|
720
|
+
if (next && !next.startsWith("-")) {
|
|
721
|
+
forwarded.push(next);
|
|
722
|
+
i += 1;
|
|
723
|
+
}
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return forwarded;
|
|
728
|
+
}
|
|
729
|
+
async function addComponents(packages, forwardedOptions, isVerbose) {
|
|
730
|
+
const runner = await getPackageRunner(process.cwd());
|
|
731
|
+
const env = {
|
|
732
|
+
...process.env,
|
|
733
|
+
REGISTRY_URL: process.env.REGISTRY_URL || DEFAULT_REGISTRY_URL
|
|
734
|
+
};
|
|
735
|
+
const baseArgs = [
|
|
736
|
+
"shadcn@latest",
|
|
737
|
+
"add",
|
|
738
|
+
...packages,
|
|
739
|
+
...forwardedOptions
|
|
740
|
+
];
|
|
741
|
+
let cmd = "npx";
|
|
742
|
+
let args = ["-y", ...baseArgs];
|
|
743
|
+
if (runner === "bunx") {
|
|
744
|
+
cmd = "bunx";
|
|
745
|
+
args = baseArgs;
|
|
746
|
+
} else if (runner === "pnpm dlx") {
|
|
747
|
+
cmd = "pnpm";
|
|
748
|
+
args = ["dlx", ...baseArgs];
|
|
749
|
+
} else if (runner === "npx") {
|
|
750
|
+
cmd = "npx";
|
|
751
|
+
args = ["-y", ...baseArgs];
|
|
752
|
+
}
|
|
753
|
+
if (isVerbose) logger.info(`[bejamas-ui] ${cmd} ${args.join(" ")}`);
|
|
754
|
+
try {
|
|
755
|
+
await execa(cmd, args, {
|
|
756
|
+
stdio: "inherit",
|
|
757
|
+
env
|
|
758
|
+
});
|
|
759
|
+
} catch (err) {
|
|
760
|
+
process.exit(1);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
const add = new Command().name("add").description("Add components via shadcn@latest using registry URLs").argument("[components...]", "Component package names to add").option("-y, --yes", "skip confirmation prompt.", false).option("-o, --overwrite", "overwrite existing files.", false).option("-c, --cwd <cwd>", "the working directory. defaults to the current directory.", process.cwd()).option("-a, --all", "add all available components", false).option("-p, --path <path>", "the path to add the component to.").option("-s, --silent", "mute output.", false).option("--src-dir", "use the src directory when creating a new project.", false).option("--no-src-dir", "do not use the src directory when creating a new project.").action(async function action(packages, _opts, cmd) {
|
|
764
|
+
const root = cmd?.parent;
|
|
765
|
+
const verbose = Boolean(root?.opts?.().verbose);
|
|
766
|
+
const rawArgv = process.argv.slice(2);
|
|
767
|
+
const forwardedOptions = extractOptionsForShadcn(rawArgv);
|
|
768
|
+
await addComponents(packages || [], forwardedOptions, verbose);
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
//#endregion
|
|
772
|
+
//#region src/index.ts
|
|
773
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
774
|
+
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");
|
|
775
|
+
program.addCommand(init);
|
|
776
|
+
program.addCommand(add);
|
|
777
|
+
program.addCommand(docs);
|
|
778
|
+
program.parse(process.argv);
|
|
779
|
+
var src_default = program;
|
|
780
|
+
|
|
781
|
+
//#endregion
|
|
782
|
+
export { src_default as default };
|
|
783
|
+
//# sourceMappingURL=index.js.map
|