bigblocks
Version:
Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React
165 lines (164 loc) • 6.16 kB
JavaScript
import { execSync } from "node:child_process";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import chalk from "chalk";
import prompts from "prompts";
import { loadComponent } from "./registry.js";
// Load config from components.json
function loadConfig() {
const configPath = join(process.cwd(), "components.json");
if (!existsSync(configPath)) {
console.log(chalk.yellow("No components.json found. Run 'bigblocks init' first."));
process.exit(1);
}
const config = JSON.parse(readFileSync(configPath, "utf-8"));
return config;
}
// Transform content with template placeholders
function transformContent(content, config) {
return content.replace(/<%- config\.aliases\.(\w+) %>/g, (match, alias) => {
return config.aliases[alias] || match;
});
}
// Resolve target path
function resolveTargetPath(target, config) {
const transformed = transformContent(target, config);
const fileName = transformed.split("/").pop() || "";
if (transformed.startsWith(config.aliases.ui)) {
return join(process.cwd(), "src/components/ui", fileName);
}
if (transformed.startsWith(config.aliases.utils)) {
return join(process.cwd(), "src/lib", fileName);
}
if (transformed.startsWith(config.aliases.hooks)) {
return join(process.cwd(), "src/hooks", fileName);
}
if (transformed.startsWith(config.aliases.components)) {
return join(process.cwd(), "src/components", fileName);
}
if (transformed.startsWith(config.aliases.lib)) {
return join(process.cwd(), "src/lib", fileName);
}
return join(process.cwd(), "src/components", fileName);
}
// Detect package manager
function detectPackageManager() {
if (existsSync(join(process.cwd(), "bun.lockb")))
return "bun";
if (existsSync(join(process.cwd(), "pnpm-lock.yaml")))
return "pnpm";
if (existsSync(join(process.cwd(), "yarn.lock")))
return "yarn";
return "npm";
}
// Check if a shadcn component exists locally
function isShadcnComponentInstalled(name) {
const possiblePaths = [
join(process.cwd(), "src/components/ui", `${name}.tsx`),
join(process.cwd(), "components/ui", `${name}.tsx`),
join(process.cwd(), "app/components/ui", `${name}.tsx`),
];
return possiblePaths.some((path) => existsSync(path));
}
// Install a shadcn component
function installShadcnComponent(name) {
console.log(chalk.cyan(`Installing shadcn component: ${name}...`));
const pm = detectPackageManager();
const cmd = pm === "npm"
? `npx shadcn@latest add ${name} --yes`
: `${pm} dlx shadcn@latest add ${name} --yes`;
try {
execSync(cmd, { stdio: "inherit" });
console.log(chalk.green(`✓ Installed shadcn component: ${name}`));
}
catch (error) {
throw new Error(`Failed to install shadcn component ${name}`);
}
}
export async function installComponent(name, options, installedSet, allDependencies, spinner) {
if (installedSet.has(name))
return;
const config = loadConfig();
const component = await loadComponent(name);
if (!component) {
// Not in our registry - try shadcn
console.log(chalk.gray(`Component "${name}" not found in BigBlocks registry, checking shadcn...`));
if (isShadcnComponentInstalled(name)) {
console.log(chalk.green(`✓ shadcn component "${name}" already installed`));
installedSet.add(name);
return;
}
try {
installShadcnComponent(name);
installedSet.add(name);
return;
}
catch (error) {
throw new Error(`Component "${name}" not found in BigBlocks or shadcn registry`);
}
}
// Install registry dependencies first
if (component.registryDependencies) {
for (const dep of component.registryDependencies) {
await installComponent(dep, options, installedSet, allDependencies, spinner);
}
}
spinner.start(`Installing ${name}...`);
// Install component files
for (const file of component.files) {
let content = "";
if (file.content) {
content = file.content;
}
else {
// Read from source file
const sourcePath = join(__dirname, "..", "..", "src", file.path);
if (existsSync(sourcePath)) {
content = readFileSync(sourcePath, "utf-8");
}
else {
throw new Error(`Source file not found: ${file.path}`);
}
}
const transformedContent = transformContent(content, config);
const targetPath = file.target
? resolveTargetPath(file.target, config)
: resolveTargetPath(file.path, config);
// Create directory if needed
const targetDir = dirname(targetPath);
if (!existsSync(targetDir)) {
mkdirSync(targetDir, { recursive: true });
}
// Check if file exists
if (existsSync(targetPath) && !options.overwrite) {
const { overwrite } = await prompts({
type: "confirm",
name: "overwrite",
message: `File exists: ${targetPath}. Overwrite?`,
initial: false,
});
if (!overwrite) {
spinner.info(chalk.yellow(`Skipped ${targetPath}`));
continue;
}
}
// Write file
writeFileSync(targetPath, transformedContent);
spinner.info(chalk.green(`✓ ${targetPath}`));
}
// Collect dependencies
if (!options.skipDeps) {
if (component.dependencies) {
for (const dep of component.dependencies) {
allDependencies.deps.add(dep);
}
}
if (component.devDependencies) {
for (const dep of component.devDependencies) {
allDependencies.devDeps.add(dep);
}
}
}
spinner.succeed(`Installed ${chalk.cyan(name)}`);
installedSet.add(name);
}