lightswind
Version:
A collection of beautifully crafted React Components, Blocks & Templates built with Tailwind CSS. Create stunning web applications effortlessly by using our 100+ professional and animated react components.
1,039 lines (903 loc) • 31.6 kB
JavaScript
const fs = require("fs-extra");
const path = require("path");
const { execSync } = require("child_process");
const readline = require("readline");
// --- Configuration ---
const COMPONENT_DEPS_MAP = require("./component-deps.json");
const PACKAGE_ROOT = path.join(__dirname, "..");
const DIST_COMPONENTS_DIR = path.join(PACKAGE_ROOT, "dist", "components");
// Source paths from the package
const ALL_UI_FROM = path.join(DIST_COMPONENTS_DIR, "ui");
const LIB_FROM = path.join(DIST_COMPONENTS_DIR, "lib");
const HOOKS_FROM = path.join(DIST_COMPONENTS_DIR, "hooks");
const STYLES_FROM = path.join(DIST_COMPONENTS_DIR, "styles", "lightswind.css");
// User's current working directory
const USER_CWD = process.cwd();
// --- Helper Functions ---
/**
* Read user's package.json
*/
function readUserPackageJson() {
const pkgPath = path.join(USER_CWD, "package.json");
if (!fs.existsSync(pkgPath)) {
console.error("❌ No package.json found in current directory");
console.error("Please run this command from your project root.");
process.exit(1);
}
return fs.readJsonSync(pkgPath);
}
/**
* Detect the framework being used
*/
function detectFramework(packageJson) {
const deps = {
...(packageJson.dependencies || {}),
...(packageJson.devDependencies || {})
};
if (deps['next']) return { name: 'Next.js', type: 'nextjs' };
if (deps['vite']) return { name: 'Vite', type: 'vite' };
if (deps['react-scripts']) return { name: 'Create React App', type: 'cra' };
return { name: 'React', type: 'react' };
}
/**
* Detect the best components directory for this project
*/
function detectComponentsPath() {
// Check existing directories first
const possiblePaths = [
path.join(USER_CWD, 'src', 'components'),
path.join(USER_CWD, 'components'),
path.join(USER_CWD, 'app', 'components'),
];
// Return first existing path
for (const p of possiblePaths) {
if (fs.existsSync(p)) {
return {
components: p,
lib: path.join(path.dirname(p), 'lib'),
hooks: path.join(path.dirname(p), 'hooks'),
styles: path.join(path.dirname(p), 'lightswind.css'),
framework: null // Will be set by caller
};
}
}
// No existing components directory, choose based on framework
const packageJson = readUserPackageJson();
const framework = detectFramework(packageJson);
let basePath;
if (framework.type === 'nextjs') {
// Check if Next.js uses src/ directory
const hasSrc = fs.existsSync(path.join(USER_CWD, 'src'));
basePath = hasSrc
? path.join(USER_CWD, 'src', 'components')
: path.join(USER_CWD, 'components');
} else {
// Vite, CRA, generic React - use src/components
basePath = path.join(USER_CWD, 'src', 'components');
}
return {
components: basePath,
lib: path.join(path.dirname(basePath), 'lib'),
hooks: path.join(path.dirname(basePath), 'hooks'),
styles: path.join(path.dirname(basePath), 'lightswind.css'),
framework: framework
};
}
/**
* Get all destination paths for the current project
*/
function getPaths() {
const detected = detectComponentsPath();
const packageJson = readUserPackageJson();
const framework = detected.framework || detectFramework(packageJson);
return {
ALL_UI_TO: path.join(detected.components, 'lightswind'),
LIB_TO: detected.lib,
HOOKS_TO: detected.hooks,
STYLES_TO: detected.styles,
COMPONENTS_DIR: detected.components,
FRAMEWORK: framework
};
}
/**
* Get missing dependencies
*/
function getMissingDependencies(required, userPkg) {
const installed = {
...(userPkg.dependencies || {}),
...(userPkg.devDependencies || {})
};
return required.filter(dep => !installed[dep]);
}
/**
* Prompt user for yes/no input
*/
function promptUser(question) {
return new Promise((resolve) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question(question, (answer) => {
rl.close();
const normalized = answer.toLowerCase().trim();
resolve(normalized === 'y' || normalized === 'yes' || normalized === '');
});
});
}
/**
* Install npm dependencies
*/
function installDependencies(deps) {
if (deps.length === 0) return;
console.log(`\n⏳ Installing ${deps.join(", ")}...`);
const cmd = `npm install ${deps.join(" ")}`;
try {
execSync(cmd, { stdio: "inherit", cwd: USER_CWD });
console.log("✅ Dependencies installed successfully\n");
} catch (error) {
console.error("❌ Failed to install dependencies");
console.error("You can install them manually:", deps.join(" "));
}
}
/**
* Copy shared utilities (lib, hooks, styles)
*/
function copySharedUtils() {
const paths = getPaths();
let utilsInstalled = false;
// Copy lib folder
if (fs.existsSync(LIB_FROM)) {
fs.ensureDirSync(paths.LIB_TO);
fs.copySync(LIB_FROM, paths.LIB_TO, { overwrite: true });
utilsInstalled = true;
}
// Copy hooks folder
if (fs.existsSync(HOOKS_FROM)) {
fs.ensureDirSync(paths.HOOKS_TO);
fs.copySync(HOOKS_FROM, paths.HOOKS_TO, { overwrite: true });
utilsInstalled = true;
}
// Copy styles file
if (fs.existsSync(STYLES_FROM)) {
fs.ensureDirSync(path.dirname(paths.STYLES_TO));
fs.copySync(STYLES_FROM, paths.STYLES_TO, { overwrite: true });
utilsInstalled = true;
}
if (utilsInstalled) {
console.log("✅ Installed shared utilities (lib, hooks, styles)");
}
}
/**
* Copy a single component
*/
function copyComponent(componentName) {
const paths = getPaths();
const fileName = `${componentName}.tsx`;
const fromPath = path.join(ALL_UI_FROM, fileName);
const toPath = path.join(paths.ALL_UI_TO, fileName);
if (!fs.existsSync(fromPath)) {
return false;
}
fs.ensureDirSync(path.dirname(toPath));
fs.copySync(fromPath, toPath, { overwrite: true });
return true;
}
/**
* Detect Tailwind CSS version
*/
function detectTailwindVersion() {
try {
const userPkg = readUserPackageJson();
const deps = {
...(userPkg.dependencies || {}),
...(userPkg.devDependencies || {})
};
const tailwindVersion = deps['tailwindcss'];
if (!tailwindVersion) {
return null;
}
// Extract major version number
// Match first digit(s), handling ^4, ~3.0.0, 4.0.0 etc
const versionMatch = tailwindVersion.match(/(\d+)/);
if (versionMatch) {
return parseInt(versionMatch[1]);
}
return null;
} catch (error) {
return null;
}
}
/**
* Find Tailwind config file
*/
function findTailwindConfig() {
const possibleConfigs = [
'tailwind.config.js',
'tailwind.config.ts',
'tailwind.config.mjs',
'tailwind.config.cjs'
];
for (const config of possibleConfigs) {
const configPath = path.join(USER_CWD, config);
if (fs.existsSync(configPath)) {
return configPath;
}
}
return null;
}
/**
* Find main CSS file (for Tailwind v4)
*/
function findMainCSSFile() {
const possiblePaths = [
'src/app/globals.css',
'src/globals.css',
'app/globals.css',
'src/styles/globals.css',
'src/index.css',
'src/App.css',
'styles/globals.css'
];
for (const cssPath of possiblePaths) {
const fullPath = path.join(USER_CWD, cssPath);
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
// Fallback: Search for any CSS file in src/ that might be the main one
// We prioritize files that likely contain Tailwind directives
const srcDir = path.join(USER_CWD, 'src');
if (fs.existsSync(srcDir)) {
try {
const files = fs.readdirSync(srcDir);
// First pass: look for index.css, style.css, main.css
const priorityNames = ['index.css', 'style.css', 'main.css', 'app.css'];
for (const file of files) {
if (priorityNames.includes(file.toLowerCase())) {
return path.join(srcDir, file);
}
}
// Second pass: return the first CSS file found
for (const file of files) {
if (file.endsWith('.css')) {
return path.join(srcDir, file);
}
}
} catch (e) {
// Ignore errors accessing src directory
}
}
return null;
}
/**
* Configure Tailwind plugin for v3 (tailwind.config.js)
*/
function configureTailwindV3Plugin() {
const configPath = findTailwindConfig();
if (!configPath) {
console.log("\n⚠️ Tailwind config not found");
console.log("💡 Add Lightswind plugin manually to tailwind.config.js:");
console.log(" plugins: [require('lightswind/plugin')]");
return false;
}
try {
let content = fs.readFileSync(configPath, 'utf-8');
// Check if already configured
if (content.includes("lightswind/plugin")) {
console.log("✅ Lightswind plugin already in tailwind.config");
return true;
}
// Add plugin to config
if (content.includes('plugins:')) {
// Plugins array exists, add to it
content = content.replace(
/plugins:\s*\[/,
"plugins: [\n require('lightswind/plugin'),"
);
} else {
// No plugins array, add it
content = content.replace(
/module\.exports\s*=\s*{/,
"module.exports = {\n plugins: [require('lightswind/plugin')],"
);
}
fs.writeFileSync(configPath, content, 'utf-8');
console.log("✅ Added Lightswind plugin to tailwind.config");
return true;
} catch (error) {
console.log("\n⚠️ Could not auto-configure Tailwind plugin");
console.log("💡 Add manually to tailwind.config.js:");
console.log(" plugins: [require('lightswind/plugin')]");
return false;
}
}
/**
* Configure Tailwind plugin for v4 (CSS file)
*/
function configureTailwindV4Plugin() {
const cssPath = findMainCSSFile();
if (!cssPath) {
console.log("\n⚠️ Main CSS file not found");
console.log("💡 Add Lightswind plugin manually to your CSS file:");
console.log(" @import 'tailwindcss';");
console.log(" @plugin 'lightswind/plugin';");
return false;
}
try {
let content = fs.readFileSync(cssPath, 'utf-8');
// Check if already configured
if (content.includes("lightswind/plugin")) {
console.log("✅ Lightswind plugin already in CSS");
return true;
}
// Add @plugin after @import 'tailwindcss'
if (content.includes("@import 'tailwindcss'") || content.includes('@import "tailwindcss"')) {
content = content.replace(
/@import ['"]tailwindcss['"];?\s*/,
"@import 'tailwindcss';\n@plugin 'lightswind/plugin';\n"
);
} else {
// Tailwind not imported yet, add both
content = "@import 'tailwindcss';\n@plugin 'lightswind/plugin';\n\n" + content;
}
fs.writeFileSync(cssPath, content, 'utf-8');
console.log(`✅ Added Lightswind plugin to ${path.basename(cssPath)}`);
return true;
} catch (error) {
console.log("\n⚠️ Could not auto-configure Tailwind plugin");
console.log("💡 Add manually to your main CSS file:");
console.log(" @plugin 'lightswind/plugin';");
return false;
}
}
/**
* Auto-configure Tailwind plugin based on version
*/
function autoConfigureTailwindPlugin() {
const tailwindVersion = detectTailwindVersion();
if (!tailwindVersion) {
console.log("\n⚠️ Tailwind CSS not found in package.json");
console.log("💡 Install Tailwind CSS first:");
console.log(" npm install -D tailwindcss");
return;
}
console.log(`\n🔧 Configuring Lightswind for Tailwind CSS v${tailwindVersion}...`);
if (tailwindVersion >= 4) {
// Tailwind v4 - add to CSS file
if (!configureTailwindV4Plugin()) {
appendCssFallback();
}
} else {
// Tailwind v3 or earlier - add to config file
if (!configureTailwindV3Plugin()) {
appendCssFallback();
}
}
}
/**
* Append Lightswind CSS content to main CSS file as fallback
*/
function appendCssFallback() {
console.log("\n⚠️ Falling back to manual CSS injection...");
const cssPath = findMainCSSFile();
if (!cssPath) {
console.error("❌ Could not find main CSS file to inject styles.");
return;
}
const paths = getPaths();
// We can read from the installed location since we copied it there
const stylesPath = paths.STYLES_TO;
if (!fs.existsSync(stylesPath)) {
console.error("❌ Could not find source styles file.");
return;
}
try {
const stylesContent = fs.readFileSync(stylesPath, 'utf-8');
let mainCssContent = fs.readFileSync(cssPath, 'utf-8');
if (mainCssContent.includes("/* lightswind.css */")) {
console.log("✅ Lightswind styles already present in CSS");
return;
}
// Append styles
mainCssContent += "\n\n/* lightswind.css */\n" + stylesContent;
fs.writeFileSync(cssPath, mainCssContent, 'utf-8');
console.log(`✅ Appended Lightswind styles to ${path.basename(cssPath)}`);
} catch (error) {
console.error("❌ Failed to append styles:", error.message);
}
}
// --- Commands ---
/**
* Install all components
*/
async function installAll() {
const paths = getPaths();
console.log("\n🚀 Installing all Lightswind components...");
console.log(`📦 Detected: ${paths.FRAMEWORK.name}`);
console.log(`📁 Installing to: ${paths.ALL_UI_TO}\n`);
// Get all unique dependencies
const allDeps = new Set();
Object.values(COMPONENT_DEPS_MAP).forEach(deps => {
deps.forEach(dep => allDeps.add(dep));
});
const requiredDeps = Array.from(allDeps);
const userPkg = readUserPackageJson();
const missingDeps = getMissingDependencies(requiredDeps, userPkg);
// Prompt for dependencies
if (missingDeps.length > 0) {
console.log("📦 The following dependencies are required:");
console.log(missingDeps.map(d => ` • ${d}`).join("\n"));
console.log(`\nTotal: ${missingDeps.length} packages\n`);
const shouldInstall = await promptUser("Install all dependencies? (Y/n): ");
if (shouldInstall) {
installDependencies(missingDeps);
} else {
console.log("⚠️ Skipping dependency installation.");
console.log("Some components may not work without their dependencies.\n");
}
}
// Copy all components
if (fs.existsSync(ALL_UI_FROM)) {
fs.ensureDirSync(paths.ALL_UI_TO);
fs.copySync(ALL_UI_FROM, paths.ALL_UI_TO, { overwrite: true });
console.log("✅ Installed all components");
} else {
console.error("❌ UI components source directory not found.");
process.exit(1);
}
// Copy shared utilities
copySharedUtils();
// Auto-configure Tailwind plugin
autoConfigureTailwindPlugin();
console.log("\n🎉 Success! All Lightswind components installed.");
console.log("\nNext steps:");
console.log(" 1. Import components: import { Button } from '@/components/lightswind/button'");
console.log(" 2. Start building! 🚀\n");
}
/**
* Install a specific component
*/
async function installComponent(componentName) {
if (!componentName) {
console.error("❌ Please specify a component name");
console.error("Usage: npx lightswind@latest add <component-name>");
console.error("Example: npx lightswind@latest add button\n");
process.exit(1);
}
const paths = getPaths();
console.log(`\n📦 Installing ${componentName}...`);
console.log(`📦 Detected: ${paths.FRAMEWORK.name}`);
console.log(`📁 Installing to: ${paths.ALL_UI_TO}\n`);
// Check if component exists
const componentFile = `${componentName}.tsx`;
const sourceExists = fs.existsSync(path.join(ALL_UI_FROM, componentFile));
if (!sourceExists) {
console.error(`❌ Component '${componentName}' not found.`);
console.error("\nRun 'npx lightswind@latest list' to see available components.\n");
process.exit(1);
}
// Get required dependencies
const requiredDeps = COMPONENT_DEPS_MAP[componentName] || [];
// Check missing dependencies
if (requiredDeps.length > 0) {
const userPkg = readUserPackageJson();
const missingDeps = getMissingDependencies(requiredDeps, userPkg);
if (missingDeps.length > 0) {
console.log(`📦 ${componentName} requires: ${missingDeps.join(", ")}`);
const shouldInstall = await promptUser("Install dependencies? (Y/n): ");
if (shouldInstall) {
installDependencies(missingDeps);
} else {
console.log("⚠️ Skipping dependency installation.");
console.log("This component may not work without these dependencies.\n");
}
}
}
// Copy component
if (copyComponent(componentName)) {
console.log(`✅ Installed ${componentName} component`);
}
// Copy shared utilities
copySharedUtils();
// Auto-configure Tailwind plugin
autoConfigureTailwindPlugin();
console.log(`\n🎉 Success! ${componentName} is ready to use.`);
console.log(`\nImport it: import { ${toPascalCase(componentName)} } from '@/components/lightswind/${componentName}'\n`);
}
/**
* Group components by category
*/
/**
* Group components by category
*/
function groupComponentsByCategory() {
// Map of components to their categories based on component-categories.ts
const COMPONENT_CATEGORY_MAP = {
// Utilities
"toggle-theme": "utility",
// Background
"animated-wave": "background",
"animated-bubble-particles": "background",
"aurora-shader": "background",
"beam-grid-background": "background",
"fall-beam-background": "background",
"grid-dot-backgrounds": "background",
"gradient-background": "background",
"hell-background": "background",
"interactive-grid-background": "background",
"particles-background": "background",
"rays-background": "background",
"reflect-background": "background",
"smokey-background": "background",
"shader-background": "background",
"sparkle-particles": "background",
"stripes-background": "background",
"wave-background": "background",
// Button
"border-beam": "button",
"confetti-button": "button",
"gradient-button": "button",
"ripple-button": "button",
"shine-button": "button",
"trial-button": "button",
// Text
"aurora-text-effect": "text",
"scroll-reveal": "text",
"shiny-text": "text",
"text-scroll-marquee": "text",
"typewriter-input": "text",
"typing-text": "text",
"video-text": "text",
// 3D Elements
"3d-image-ring": "3d",
"3d-image-carousel": "3d",
"3d-carousel": "3d",
"3d-hover-gallery": "3d",
"3d-marquee": "3d",
"3d-model-viewer": "3d",
"3d-perspective-card": "3d",
"3d-scroll-trigger": "3d",
"3d-slider": "3d",
"beam-circle": "3d",
"chain-carousel": "3d",
"plasma-globe": "3d",
"scroll-carousel": "3d",
"sparkle-navbar": "3d",
// Cursor
"canvas-confetti-cursor": "cursor",
"particle-orbit-effect": "cursor",
"smokey-cursor": "cursor",
"smooth-cursor": "cursor",
// Components (Animated)
"animated-notification": "components",
"bento-grid": "components",
"code-hover-cards": "components",
"count-up": "components",
"dock": "components",
"drag-order-list": "components",
"dynamic-navigation": "components",
"electro-border": "components",
"glass-folder": "components",
"globe": "components",
"glowing-cards": "components",
"hamburger-menu-overlay": "components",
"image-reveal": "components",
"image-trail-effect": "components",
"interactive-card": "components",
"interactive-gradient-card": "components",
"iphone16-pro": "components",
"lens": "components",
"magic-loader": "components",
"morphing-navigation": "components",
"orbit-card": "components",
"password-strength-indicator": "components",
"scroll-list": "components",
"scroll-stack": "components",
"scroll-timeline": "components",
"seasonal-hover-cards": "components",
"sliding-cards": "components",
"sliding-logo-marquee": "components",
"stack-list": "components",
"team-carousel": "components",
"terminal-card": "components",
"top-loader": "components",
"top-sticky-bar": "components",
"trusted-users": "components",
"ripple-loader": "components",
"woofy-hover-image": "components",
// Layout
"accordion": "layout",
"aspect-ratio": "layout",
"resizable": "layout",
"scroll-area": "layout",
"separator": "layout",
"tabs": "layout",
// UI Elements
"alert": "ui",
"alert-dialog": "ui",
"avatar": "ui",
"badge": "ui",
"button": "ui",
"card": "ui",
"carousel": "ui",
"chart": "ui",
"collapsible": "ui",
"context-menu": "ui",
"dialog": "ui",
"drawer": "ui",
"dropdown-menu": "ui",
"hover-card": "ui",
"popover": "ui",
"progress": "ui",
"sheet": "ui",
"skeleton": "ui",
"table": "ui",
"toast": "ui",
"tooltip": "ui",
// Form Controls
"calendar": "form",
"checkbox": "form",
"command": "form",
"form": "form",
"input": "form",
"input-otp": "form",
"label": "form",
"radio-group": "form",
"select": "form",
"slider": "form",
"switch": "form",
"textarea": "form",
"toggle": "form",
"toggle-group": "form",
// Navigation
"breadcrumb": "navigation",
"navigation-menu": "navigation",
"pagination": "navigation",
"sidebar": "navigation"
};
const groups = {
utility: { name: 'Utilities', emoji: '🛠️', components: [] },
background: { name: 'Background', emoji: '🌅', components: [] },
button: { name: 'Button', emoji: '🔘', components: [] },
text: { name: 'Text', emoji: '📝', components: [] },
'3d': { name: '3D Elements', emoji: '🧊', components: [] },
cursor: { name: 'Cursor', emoji: '🖱️', components: [] },
components: { name: 'Components', emoji: '🧩', components: [] },
layout: { name: 'Layout', emoji: '📐', components: [] },
ui: { name: 'UI Elements', emoji: '🎨', components: [] },
form: { name: 'Form Controls', emoji: '📝', components: [] },
navigation: { name: 'Navigation', emoji: '🧭', components: [] },
// Keep basic/specialized as fallbacks or aliases if needed, but primarily use the new ones
basic: { name: 'Basic UI', emoji: '✨', components: [] },
charts: { name: 'Chart Components', emoji: '📊', components: [] },
specialized: { name: 'Specialized Components', emoji: '🔮', components: [] }
};
Object.keys(COMPONENT_DEPS_MAP).forEach((component) => {
const category = COMPONENT_CATEGORY_MAP[component];
if (category && groups[category]) {
groups[category].components.push(component);
} else {
// Fallback logic for components not in the map
const deps = COMPONENT_DEPS_MAP[component];
if (deps.length === 0) {
groups.basic.components.push(component);
} else if (deps.includes("recharts")) {
groups.charts.components.push(component);
} else {
groups.specialized.components.push(component);
}
}
});
return groups;
}
/**
* Install all components from a specific category
*/
async function installCategory(categoryName) {
const paths = getPaths();
const groups = groupComponentsByCategory();
// Normalize category name
const normalizedCategory = categoryName.toLowerCase();
// Find matching category
const category = groups[normalizedCategory];
if (!category || category.components.length === 0) {
console.error(`\n❌ Category '${categoryName}' not found or empty.\n`);
console.log("Available categories:");
Object.entries(groups).forEach(([key, cat]) => {
if (cat.components.length > 0) {
console.log(` • ${key} - ${cat.name} (${cat.components.length} components)`);
}
});
console.log("");
process.exit(1);
}
console.log(`\n${category.emoji} Installing ${category.name}...`);
console.log(`📦 Detected: ${paths.FRAMEWORK.name}`);
console.log(`📁 Installing to: ${paths.ALL_UI_TO}`);
console.log(`📊 Total components: ${category.components.length}\n`);
// Collect all unique dependencies from category
const allDeps = new Set();
category.components.forEach(comp => {
const deps = COMPONENT_DEPS_MAP[comp] || [];
deps.forEach(dep => allDeps.add(dep));
});
const requiredDeps = Array.from(allDeps);
const userPkg = readUserPackageJson();
const missingDeps = getMissingDependencies(requiredDeps, userPkg);
// Prompt for dependencies
if (missingDeps.length > 0) {
console.log("📦 The following dependencies are required:");
console.log(missingDeps.map(d => ` • ${d}`).join("\n"));
console.log(`\nTotal: ${missingDeps.length} packages\n`);
const shouldInstall = await promptUser("Install dependencies? (Y/n): ");
if (shouldInstall) {
installDependencies(missingDeps);
} else {
console.log("⚠️ Skipping dependency installation.");
console.log("Some components may not work without their dependencies.\n");
}
}
// Copy all components in category
let installedCount = 0;
category.components.forEach(comp => {
if (copyComponent(comp)) {
installedCount++;
}
});
// Copy shared utilities
copySharedUtils();
// Auto-configure Tailwind plugin
autoConfigureTailwindPlugin();
console.log(`\n✅ Installed ${installedCount} components from ${category.name}`);
console.log(`\n🎉 Success! ${category.name} ready to use.`);
console.log(`\nComponents installed:`);
console.log(category.components.slice(0, 5).map(c => ` • ${c}`).join("\n"));
if (category.components.length > 5) {
console.log(` ... and ${category.components.length - 5} more\n`);
} else {
console.log("");
}
}
/**
* List all available components
*/
function listComponents() {
console.log("\n📋 Available Lightswind Components:\n");
// Group components by dependency type
const groups = {
noDeps: [],
lucideOnly: [],
framerMotion: [],
threeDLibs: [],
charts: [],
specialized: []
};
Object.entries(COMPONENT_DEPS_MAP).forEach(([component, deps]) => {
if (deps.length === 0) {
groups.noDeps.push(component);
} else if (deps.includes("recharts")) {
groups.charts.push(component);
} else if (deps.includes("@react-three/fiber") || deps.includes("@react-three/drei")) {
groups.threeDLibs.push(component);
} else if (deps.includes("framer-motion") && deps.length > 1) {
groups.specialized.push(component);
} else if (deps.includes("framer-motion")) {
groups.framerMotion.push(component);
} else if (deps.length === 1 && deps[0] === "lucide-react") {
groups.lucideOnly.push(component);
} else {
groups.specialized.push(component);
}
});
// Display groups
if (groups.noDeps.length > 0) {
console.log("✨ Basic UI (no external dependencies):");
groups.noDeps.forEach(c => console.log(` • ${c}`));
console.log("");
}
if (groups.lucideOnly.length > 0) {
console.log("🎯 UI Components (requires lucide-react only):");
groups.lucideOnly.forEach(c => console.log(` • ${c}`));
console.log("");
}
if (groups.framerMotion.length > 0) {
console.log("🎨 Animated Components (requires framer-motion):");
groups.framerMotion.forEach(c => console.log(` • ${c}`));
console.log("");
}
if (groups.threeDLibs.length > 0) {
console.log("🌐 3D Components (requires @react-three/fiber, @react-three/drei):");
groups.threeDLibs.forEach(c => console.log(` • ${c}`));
console.log("");
}
if (groups.charts.length > 0) {
console.log("📊 Chart Components (requires recharts):");
groups.charts.forEach(c => console.log(` • ${c}`));
console.log("");
}
if (groups.specialized.length > 0) {
console.log("🔮 Specialized Components (various dependencies):");
groups.specialized.forEach(c => console.log(` • ${c}`));
console.log("");
}
const total = Object.values(groups).reduce((sum, arr) => sum + arr.length, 0);
console.log(`Total: ${total} components available\n`);
console.log("Usage:");
console.log(" npx lightswind@latest add <component-name>");
console.log(" npx lightswind@latest init (install all)\n");
}
/**
* Show help message
*/
function showHelp() {
console.log("\n⚡ Lightswind UI CLI\n");
console.log("Usage:");
console.log(" npx lightswind@latest <command> [options]\n");
console.log("Commands:");
console.log(" init Install all components and utilities");
console.log(" add <component-name> Install a specific component");
console.log(" add --category <name> Install all components from category");
console.log(" list Show all available components\n");
console.log("Categories:");
console.log(" basic, ui, animated, 3d, charts, specialized\n");
console.log("Examples:");
console.log(" npx lightswind@latest init");
console.log(" npx lightswind@latest add button");
console.log(" npx lightswind@latest add globe");
console.log(" npx lightswind@latest add --category animated");
console.log(" npx lightswind@latest add --category 3d");
console.log(" npx lightswind@latest list\n");
console.log("Learn more: https://lightswind.com\n");
}
/**
* Convert kebab-case to PascalCase
*/
function toPascalCase(str) {
return str
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join('');
}
// --- Main ---
const command = process.argv[2];
const arg = process.argv[3];
(async () => {
switch (command) {
case "init":
await installAll();
break;
case "add":
// Check if it's a category installation
if (arg === "--category" || arg === "-c") {
const categoryName = process.argv[4];
if (!categoryName) {
console.error("\n❌ Please specify a category name");
console.error("Usage: npx lightswind@latest add --category <name>");
console.error("\nAvailable categories: basic, ui, animated, 3d, charts, specialized\n");
process.exit(1);
}
await installCategory(categoryName);
} else {
await installComponent(arg);
}
break;
case "list":
listComponents();
break;
case "--help":
case "-h":
case "help":
showHelp();
break;
default:
if (!command) {
showHelp();
} else {
console.error(`\n❌ Unknown command: ${command}\n`);
showHelp();
}
process.exit(1);
}
})();