@moontra/moonui-cli
Version:
CLI tool for MoonUI component library
1,420 lines (1,402 loc) • 151 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// package.json
var require_package = __commonJS({
"package.json"(exports, module2) {
module2.exports = {
name: "@moontra/moonui-cli",
version: "2.6.8",
description: "CLI tool for MoonUI component library",
main: "dist/index.js",
types: "dist/index.d.ts",
bin: {
moonui: "./dist/index.js"
},
files: [
"dist",
"src/postcss",
"src/vite",
"src/theme-presets.ts",
"src/utils/theme-generator.ts"
],
exports: {
".": {
types: "./dist/index.d.ts",
import: "./dist/index.js",
require: "./dist/index.cjs"
},
"./postcss/moonui-theme-plugin": {
types: "./src/postcss/moonui-theme-plugin.d.ts",
require: "./src/postcss/moonui-theme-plugin.js"
},
"./vite/moonui-theme-plugin": {
types: "./dist/vite/moonui-theme-plugin.d.ts",
import: "./dist/vite/moonui-theme-plugin.js"
}
},
scripts: {
build: "tsup src/index.ts --format cjs,esm --dts",
dev: "tsup src/index.ts --format cjs,esm --dts --watch --sourcemap",
clean: "rm -rf dist",
lint: 'eslint "src/**/*.ts*"',
test: "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
prepublishOnly: "npm run clean && npm run build",
pub: "npm version patch && npm run build && npm publish --access=public",
"pub:minor": "npm version minor && npm run build && npm publish --access=public",
"pub:major": "npm version major && npm run build && npm publish --access=public"
},
keywords: [
"ui",
"components",
"cli",
"moonui"
],
author: "MoonUI",
license: "MIT",
dependencies: {
"@radix-ui/react-primitive": "^2.1.3",
"@types/jsonwebtoken": "^9.0.10",
"@types/node-fetch": "^2.6.12",
"@types/react-syntax-highlighter": "^15.5.13",
chalk: "^4.1.2",
"class-variance-authority": "^0.7.1",
clsx: "^2.1.1",
commander: "^10.0.1",
figlet: "^1.6.0",
"fs-extra": "^11.1.1",
jsonwebtoken: "^9.0.2",
"node-fetch": "^3.3.2",
open: "^8.4.2",
ora: "^5.4.1",
prompts: "^2.4.2",
"react-syntax-highlighter": "^15.6.1",
"tailwind-merge": "^3.3.1",
"ts-morph": "^18.0.0"
},
devDependencies: {
"@next/bundle-analyzer": "^15.4.6",
"@sentry/nextjs": "^10.3.0",
"@svgr/webpack": "^8.1.0",
"@types/figlet": "^1.7.0",
"@types/fs-extra": "^11.0.4",
"@types/node": "^18.16.0",
"@types/ora": "^3.1.0",
"@types/prompts": "^2.4.9",
autoprefixer: "^10.4.21",
eslint: "^8.39.0",
jest: "^29.5.0",
postcss: "^8.5.6",
tailwindcss: "^4.1.11",
tsup: "^6.7.0",
typescript: "^5.0.4"
}
};
}
});
// src/services/device.service.ts
var import_os, import_crypto, import_os2, DeviceService;
var init_device_service = __esm({
"src/services/device.service.ts"() {
"use strict";
import_os = __toESM(require("os"));
import_crypto = __toESM(require("crypto"));
import_os2 = require("os");
DeviceService = class {
/**
* Generate a unique device fingerprint based on system attributes
*/
static getDeviceFingerprint() {
const deviceInfo = this.getDeviceInfo();
const fingerprintData = [
deviceInfo.hostname,
deviceInfo.username,
deviceInfo.platform,
deviceInfo.arch,
deviceInfo.cpuModel,
deviceInfo.cpuCount.toString(),
deviceInfo.macAddress || "no-mac"
].join("|");
return import_crypto.default.createHash("sha256").update(fingerprintData).digest("hex");
}
/**
* Get detailed device information
*/
static getDeviceInfo() {
const cpus = import_os.default.cpus();
const macAddress = this.getMacAddress();
const packageJson = require_package();
const cliVersion = packageJson.version || "unknown";
return {
hostname: import_os.default.hostname(),
username: import_os.default.userInfo().username,
platform: import_os.default.platform(),
arch: import_os.default.arch(),
cpuModel: cpus[0]?.model || "unknown",
cpuCount: cpus.length,
totalMemory: import_os.default.totalmem(),
nodeVersion: process.version,
cliVersion,
macAddress
};
}
/**
* Get the primary MAC address of the device
*/
static getMacAddress() {
const interfaces = (0, import_os2.networkInterfaces)();
for (const name of Object.keys(interfaces)) {
const iface = interfaces[name];
if (!iface)
continue;
for (const details of iface) {
if (!details.internal && details.mac && details.mac !== "00:00:00:00:00:00") {
return details.mac;
}
}
}
for (const name of Object.keys(interfaces)) {
const iface = interfaces[name];
if (!iface)
continue;
for (const details of iface) {
if (details.mac && details.mac !== "00:00:00:00:00:00") {
return details.mac;
}
}
}
return void 0;
}
/**
* Validate device against API
*/
static async validateDevice(apiKey, deviceId) {
try {
const API_BASE3 = "https://moonui.dev";
const response = await fetch(`${API_BASE3}/api/device/validate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
"X-Device-ID": deviceId
},
body: JSON.stringify({
deviceId,
deviceInfo: this.getDeviceInfo()
})
});
return response.ok;
} catch (error) {
console.error("Device validation error:", error);
return false;
}
}
/**
* Register a new device
*/
static async registerDevice(apiKey) {
try {
const deviceId = this.getDeviceFingerprint();
const deviceInfo = this.getDeviceInfo();
const API_BASE3 = "https://moonui.dev";
const response = await fetch(`${API_BASE3}/api/device/register`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify({
deviceId,
deviceInfo
})
});
if (response.ok) {
return { success: true, deviceId };
} else {
const error = await response.json();
return { success: false, error: error.message || "Failed to register device" };
}
} catch (error) {
return {
success: false,
error: error.message || "Device registration failed"
};
}
}
/**
* List all registered devices for the user
*/
static async listDevices(apiKey) {
try {
const API_BASE3 = "https://moonui.dev";
const response = await fetch(`${API_BASE3}/api/device/list`, {
headers: {
"Authorization": `Bearer ${apiKey}`
}
});
if (response.ok) {
const data = await response.json();
return data.devices || [];
}
return [];
} catch (error) {
console.error("Failed to list devices:", error);
return [];
}
}
/**
* Revoke a device
*/
static async revokeDevice(apiKey, deviceId) {
try {
const API_BASE3 = "https://moonui.dev";
const response = await fetch(`${API_BASE3}/api/device/revoke`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify({ deviceId })
});
return response.ok;
} catch (error) {
console.error("Failed to revoke device:", error);
return false;
}
}
/**
* Get current device status
*/
static async getDeviceStatus(apiKey) {
try {
const deviceId = this.getDeviceFingerprint();
const API_BASE3 = "https://moonui.dev";
const response = await fetch(`${API_BASE3}/api/device/status`, {
headers: {
"Authorization": `Bearer ${apiKey}`,
"X-Device-ID": deviceId
}
});
if (response.ok) {
return await response.json();
}
return null;
} catch (error) {
console.error("Failed to get device status:", error);
return null;
}
}
};
}
});
// src/services/auth-service.ts
var auth_service_exports = {};
__export(auth_service_exports, {
AuthService: () => AuthService
});
var import_fs_extra4, import_path4, import_os3, import_crypto2, import_chalk2, API_BASE, CACHE_DURATION, REFRESH_THRESHOLD, AuthService;
var init_auth_service = __esm({
"src/services/auth-service.ts"() {
"use strict";
import_fs_extra4 = __toESM(require("fs-extra"));
import_path4 = __toESM(require("path"));
import_os3 = __toESM(require("os"));
import_crypto2 = __toESM(require("crypto"));
import_chalk2 = __toESM(require("chalk"));
init_device_service();
API_BASE = "https://moonui.dev";
CACHE_DURATION = 24 * 60 * 60 * 1e3;
REFRESH_THRESHOLD = 60 * 60 * 1e3;
AuthService = class {
constructor() {
const configDir = import_path4.default.join(import_os3.default.homedir(), ".moonui");
this.authPath = import_path4.default.join(configDir, "auth.encrypted");
this.encryptionKey = this.getEncryptionKey();
}
getDeviceFingerprint() {
return DeviceService.getDeviceFingerprint();
}
getEncryptionKey() {
return import_crypto2.default.createHash("sha256").update(this.getDeviceFingerprint()).digest();
}
encrypt(data) {
const iv = import_crypto2.default.randomBytes(16);
const cipher = import_crypto2.default.createCipheriv("aes-256-cbc", this.encryptionKey, iv);
let encrypted = cipher.update(data, "utf8", "hex");
encrypted += cipher.final("hex");
return iv.toString("hex") + ":" + encrypted;
}
decrypt(data) {
const parts = data.split(":");
const iv = Buffer.from(parts[0], "hex");
const encrypted = parts[1];
const decipher = import_crypto2.default.createDecipheriv("aes-256-cbc", this.encryptionKey, iv);
let decrypted = decipher.update(encrypted, "hex", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
generateSignature(token) {
const data = JSON.stringify({
accessToken: token.accessToken,
userId: token.user.id,
plan: token.user.plan,
expiresAt: token.expiresAt
});
return import_crypto2.default.createHmac("sha256", this.encryptionKey).update(data).digest("hex");
}
verifySignature(token) {
if (!token.signature)
return false;
const expectedSignature = this.generateSignature(token);
return token.signature === expectedSignature;
}
async saveAuth(token) {
try {
token.signature = this.generateSignature(token);
token.lastVerified = (/* @__PURE__ */ new Date()).toISOString();
const deviceFingerprint = DeviceService.getDeviceFingerprint();
token.deviceId = deviceFingerprint;
const cache = {
token,
deviceId: deviceFingerprint,
machineId: deviceFingerprint,
encryptedAt: (/* @__PURE__ */ new Date()).toISOString()
};
const encrypted = this.encrypt(JSON.stringify(cache));
const hmac = import_crypto2.default.createHmac("sha256", this.encryptionKey);
const [iv, encryptedData] = encrypted.split(":");
hmac.update(encryptedData);
const signature = hmac.digest("hex");
const finalData = `${iv}.${encryptedData}.${signature}`;
const configDir = import_path4.default.dirname(this.authPath);
await import_fs_extra4.default.ensureDir(configDir);
await import_fs_extra4.default.writeFile(this.authPath, finalData, "utf8");
await import_fs_extra4.default.chmod(this.authPath, 384);
} catch (error) {
throw new Error(`Failed to save auth: ${error.message}`);
}
}
async clearAuth() {
try {
if (await import_fs_extra4.default.pathExists(this.authPath)) {
await import_fs_extra4.default.remove(this.authPath);
}
} catch (error) {
throw new Error(`Failed to clear auth: ${error.message}`);
}
}
async getAuth() {
try {
if (!await import_fs_extra4.default.pathExists(this.authPath)) {
return null;
}
const fileContent = await import_fs_extra4.default.readFile(this.authPath, "utf8");
const parts = fileContent.split(".");
if (parts.length !== 3) {
return null;
}
const [iv, encryptedData, signature] = parts;
const hmac = import_crypto2.default.createHmac("sha256", this.encryptionKey);
hmac.update(encryptedData);
const computedSignature = hmac.digest("hex");
if (computedSignature !== signature) {
return null;
}
const encrypted = `${iv}:${encryptedData}`;
const decrypted = this.decrypt(encrypted);
const cache = JSON.parse(decrypted);
const currentDeviceId = DeviceService.getDeviceFingerprint();
if (cache.deviceId && cache.deviceId !== currentDeviceId) {
console.warn(import_chalk2.default.red("\u26A0\uFE0F This license is registered to a different device"));
console.warn(import_chalk2.default.yellow(" MoonUI Pro licenses are device-specific"));
console.warn(import_chalk2.default.yellow(" Please login again or contact support for multi-device access"));
await this.clearAuth();
return null;
}
if (!cache.token.deviceId) {
cache.token.deviceId = currentDeviceId;
}
if (!this.verifySignature(cache.token)) {
console.warn(import_chalk2.default.red("Auth token signature verification failed"));
await this.clearAuth();
return null;
}
const expiresAt = new Date(cache.token.expiresAt);
const now = /* @__PURE__ */ new Date();
if (expiresAt < now) {
if (cache.token.refreshToken) {
try {
const newToken = await this.refreshToken(cache.token.refreshToken);
await this.saveAuth(newToken);
return newToken;
} catch (error) {
return null;
}
}
return null;
}
const lastVerified = new Date(cache.token.lastVerified);
if (now.getTime() - lastVerified.getTime() > REFRESH_THRESHOLD) {
this.backgroundRefresh(cache.token);
}
return cache.token;
} catch (error) {
await this.clearAuth();
return null;
}
}
async backgroundRefresh(token) {
try {
const response = await fetch(`${API_BASE}/api/auth/verify`, {
method: "POST",
headers: {
"Authorization": `Bearer ${token.accessToken}`,
"Content-Type": "application/json"
}
});
if (response.ok) {
const data = await response.json();
token.user = data.user;
token.lastVerified = (/* @__PURE__ */ new Date()).toISOString();
token.signature = this.generateSignature(token);
await this.saveAuth(token);
}
} catch {
}
}
async refreshToken(refreshToken) {
const response = await fetch(`${API_BASE}/api/auth/refresh`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
refresh_token: refreshToken,
grant_type: "refresh_token"
})
});
if (!response.ok) {
throw new Error("Token refresh failed");
}
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresAt: data.expires_at,
user: data.user,
lastVerified: (/* @__PURE__ */ new Date()).toISOString()
};
}
async isAuthenticated(skipServerValidation = false) {
const auth = await this.getAuth();
if (!auth)
return false;
if (skipServerValidation) {
return true;
}
const isValid = await this.validateTokenWithServer(auth.accessToken);
if (!isValid) {
await this.clearAuth();
return false;
}
return true;
}
async requireAuth() {
const auth = await this.getAuth();
if (!auth) {
console.log(import_chalk2.default.yellow("\n\u{1F510} Authentication required"));
console.log(import_chalk2.default.gray("Please login to continue:"));
console.log(import_chalk2.default.cyan(" moonui login\n"));
process.exit(1);
}
const isValid = await this.validateTokenWithServer(auth.accessToken);
if (!isValid) {
console.log(import_chalk2.default.red("\n\u274C Your session has been revoked"));
console.log(import_chalk2.default.yellow("Please login again:"));
console.log(import_chalk2.default.cyan(" moonui login\n"));
await this.clearAuth();
process.exit(1);
}
return auth;
}
async validateTokenWithServer(token) {
try {
const deviceId = DeviceService.getDeviceFingerprint();
const response = await fetch(`${API_BASE}/api/device/validate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
"X-Device-ID": deviceId
},
body: JSON.stringify({ deviceId })
});
if (!response.ok) {
try {
const errorData = await response.json();
if (response.status === 401 && (errorData.error?.includes("revoked") || errorData.error?.includes("Device session revoked"))) {
return false;
}
} catch {
if (response.status === 401) {
return false;
}
}
}
return response.ok;
} catch (error) {
console.log(import_chalk2.default.gray("\n\u26A0\uFE0F Could not validate session (offline mode)"));
return true;
}
}
async checkProAccess(component) {
const auth = await this.getAuth();
if (!auth) {
return false;
}
if (auth.user.hasLifetimeAccess === true) {
return true;
}
if (auth.user.plan === "pro_lifetime" || auth.user.plan === "lifetime") {
return true;
}
if (component && this.isCriticalComponent(component)) {
try {
const response = await fetch(`${API_BASE}/api/license/verify`, {
method: "POST",
headers: {
"Authorization": `Bearer ${auth.accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
component,
plan: auth.user.plan
})
});
if (!response.ok) {
return false;
}
const data = await response.json();
if (data.user?.hasLifetimeAccess === true) {
return true;
}
return data.hasAccess && auth.user.plan !== "free";
} catch {
}
}
if (auth.user.hasProAccess === true) {
return true;
}
return auth.user.plan !== "free";
}
isCriticalComponent(component) {
const criticalComponents = [
"data-table",
"advanced-chart",
"dashboard",
"kanban",
"calendar"
];
return criticalComponents.includes(component);
}
async getUserInfo() {
const auth = await this.getAuth();
return auth?.user || null;
}
async makeAuthenticatedRequest(url, options = {}) {
const auth = await this.requireAuth();
const deviceId = DeviceService.getDeviceFingerprint();
return fetch(url, {
...options,
headers: {
...options.headers,
"Authorization": `Bearer ${auth.accessToken}`,
"X-Device-ID": deviceId,
"X-Machine-ID": deviceId
}
});
}
async validateDevice() {
try {
const auth = await this.getAuth();
if (!auth) {
return { valid: false, error: "Not authenticated" };
}
const deviceId = DeviceService.getDeviceFingerprint();
const deviceInfo = DeviceService.getDeviceInfo();
const response = await fetch(`${API_BASE}/api/device/validate`, {
method: "POST",
headers: {
"Authorization": `Bearer ${auth.accessToken}`,
"X-Device-ID": deviceId,
"Content-Type": "application/json"
},
body: JSON.stringify({ deviceInfo })
});
if (response.ok) {
const data = await response.json();
return { valid: data.valid };
} else {
const error = await response.json();
return { valid: false, error: error.error || "Device validation failed" };
}
} catch (error) {
return { valid: false, error: "Failed to validate device" };
}
}
async registerDevice() {
try {
const auth = await this.getAuth();
if (!auth) {
return { success: false, error: "Not authenticated" };
}
const deviceId = DeviceService.getDeviceFingerprint();
const deviceInfo = DeviceService.getDeviceInfo();
const response = await fetch(`${API_BASE}/api/device/register`, {
method: "POST",
headers: {
"Authorization": `Bearer ${auth.accessToken}`,
"X-Device-ID": deviceId,
"Content-Type": "application/json"
},
body: JSON.stringify({ deviceId, deviceInfo })
});
if (response.ok) {
const data = await response.json();
auth.deviceId = deviceId;
auth.deviceValidated = true;
await this.saveAuth(auth);
return { success: true };
} else {
const error = await response.json();
if (error.error === "Device limit exceeded") {
console.log(import_chalk2.default.red("\n\u274C Device limit exceeded"));
console.log(import_chalk2.default.yellow(` You have ${error.currentDevices}/${error.maxDevices} devices registered`));
console.log(import_chalk2.default.blue(" Manage your devices at: https://moonui.dev/dashboard/devices"));
}
return { success: false, error: error.error };
}
} catch (error) {
return { success: false, error: "Failed to register device" };
}
}
};
}
});
// src/index.ts
var import_commander3 = require("commander");
var import_chalk11 = __toESM(require("chalk"));
var import_figlet = __toESM(require("figlet"));
// src/commands/init.ts
var import_fs_extra = __toESM(require("fs-extra"));
var import_path = __toESM(require("path"));
var import_chalk = __toESM(require("chalk"));
var import_ora = __toESM(require("ora"));
var import_child_process = require("child_process");
async function initCommand(options = {}) {
const spinner = (0, import_ora.default)("Initializing MoonUI in your project...").start();
try {
const cwd = process.cwd();
const packageJsonPath = import_path.default.join(cwd, "package.json");
if (!import_fs_extra.default.existsSync(packageJsonPath)) {
spinner.fail(import_chalk.default.red("No package.json found in the current directory."));
console.log(import_chalk.default.yellow("Please run this command in a JavaScript/TypeScript project root."));
return;
}
const configFileName = "moonui.config.js";
const configPath = import_path.default.join(cwd, configFileName);
if (import_fs_extra.default.existsSync(configPath) && !options.force) {
spinner.fail(import_chalk.default.red(`${configFileName} already exists.`));
console.log(import_chalk.default.yellow(`Use --force to overwrite the existing configuration.`));
return;
}
const configContent = `module.exports = {
// MoonUI Theme Configuration
theme: {
// Option 1: Use a preset theme
preset: 'default', // 'default' | 'corporate' | 'creative' | 'nature' | 'minimal' | 'ocean'
// Option 2: Define custom theme (uncomment to use)
// custom: {
// colors: {
// background: "0 0% 100%",
// foreground: "222.2 84% 4.9%",
// primary: "222.2 47.4% 11.2%",
// "primary-foreground": "210 40% 98%",
// secondary: "210 40% 96.1%",
// "secondary-foreground": "222.2 47.4% 11.2%",
// accent: "210 40% 96.1%",
// "accent-foreground": "222.2 47.4% 11.2%",
// destructive: "0 84.2% 60.2%",
// "destructive-foreground": "210 40% 98%",
// muted: "210 40% 96.1%",
// "muted-foreground": "215.4 16.3% 46.9%",
// border: "214.3 31.8% 91.4%",
// input: "214.3 31.8% 91.4%",
// ring: "222.2 84% 4.9%",
// card: "0 0% 100%",
// "card-foreground": "222.2 84% 4.9%",
// popover: "0 0% 100%",
// "popover-foreground": "222.2 84% 4.9%",
// },
// darkMode: {
// background: "222.2 84% 4.9%",
// foreground: "210 40% 98%",
// primary: "210 40% 98%",
// "primary-foreground": "222.2 47.4% 11.2%",
// // ... other dark mode colors
// },
// radius: 0.5, // Border radius in rem
// },
},
// Tailwind CSS configuration
tailwind: {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
darkMode: 'class', // or 'media'
},
// Component default settings
components: {
// Configure default props and variants for components
Button: {
defaultVariant: 'default',
defaultSize: 'md',
},
Card: {
defaultVariant: 'default',
},
// Add more component defaults as needed
},
// Path settings
paths: {
// Where to output the components
components: './src/components/ui',
// Where to output the utilities
utils: './src/lib',
// Where to output the generated theme CSS
styles: './src/styles',
},
// Build settings
build: {
// Automatically generate theme CSS on build
generateThemeCSS: true,
// CSS output file name
themeCSSFile: 'moonui-theme.css',
},
}`;
import_fs_extra.default.writeFileSync(configPath, configContent);
const componentsDir = import_path.default.join(cwd, "src", "components", "ui");
const libDir = import_path.default.join(cwd, "src", "lib");
import_fs_extra.default.ensureDirSync(componentsDir);
import_fs_extra.default.ensureDirSync(libDir);
const utilsPath = import_path.default.join(libDir, "utils.ts");
const utilsContent = `import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
/**
* A utility function to merge Tailwind CSS classes conditionally
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}`;
import_fs_extra.default.writeFileSync(utilsPath, utilsContent);
spinner.text = "Installing required dependencies...";
const dependencies = [
"@moontra/moonui@latest",
// Her zaman en son versiyon
"clsx",
"tailwind-merge",
"class-variance-authority"
];
const devDependencies = [
"tailwindcss",
"postcss",
"autoprefixer"
];
try {
const hasYarnLock = import_fs_extra.default.existsSync(import_path.default.join(cwd, "yarn.lock"));
const packageManager = hasYarnLock ? "yarn" : "npm";
if (packageManager === "yarn") {
(0, import_child_process.execSync)(`yarn add ${dependencies.join(" ")}`, { stdio: "ignore" });
(0, import_child_process.execSync)(`yarn add -D ${devDependencies.join(" ")}`, { stdio: "ignore" });
} else {
(0, import_child_process.execSync)(`npm install --save ${dependencies.join(" ")}`, { stdio: "ignore" });
(0, import_child_process.execSync)(`npm install --save-dev ${devDependencies.join(" ")}`, { stdio: "ignore" });
}
} catch (error) {
spinner.warn(import_chalk.default.yellow("Failed to automatically install dependencies."));
console.log(import_chalk.default.yellow("Please install the following dependencies manually:"));
console.log(import_chalk.default.cyan(`
Dependencies:
- ${dependencies.join("\n- ")}`));
console.log(import_chalk.default.cyan(`
Dev Dependencies:
- ${devDependencies.join("\n- ")}`));
}
spinner.succeed(import_chalk.default.green("MoonUI initialized successfully!"));
console.log("\nNext steps:");
console.log(import_chalk.default.cyan("1. Add components: ") + "moonui add button card input");
console.log(import_chalk.default.cyan("2. Configure your theme: ") + "Edit moonui.config.js");
console.log(import_chalk.default.cyan("3. Check documentation: ") + "https://moonui.dev/docs");
} catch (error) {
spinner.fail(import_chalk.default.red("Failed to initialize MoonUI."));
console.error(error);
}
}
// src/commands/add.ts
var import_fs_extra7 = __toESM(require("fs-extra"));
var import_path7 = __toESM(require("path"));
var import_chalk5 = __toESM(require("chalk"));
var import_ora2 = __toESM(require("ora"));
var import_prompts2 = __toESM(require("prompts"));
var import_os4 = __toESM(require("os"));
// src/utils/components.ts
async function getComponentCategories() {
return [
{
name: "Form",
description: "Components for user input and forms",
components: [
{
id: "button",
name: "Button",
description: "A button component with various styles, sizes, and states",
dependencies: []
},
{
id: "input",
name: "Input",
description: "A text input component with validation and state handling",
dependencies: []
},
{
id: "checkbox",
name: "Checkbox",
description: "A checkbox input component for boolean selections",
dependencies: []
},
{
id: "switch",
name: "Switch",
description: "A toggle switch component for boolean state",
dependencies: []
},
{
id: "select",
name: "Select",
description: "A dropdown select component for choosing from options",
dependencies: []
},
{
id: "radio-group",
name: "RadioGroup",
description: "A group of radio buttons for selecting one option",
dependencies: ["radio"]
},
{
id: "textarea",
name: "Textarea",
description: "A multi-line text input area",
dependencies: []
},
{
id: "label",
name: "Label",
description: "A form label component",
dependencies: []
}
]
},
{
name: "Layout",
description: "Components for page layout and structure",
components: [
{
id: "card",
name: "Card",
description: "A container component with header, content, and footer sections",
dependencies: []
},
{
id: "dialog",
name: "Dialog",
description: "A modal dialog component for displaying content over the page",
dependencies: []
},
{
id: "accordion",
name: "Accordion",
description: "A collapsible content panel",
dependencies: []
},
{
id: "sheet",
name: "Sheet",
description: "A slide-out panel from any edge of the screen",
dependencies: []
},
{
id: "popover",
name: "Popover",
description: "A small overlay that appears on user interaction",
dependencies: []
},
{
id: "hover-card",
name: "HoverCard",
description: "A card that appears when hovering over a trigger element",
dependencies: []
},
{
id: "separator",
name: "Separator",
description: "A horizontal or vertical separator line",
dependencies: []
},
{
id: "aspect-ratio",
name: "AspectRatio",
description: "A component to maintain a specific aspect ratio",
dependencies: []
}
]
},
{
name: "Navigation",
description: "Components for navigation and menus",
components: [
{
id: "tabs",
name: "Tabs",
description: "A tabbed interface for switching between content sections",
dependencies: []
},
{
id: "breadcrumb",
name: "Breadcrumb",
description: "A breadcrumb navigation component",
dependencies: []
},
{
id: "dropdown-menu",
name: "DropdownMenu",
description: "A dropdown menu component for navigation or actions",
dependencies: []
},
{
id: "navigation-menu",
name: "NavigationMenu",
description: "A responsive navigation menu component",
dependencies: []
},
{
id: "menubar",
name: "Menubar",
description: "A horizontal menu bar component",
dependencies: []
},
{
id: "pagination",
name: "Pagination",
description: "A component for paginating through content",
dependencies: []
}
]
},
{
name: "Data Display",
description: "Components for displaying data",
components: [
{
id: "badge",
name: "Badge",
description: "A badge component for labels and counts",
dependencies: []
},
{
id: "table",
name: "Table",
description: "A table component for displaying data in rows and columns",
dependencies: []
},
{
id: "avatar",
name: "Avatar",
description: "An avatar component for user or entity representation",
dependencies: []
},
{
id: "progress",
name: "Progress",
description: "A progress indicator component",
dependencies: []
},
{
id: "skeleton",
name: "Skeleton",
description: "A placeholder loading state component",
dependencies: []
},
{
id: "alert",
name: "Alert",
description: "An alert component for important messages",
dependencies: []
},
{
id: "toast",
name: "Toast",
description: "A toast notification component",
dependencies: []
},
{
id: "tooltip",
name: "Tooltip",
description: "A tooltip component for displaying information on hover",
dependencies: []
}
]
},
{
name: "Premium",
description: "Premium components available with MoonUI Pro",
components: [
{
id: "data-table",
name: "DataTable",
description: "Advanced data table with sorting, filtering, and pagination",
dependencies: ["@tanstack/react-table"]
},
{
id: "advanced-chart",
name: "AdvancedChart",
description: "Interactive charts with multiple types and customization options",
dependencies: ["recharts"]
},
{
id: "file-upload",
name: "FileUpload",
description: "Advanced file upload component with drag-and-drop support",
dependencies: []
},
{
id: "rich-text-editor",
name: "RichTextEditor",
description: "Rich text editor with formatting toolbar and customization",
dependencies: []
},
{
id: "calendar",
name: "Calendar",
description: "Interactive calendar component with event management",
dependencies: []
},
{
id: "kanban",
name: "Kanban",
description: "Drag-and-drop kanban board for project management",
dependencies: []
},
{
id: "timeline",
name: "Timeline",
description: "Timeline component for displaying chronological events",
dependencies: []
},
{
id: "dashboard",
name: "Dashboard",
description: "Dashboard component with metrics and widgets",
dependencies: []
}
]
}
];
}
// src/utils/config.ts
var import_fs_extra2 = __toESM(require("fs-extra"));
var import_path2 = __toESM(require("path"));
async function getConfig(projectPath) {
try {
const configPath = import_path2.default.join(projectPath, "moonui.config.js");
if (!import_fs_extra2.default.existsSync(configPath)) {
return null;
}
return {
tailwind: {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
darkMode: "class"
},
theme: {
extend: {
colors: {
primary: "hsl(var(--primary))",
secondary: "hsl(var(--secondary))"
}
}
},
components: {
Button: {
defaultVariant: "default",
defaultSize: "md"
}
},
paths: {
components: "./src/components/ui",
utils: "./src/lib"
}
};
} catch (error) {
console.error("Error reading MoonUI config:", error);
return null;
}
}
// src/utils/license.ts
var PRO_COMPONENTS = [
{
id: "data-table",
name: "Data Table",
category: "data",
tier: "pro",
description: "Advanced data table with sorting, filtering, pagination, and export",
dependencies: ["@tanstack/react-table"],
tags: ["table", "data", "sorting", "filtering", "pagination"]
},
{
id: "advanced-chart",
name: "Advanced Chart",
category: "visualization",
tier: "pro",
description: "Interactive charts with multiple types and customization options",
dependencies: ["recharts"],
tags: ["chart", "visualization", "analytics", "graph"]
},
{
id: "dashboard",
name: "Dashboard",
category: "layout",
tier: "pro",
description: "Complete dashboard layout with widgets and responsive design",
dependencies: ["framer-motion"],
tags: ["dashboard", "layout", "widgets", "analytics"]
},
{
id: "file-upload",
name: "File Upload",
category: "input",
tier: "pro",
description: "Advanced file upload with drag-and-drop, preview, and progress",
dependencies: ["react-dropzone"],
tags: ["upload", "file", "drag-drop", "preview"]
},
{
id: "rich-text-editor",
name: "Rich Text Editor",
category: "input",
tier: "pro",
description: "WYSIWYG rich text editor with formatting and media support",
dependencies: ["@tiptap/react"],
tags: ["editor", "text", "wysiwyg", "formatting"]
},
{
id: "calendar",
name: "Calendar",
category: "input",
tier: "pro",
description: "Interactive calendar with event management and date selection",
dependencies: ["date-fns"],
tags: ["calendar", "date", "events", "scheduling"]
},
{
id: "kanban",
name: "Kanban Board",
category: "layout",
tier: "pro",
description: "Drag-and-drop kanban board for project management",
dependencies: ["@dnd-kit/core"],
tags: ["kanban", "board", "drag-drop", "project"]
},
{
id: "timeline",
name: "Timeline",
category: "visualization",
tier: "pro",
description: "Interactive timeline component for displaying chronological data",
dependencies: [],
tags: ["timeline", "chronological", "events", "history"]
}
];
function isProComponent(componentId) {
return PRO_COMPONENTS.some((comp) => comp.id === componentId);
}
// src/utils/registry-client.ts
var import_fs_extra5 = __toESM(require("fs-extra"));
var import_path5 = __toESM(require("path"));
var import_chalk3 = __toESM(require("chalk"));
// src/utils/component-source-reader.ts
var import_fs_extra3 = __toESM(require("fs-extra"));
var import_path3 = __toESM(require("path"));
function transformImportPaths(content, componentName) {
let transformed = content.replace(/from ["']\.\.\/\.\.\/lib\/utils["']/g, 'from "@/lib/utils"').replace(/from ["']\.\.\/ui\/([^"']+)["']/g, 'from "@/components/ui/$1"').replace(/from ["']\.\/([^"']+)["']/g, (match, component) => {
if (component.endsWith(".css") || component.endsWith(".tsx") || component.endsWith(".ts")) {
return match;
}
return `from "@/components/ui/${component}"`;
});
transformed = transformed.replace(/from ["'](@\/[^"']+)\.tsx["']/g, 'from "$1"');
return transformed;
}
async function readComponentFromPackage(componentName, isProComponent2 = false) {
try {
const packageName = isProComponent2 ? "@moontra/moonui-pro" : "@moontra/moonui";
let componentPath;
let content = null;
try {
const cwd = process.cwd();
const localPackagePath = isProComponent2 ? import_path3.default.join(cwd, "packages", "moonui-pro") : import_path3.default.join(cwd, "packages", "moonui");
let packageRoot;
if (import_fs_extra3.default.existsSync(import_path3.default.join(localPackagePath, "package.json"))) {
packageRoot = localPackagePath;
console.log(`\u2713 Using local development package: ${packageRoot}`);
} else {
const packagePath = require.resolve(packageName);
const packageDir = import_path3.default.dirname(packagePath);
packageRoot = packageDir;
while (!import_fs_extra3.default.existsSync(import_path3.default.join(packageRoot, "package.json")) && packageRoot !== "/") {
packageRoot = import_path3.default.dirname(packageRoot);
}
console.log(`\u2713 Using NPM package: ${packageRoot}`);
}
const srcPath = import_path3.default.join(packageRoot, "src", "components", "ui", `${componentName}.tsx`);
const srcProPath = import_path3.default.join(packageRoot, "src", "components", componentName, "index.tsx");
if (!isProComponent2 && import_fs_extra3.default.existsSync(srcPath)) {
componentPath = srcPath;
content = await import_fs_extra3.default.readFile(srcPath, "utf8");
console.log(`\u2713 Read component from source: ${srcPath}`);
} else if (isProComponent2 && import_fs_extra3.default.existsSync(srcProPath)) {
componentPath = srcProPath;
content = await import_fs_extra3.default.readFile(srcProPath, "utf8");
console.log(`\u2713 Read pro component from source: ${srcProPath}`);
} else {
const distPath = import_path3.default.join(packageRoot, "dist", "components", "ui", `${componentName}.mjs`);
const distProPath = import_path3.default.join(packageRoot, "dist", "components", componentName, "index.mjs");
if (!isProComponent2 && import_fs_extra3.default.existsSync(distPath)) {
console.log(`Warning: Reading from built file, source may be minified: ${distPath}`);
return null;
}
}
} catch (error) {
console.log(`Failed to resolve package: ${packageName}`, error);
return null;
}
if (!content) {
console.log(`Component source not found in package: ${componentName}`);
return null;
}
const transformedContent = transformImportPaths(content, componentName);
const dependencies = [];
const importRegex = /from ["']([^"']+)["']/g;
let match;
while ((match = importRegex.exec(transformedContent)) !== null) {
const importPath = match[1];
if (importPath.startsWith("@/components/ui/")) {
const componentName2 = importPath.replace("@/components/ui/", "");
dependencies.push(componentName2);
}
}
const componentData = {
id: componentName,
name: componentName.charAt(0).toUpperCase() + componentName.slice(1),
description: `${componentName} component from ${packageName}`,
dependencies: [...new Set(dependencies)],
// Remove duplicates
files: [{
name: `${componentName}.tsx`,
content: transformedContent
}],
category: isProComponent2 ? "pro" : "free"
};
return componentData;
} catch (error) {
console.log(`Failed to read component from package: ${componentName}`, error);
return null;
}
}
// src/utils/registry-client.ts
init_device_service();
var fetch2 = (url, options) => import("node-fetch").then(({ default: fetch4 }) => fetch4(url, options));
var RegistryClient = class {
constructor() {
this.cache = /* @__PURE__ */ new Map();
this.baseUrl = "https://moonui.dev/api/registry";
}
/**
* Fetch component from registry
*/
async fetchComponent(componentId, license) {
const cacheKey = `${componentId}-${license?.user?.plan || "free"}`;
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
try {
const isProComponent2 = this.isProComponent(componentId);
const isPattern = componentId.includes("/");
let endpoint;
let url;
if (isProComponent2 || isPattern) {
endpoint = `${this.baseUrl}/pro`;
const componentPath = isPattern ? componentId : `components/${componentId}`;
url = `${endpoint}?component=${encodeURIComponent(componentPath)}`;
} else {
endpoint = `${this.baseUrl}/free`;
url = `${endpoint}?component=${componentId}`;
}
const deviceId = DeviceService.getDeviceFingerprint();
const requestOptions = {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Device-ID": deviceId
}
};
if ((isProComponent2 || isPattern) && license) {
requestOptions.headers["Authorization"] = `Bearer ${license.accessToken}`;
}
const response = await fetch2(url, requestOptions);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
if (response.status === 401 && errorData.error?.includes("revoked")) {
console.error(import_chalk3.default.red("\n\u274C Your device has been revoked"));
console.log(import_chalk3.default.yellow("Please login again to continue:"));
console.log(import_chalk3.default.cyan(" moonui login\n"));
const { AuthService: AuthService2 } = await Promise.resolve().then(() => (init_auth_service(), auth