UNPKG

@reliverse/rse-sdk

Version:

@reliverse/rse-sdk allows you to create new plugins for @reliverse/rse CLI, interact with reliverse.org, and even extend your own CLI functionality (you may also try @reliverse/dler-sdk for this case).

315 lines (314 loc) 9.19 kB
import path from "@reliverse/pathkit"; import fs from "@reliverse/relifso"; import { relinka } from "@reliverse/relinka"; import { execa } from "execa"; import { pmx } from "../getPackageManager.js"; const COMPONENT_DEPENDENCIES = { "alert-dialog": ["button"], "context-menu": ["button"], "dropdown-menu": ["button"], form: ["button", "label", "input"], "hover-card": ["button"], menubar: ["button"], "navigation-menu": ["button"], popover: ["button"], sheet: ["button"], toast: ["toast", "button"], combobox: ["popover", "command"], command: ["dialog"], "date-picker": ["button", "calendar", "popover"], "input-otp": ["input"], resizable: ["separator"], sonner: [], "toggle-group": ["toggle"] }; const THEMES = [ { name: "Default", colors: { "--background": "0 0% 100%", "--foreground": "240 10% 3.9%", "--card": "0 0% 100%", "--card-foreground": "240 3% 6%", "--card-two": "240 10% 3.9%", "--card-two-foreground": "0 0% 98%", "--popover": "0 0% 100%", "--popover-foreground": "240 10% 3.9%", "--primary": "240 5.9% 10%", "--primary-foreground": "0 0% 98%", "--secondary": "240 4.8% 95.9%", "--secondary-foreground": "240 5.9% 10%", "--muted": "240 4.8% 95.9%", "--muted-foreground": "240 3.8% 46.1%", "--accent": "240 4.8% 95.9%", "--accent-foreground": "240 5.9% 10%", "--destructive": "0 84.2% 60.2%", "--destructive-foreground": "0 0% 98%", "--border": "240 5.9% 90%", "--input": "240 5.9% 90%", "--ring": "240 10% 3.9%", "--chart-1": "12 76% 61%", "--chart-2": "173 58% 39%", "--chart-3": "197 37% 24%", "--chart-4": "43 74% 66%", "--chart-5": "27 87% 67%", "--radius": "0.5rem", "--sidebar-background": "0 0% 98%", "--sidebar-foreground": "240 5.3% 26.1%", "--sidebar-primary": "240 5.9% 10%", "--sidebar-primary-foreground": "0 0% 98%", "--sidebar-accent": "240 4.8% 95.9%", "--sidebar-accent-foreground": "240 5.9% 10%", "--sidebar-border": "220 13% 91%", "--sidebar-ring": "217.2 91.2% 59.8%" } }, { name: "Dark", colors: { "--background": "240 10% 3.9%", "--foreground": "0 0% 98%", "--card": "240 10% 3.9%", "--card-foreground": "0 0% 98%", "--card-two": "240 3% 6%", "--card-two-foreground": "0 0% 98%", "--popover": "240 10% 3.9%", "--popover-foreground": "0 0% 98%", "--primary": "0 0% 98%", "--primary-foreground": "240 5.9% 10%", "--secondary": "240 3.7% 15.9%", "--secondary-foreground": "0 0% 98%", "--muted": "240 3.7% 15.9%", "--muted-foreground": "240 5% 64.9%", "--accent": "240 3.7% 15.9%", "--accent-foreground": "0 0% 98%", "--destructive": "0 62.8% 30.6%", "--destructive-foreground": "0 0% 98%", "--border": "240 3.7% 15.9%", "--input": "240 3.7% 15.9%", "--ring": "240 4.9% 83.9%", "--chart-1": "220 70% 50%", "--chart-2": "160 60% 45%", "--chart-3": "30 80% 55%", "--chart-4": "280 65% 60%", "--chart-5": "340 75% 55%", "--radius": "0.5rem", "--sidebar-background": "240 5.9% 10%", "--sidebar-foreground": "240 4.8% 95.9%", "--sidebar-primary": "224.3 76.3% 48%", "--sidebar-primary-foreground": "0 0% 100%", "--sidebar-accent": "240 3.7% 15.9%", "--sidebar-accent-foreground": "240 4.8% 95.9%", "--sidebar-border": "240 3.7% 15.9%", "--sidebar-ring": "217.2 91.2% 59.8%" } } ]; export async function readShadcnConfig(cwd) { const configPath = path.join(cwd, "components.json"); try { if (await fs.pathExists(configPath)) { return await fs.readJson(configPath); } } catch (error) { relinka( "error", "Error reading shadcn config:", error instanceof Error ? error.message : String(error) ); } return null; } export async function getInstalledComponents(cwd, config) { const uiPath = path.join(cwd, config.aliases.ui.replace("~", "src")); try { if (await fs.pathExists(uiPath)) { const files = await fs.readdir(uiPath); return files.filter((f) => f.endsWith(".tsx")).map((f) => f.replace(".tsx", "")); } } catch (error) { relinka( "error", "Error reading UI components:", error instanceof Error ? error.message : String(error) ); } return []; } async function ensureComponentDependencies(cwd, component, config) { const dependencies = COMPONENT_DEPENDENCIES[component] ?? []; const installedComponents = await getInstalledComponents(cwd, config); for (const dep of dependencies) { if (!installedComponents.includes(dep)) { relinka("info", `Installing required dependency: ${dep}`); await installComponent(cwd, dep); } } } export async function installComponent(cwd, component, options = {}) { try { const config = await readShadcnConfig(cwd); if (!config) { relinka("error", "shadcn/ui configuration not found"); return; } await ensureComponentDependencies(cwd, component, config); const args = ["shadcn-ui@latest", "add", component]; if (options.yes) { args.push("--yes"); } if (options.overwrite) { args.push("--overwrite"); } if (options.all) { args.push("--all"); } if (options.path) { args.push("--path"); args.push(options.path); } await execa(pmx, args, { cwd }); relinka("success", `Installed component: ${component}`); } catch (error) { relinka( "error", `Failed to install ${component}:`, error instanceof Error ? error.message : String(error) ); } } export async function updateComponent(cwd, component) { return installComponent(cwd, component, { overwrite: true }); } export async function removeComponent(cwd, config, component) { try { const dependentComponents = Object.entries(COMPONENT_DEPENDENCIES).filter(([_, deps]) => deps.includes(component)).map(([comp]) => comp); const installedComponents = await getInstalledComponents(cwd, config); const installedDependents = dependentComponents.filter( (comp) => installedComponents.includes(comp) ); if (installedDependents.length > 0) { relinka( "error", `Cannot remove ${component} as it is required by: ${installedDependents.join(", ")}` ); return; } const componentPath = path.join( cwd, config.aliases.ui.replace("~", "src"), `${component}.tsx` ); await fs.remove(componentPath); relinka("success", `Removed component: ${component}`); } catch (error) { relinka( "error", `Failed to remove ${component}:`, error instanceof Error ? error.message : String(error) ); } } export async function applyTheme(cwd, config, theme) { const cssPath = path.join(cwd, config.tailwind.css.replace("~", "src")); try { let cssContent = await fs.readFile(cssPath, "utf-8"); await fs.writeFile(`${cssPath}.backup`, cssContent); const rootRegex = /:root\s*{[^}]*}/; const newRootSection = `:root { ${Object.entries(theme.colors).map(([key, value]) => ` ${key}: ${value};`).join("\n")} }`; cssContent = cssContent.replace(rootRegex, newRootSection); await fs.writeFile(cssPath, cssContent); relinka( "success", `Applied theme: ${theme.name} (backup created at ${cssPath}.backup)` ); } catch (error) { relinka( "error", `Failed to apply theme ${theme.name}:`, error instanceof Error ? error.message : String(error) ); try { if (await fs.pathExists(`${cssPath}.backup`)) { await fs.copy(`${cssPath}.backup`, cssPath); relinka("info", "Restored previous theme from backup"); } } catch (backupError) { relinka( "error", "Failed to restore theme backup:", backupError instanceof Error ? backupError.message : String(backupError) ); } } } export const AVAILABLE_COMPONENTS = [ "accordion", "alert-dialog", "alert", "aspect-ratio", "avatar", "badge", "button", "calendar", "card", "carousel", "charts", "checkbox", "collapsible", "combobox", "command", "context-menu", "data-table", "date-picker", "dialog", "drawer", "dropdown-menu", "form", "hover-card", "input", "input-otp", "label", "menubar", "navigation-menu", "pagination", "popover", "progress", "radio-group", "resizable", "scroll-area", "select", "separator", "sheet", "sidebar", "skeleton", "slider", "sonner", "switch", "table", "tabs", "textarea", "toast", "toggle", "toggle-group", "tooltip" ]; export { THEMES }; export function selectSidebarPrompt(projectPath) { relinka( "verbose", "The following project requested sidebar installation", projectPath ); relinka("verbose", "Coming soon..."); } export function selectChartsPrompt(projectPath) { relinka( "verbose", "The following project requested charts installation", projectPath ); relinka("verbose", "Coming soon..."); }