UNPKG

muspe-cli

Version:

MusPE Advanced Framework v2.1.3 - Mobile User-friendly Simple Progressive Engine with Enhanced CLI Tools, Specialized E-Commerce Templates, Material Design 3, Progressive Enhancement, Mobile Optimizations, Performance Analysis, and Enterprise-Grade Develo

1,384 lines (1,149 loc) 35 kB
const fs = require('fs-extra'); const path = require('path'); const chalk = require('chalk'); const inquirer = require('inquirer'); async function generateComponent(type, name, options) { const validTypes = ['component', 'page', 'service', 'hook', 'util']; if (!validTypes.includes(type)) { console.log(chalk.red(`Invalid type: ${type}`)); console.log(chalk.gray(`Valid types: ${validTypes.join(', ')}`)); return; } const projectRoot = findProjectRoot(); if (!projectRoot) { console.log(chalk.red('Not in a MusPE project directory')); return; } try { switch (type) { case 'component': await generateComponentFile(projectRoot, name, options); break; case 'page': await generatePageFile(projectRoot, name, options); break; case 'service': await generateServiceFile(projectRoot, name, options); break; case 'hook': await generateHookFile(projectRoot, name, options); break; case 'util': await generateUtilFile(projectRoot, name, options); break; } console.log(chalk.green(`✨ Generated ${type}: ${name}`)); } catch (error) { console.log(chalk.red(`Failed to generate ${type}: ${error.message}`)); } } function findProjectRoot() { let currentDir = process.cwd(); while (currentDir !== path.parse(currentDir).root) { const configPath = path.join(currentDir, 'muspe.config.js'); if (fs.existsSync(configPath)) { return currentDir; } currentDir = path.dirname(currentDir); } return null; } async function generateComponentFile(projectRoot, name, options) { const componentsDir = path.join(projectRoot, 'src', 'components'); const componentPath = options.path || componentsDir; const fileName = `${name}.js`; const filePath = path.join(componentPath, fileName); // Ensure directory exists await fs.ensureDir(componentPath); // Check if file already exists if (await fs.pathExists(filePath)) { const { overwrite } = await inquirer.prompt([ { type: 'confirm', name: 'overwrite', message: `Component ${name} already exists. Overwrite?`, default: false } ]); if (!overwrite) { console.log(chalk.yellow('Generation cancelled')); return; } } const componentContent = generateComponentContent(name, options); await fs.writeFile(filePath, componentContent); // Generate styles if requested if (options.style && options.style !== 'none') { const styleExt = options.style === 'scss' ? 'scss' : 'css'; const stylePath = path.join(componentPath, `${name}.${styleExt}`); const styleContent = generateComponentStyles(name, options.style); await fs.writeFile(stylePath, styleContent); console.log(chalk.blue(`📝 Style file: ${path.relative(projectRoot, stylePath)}`)); } console.log(chalk.blue(`📄 Component: ${path.relative(projectRoot, filePath)}`)); } async function generatePageFile(projectRoot, name, options) { const pagesDir = path.join(projectRoot, 'src', 'pages'); const pagePath = options.path || pagesDir; const fileName = `${name}.js`; const filePath = path.join(pagePath, fileName); await fs.ensureDir(pagePath); if (await fs.pathExists(filePath)) { const { overwrite } = await inquirer.prompt([ { type: 'confirm', name: 'overwrite', message: `Page ${name} already exists. Overwrite?`, default: false } ]); if (!overwrite) { console.log(chalk.yellow('Generation cancelled')); return; } } const pageContent = generatePageContent(name, options); await fs.writeFile(filePath, pageContent); // Generate corresponding HTML file const htmlPath = path.join(pagePath, `${name}.html`); const htmlContent = generatePageHTML(name); await fs.writeFile(htmlPath, htmlContent); console.log(chalk.blue(`📄 Page: ${path.relative(projectRoot, filePath)}`)); console.log(chalk.blue(`🌐 HTML: ${path.relative(projectRoot, htmlPath)}`)); } async function generateServiceFile(projectRoot, name, options) { const servicesDir = path.join(projectRoot, 'src', 'services'); const servicePath = options.path || servicesDir; const fileName = `${name}.js`; const filePath = path.join(servicePath, fileName); await fs.ensureDir(servicePath); const serviceContent = generateServiceContent(name); await fs.writeFile(filePath, serviceContent); console.log(chalk.blue(`🔧 Service: ${path.relative(projectRoot, filePath)}`)); } async function generateHookFile(projectRoot, name, options) { const hooksDir = path.join(projectRoot, 'src', 'hooks'); const hookPath = options.path || hooksDir; const fileName = `use${name}.js`; const filePath = path.join(hookPath, fileName); await fs.ensureDir(hookPath); const hookContent = generateHookContent(name); await fs.writeFile(filePath, hookContent); console.log(chalk.blue(`🪝 Hook: ${path.relative(projectRoot, filePath)}`)); } async function generateUtilFile(projectRoot, name, options) { const utilsDir = path.join(projectRoot, 'src', 'utils'); const utilPath = options.path || utilsDir; const fileName = `${name}.js`; const filePath = path.join(utilPath, fileName); await fs.ensureDir(utilPath); const utilContent = generateUtilContent(name); await fs.writeFile(filePath, utilContent); console.log(chalk.blue(`🛠️ Utility: ${path.relative(projectRoot, filePath)}`)); } function generateComponentContent(name, options) { return `// MusPE Component - ${name} class ${name} { constructor(options = {}) { this.options = { className: '${name.toLowerCase()}', ...options }; this.element = null; this.isMounted = false; } render() { // Use MusPE DOM utilities for faster development this.element = MusPE.dom.create('div', { class: this.options.className, html: \` <div class="${name.toLowerCase()}__container"> <h3 class="${name.toLowerCase()}__title">${name} Component</h3> <p class="${name.toLowerCase()}__description"> This is the ${name} component generated by MusPE with DOM utilities. </p> <button class="${name.toLowerCase()}__action-btn">Click me!</button> </div> \` }); this.setupEventListeners(); return this.element; } setupEventListeners() { if (!this.element) return; // Use MusPE DOM event handling MusPE.dom.on(this.element, 'click', \`.\${this.options.className}__action-btn\`, (e) => { this.handleAction(e); }); // Mobile touch events with DOM utilities if (MusPE.env.hasTouch) { MusPE.dom.on(this.element, 'touchstart', (e) => { MusPE.dom.addClass(this.element, \`\${this.options.className}--touched\`); }); MusPE.dom.on(this.element, 'touchend', (e) => { setTimeout(() => { MusPE.dom.removeClass(this.element, \`\${this.options.className}--touched\`); }, 150); }); } } handleAction(e) { console.log('${name} action triggered:', e); // Demo: Animate button click with DOM utilities const button = e.target; MusPE.dom.addClass(button, 'clicked'); // Demo: Make an API call using MusPE fetch this.demoAPICall(); // Demo: Check if running on mobile device with Cordova if (MusPE.cordova && MusPE.cordova.isCordova()) { console.log('Running on mobile device:', MusPE.cordova.getPlatform()); this.handleMobileAction(); } setTimeout(() => { MusPE.dom.removeClass(button, 'clicked'); }, 200); } handleMobileAction() { // Example of mobile-specific functionality if (MusPE.cordova) { const deviceInfo = MusPE.cordova.getDeviceInfo(); const networkInfo = MusPE.cordova.getNetworkInfo(); console.log('Device info:', deviceInfo); console.log('Network info:', networkInfo); // Example: Provide haptic feedback on supported devices if (navigator.vibrate) { navigator.vibrate(50); } // Example: Update UI based on network status if (networkInfo && !networkInfo.isOnline) { this.showOfflineMessage(); } } } showOfflineMessage() { const message = MusPE.dom.create('div', { class: 'offline-message', textContent: 'You are currently offline', style: 'background: #ff6b6b; color: white; padding: 10px; border-radius: 4px; margin: 10px 0;' }); MusPE.dom.prepend(this.element, message); // Remove message after 3 seconds setTimeout(() => { MusPE.dom.fadeOut(message, 300).then(() => { MusPE.dom.remove(message); }); }, 3000); } async demoAPICall() { try { // Example API call using MusPE fetch utilities console.log('Making demo API call...'); // This is just a demo - replace with your actual API endpoints const response = await MusPE.http.get('/api/demo', { timeout: 5000, cache: true }); console.log('API response:', response); } catch (error) { console.log('API call failed (expected for demo):', error.message); } } mount(container) { if (this.isMounted) return; const rendered = this.render(); const containerEl = typeof container === 'string' ? MusPE.dom.$(container) : container; if (containerEl) { MusPE.dom.append(containerEl, rendered); this.isMounted = true; this.onMount(); // Animate in MusPE.dom.fadeIn(rendered, 300); } } unmount() { if (!this.isMounted || !this.element) return; this.onUnmount(); // Animate out then remove MusPE.dom.fadeOut(this.element, 300).then(() => { MusPE.dom.remove(this.element); this.isMounted = false; }); } onMount() { // Override in subclasses console.log('${name} mounted'); MusPE.emit('component:mounted', { component: '${name}', element: this.element }); } onUnmount() { // Override in subclasses console.log('${name} unmounted'); MusPE.emit('component:unmounted', { component: '${name}' }); } update(newOptions = {}) { this.options = { ...this.options, ...newOptions }; if (this.isMounted) { const parent = this.element.parentNode; this.unmount(); setTimeout(() => this.mount(parent), 350); } } // Utility methods using MusPE DOM show() { if (this.element) MusPE.dom.show(this.element); return this; } hide() { if (this.element) MusPE.dom.hide(this.element); return this; } toggle() { if (this.element) MusPE.dom.toggle(this.element); return this; } addClass(className) { if (this.element) MusPE.dom.addClass(this.element, className); return this; } removeClass(className) { if (this.element) MusPE.dom.removeClass(this.element, className); return this; } destroy() { this.unmount(); this.element = null; this.options = null; } } // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = ${name}; } // Auto-register component globally if (typeof window !== 'undefined') { window.${name} = ${name}; // Register with MusPE if available if (window.MusPE) { MusPE.registerComponent('${name}', ${name}); } }`; } function generateComponentStyles(name, styleType) { const className = name.toLowerCase(); if (styleType === 'scss') { return `// ${name} Component Styles .${className} { $component-padding: 1rem; $component-radius: 0.5rem; padding: $component-padding; border-radius: $component-radius; background: white; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); &__container { display: flex; flex-direction: column; gap: 0.5rem; } &__title { font-size: 1.25rem; font-weight: 600; color: #1f2937; margin: 0; } &__description { color: #6b7280; margin: 0; } &__action-btn { background: #3b82f6; color: white; border: none; padding: 0.5rem 1rem; border-radius: 0.375rem; font-weight: 500; cursor: pointer; transition: all 0.2s ease; margin-top: 0.5rem; align-self: flex-start; &:hover { background: #2563eb; transform: translateY(-1px); } &.clicked { transform: scale(0.95); background: #1d4ed8; } } &--touched { transform: scale(0.98); transition: transform 0.1s ease; } // Mobile optimizations @media (max-width: 768px) { padding: $component-padding * 0.75; &__title { font-size: 1.125rem; } &__action-btn { padding: 0.75rem 1rem; font-size: 0.875rem; } } }`; } return `/* ${name} Component Styles */ .${className} { padding: 1rem; border-radius: 0.5rem; background: white; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .${className}__container { display: flex; flex-direction: column; gap: 0.5rem; } .${className}__title { font-size: 1.25rem; font-weight: 600; color: #1f2937; margin: 0; } .${className}__description { color: #6b7280; margin: 0; } .${className}__action-btn { background: #3b82f6; color: white; border: none; padding: 0.5rem 1rem; border-radius: 0.375rem; font-weight: 500; cursor: pointer; transition: all 0.2s ease; margin-top: 0.5rem; align-self: flex-start; } .${className}__action-btn:hover { background: #2563eb; transform: translateY(-1px); } .${className}__action-btn.clicked { transform: scale(0.95); background: #1d4ed8; } .${className}--touched { transform: scale(0.98); transition: transform 0.1s ease; } /* Mobile optimizations */ @media (max-width: 768px) { .${className} { padding: 0.75rem; } .${className}__title { font-size: 1.125rem; } .${className}__action-btn { padding: 0.75rem 1rem; font-size: 0.875rem; } }`; } function generatePageContent(name, options) { return `// MusPE Page - ${name} class ${name}Page { constructor(options = {}) { this.options = { title: '${name}', className: '${name.toLowerCase()}-page', ...options }; this.components = []; this.isActive = false; } render() { const pageElement = document.createElement('div'); pageElement.className = \`page \${this.options.className}\`; pageElement.innerHTML = \` <div class="page__header"> <h1 class="page__title">\${this.options.title}</h1> </div> <div class="page__content"> <div class="page__container"> <p>Welcome to the ${name} page!</p> <p>This page was generated by MusPE framework.</p> </div> </div> <div class="page__footer"> <button class="btn btn--primary" onclick="this.handleAction()"> Take Action </button> </div> \`; return pageElement; } async init() { // Initialize page components and data console.log(\`Initializing \${this.options.title} page\`); // Load any required data await this.loadData(); // Setup page-specific event listeners this.setupEventListeners(); } async loadData() { // Override in specific pages to load data return Promise.resolve(); } setupEventListeners() { // Page-specific event listeners document.addEventListener('scroll', this.handleScroll.bind(this)); // Mobile-specific events if ('ontouchstart' in window) { document.addEventListener('touchstart', this.handleTouchStart.bind(this)); } } handleScroll(e) { // Handle scroll events } handleTouchStart(e) { // Handle touch events for mobile } handleAction() { console.log('${name} action triggered'); // Add your action logic here } activate() { if (this.isActive) return; this.isActive = true; this.onActivate(); console.log(\`\${this.options.title} page activated\`); } deactivate() { if (!this.isActive) return; this.isActive = false; this.onDeactivate(); console.log(\`\${this.options.title} page deactivated\`); } onActivate() { // Override for page-specific activation logic document.title = this.options.title; } onDeactivate() { // Override for page-specific deactivation logic } destroy() { this.deactivate(); this.components.forEach(component => { if (component.destroy) { component.destroy(); } }); this.components = []; } } // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = ${name}Page; } // Auto-register page globally if (typeof window !== 'undefined') { window.${name}Page = ${name}Page; }`; } function generatePageHTML(name) { return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${name} - MusPE App</title> <link href="../styles/main.css" rel="stylesheet"> </head> <body> <div id="app"> <!-- Page content will be rendered here --> </div> <script src="../scripts/main.js"></script> <script src="./${name}.js"></script> <script> // Initialize page document.addEventListener('DOMContentLoaded', () => { const page = new ${name}Page(); const appContainer = document.getElementById('app'); page.init().then(() => { const pageElement = page.render(); appContainer.appendChild(pageElement); page.activate(); }); }); </script> </body> </html>`; } function generateServiceContent(name) { return `// MusPE Service - ${name} class ${name}Service { constructor() { this.baseURL = '/api'; this.cache = new Map(); this.headers = { 'Content-Type': 'application/json' }; } // Generic HTTP methods async get(endpoint, options = {}) { const url = \`\${this.baseURL}\${endpoint}\`; const cacheKey = \`GET_\${url}\`; // Check cache first if (options.cache !== false && this.cache.has(cacheKey)) { return this.cache.get(cacheKey); } try { const response = await fetch(url, { method: 'GET', headers: { ...this.headers, ...options.headers }, ...options }); if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } const data = await response.json(); // Cache successful responses if (options.cache !== false) { this.cache.set(cacheKey, data); // Auto-expire cache after 5 minutes setTimeout(() => { this.cache.delete(cacheKey); }, 5 * 60 * 1000); } return data; } catch (error) { console.error(\`${name}Service GET error:\`, error); throw error; } } async post(endpoint, data, options = {}) { const url = \`\${this.baseURL}\${endpoint}\`; try { const response = await fetch(url, { method: 'POST', headers: { ...this.headers, ...options.headers }, body: JSON.stringify(data), ...options }); if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } return await response.json(); } catch (error) { console.error(\`${name}Service POST error:\`, error); throw error; } } async put(endpoint, data, options = {}) { const url = \`\${this.baseURL}\${endpoint}\`; try { const response = await fetch(url, { method: 'PUT', headers: { ...this.headers, ...options.headers }, body: JSON.stringify(data), ...options }); if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } return await response.json(); } catch (error) { console.error(\`${name}Service PUT error:\`, error); throw error; } } async delete(endpoint, options = {}) { const url = \`\${this.baseURL}\${endpoint}\`; try { const response = await fetch(url, { method: 'DELETE', headers: { ...this.headers, ...options.headers }, ...options }); if (!response.ok) { throw new Error(\`HTTP error! status: \${response.status}\`); } return response.status === 204 ? {} : await response.json(); } catch (error) { console.error(\`${name}Service DELETE error:\`, error); throw error; } } // Service-specific methods (customize these) async getData() { return this.get('/${name.toLowerCase()}'); } async createData(data) { return this.post('/${name.toLowerCase()}', data); } async updateData(id, data) { return this.put(\`/${name.toLowerCase()}/\${id}\`, data); } async deleteData(id) { return this.delete(\`/${name.toLowerCase()}/\${id}\`); } // Utility methods setBaseURL(url) { this.baseURL = url; } setHeaders(headers) { this.headers = { ...this.headers, ...headers }; } clearCache() { this.cache.clear(); } } // Create singleton instance const ${name.toLowerCase()}Service = new ${name}Service(); // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = { ${name}Service, ${name.toLowerCase()}Service }; } // Auto-register service globally if (typeof window !== 'undefined') { window.${name}Service = ${name}Service; window.${name.toLowerCase()}Service = ${name.toLowerCase()}Service; }`; } function generateHookContent(name) { return `// MusPE Hook - use${name} function use${name}(initialValue = null) { let value = initialValue; let listeners = []; // State management const state = { get() { return value; }, set(newValue) { const oldValue = value; value = newValue; // Notify all listeners listeners.forEach(listener => { try { listener(newValue, oldValue); } catch (error) { console.error('Hook listener error:', error); } }); return value; }, update(updater) { if (typeof updater === 'function') { return this.set(updater(value)); } return this.set(updater); } }; // Subscription management const subscribe = (listener) => { if (typeof listener !== 'function') { throw new Error('Listener must be a function'); } listeners.push(listener); // Return unsubscribe function return () => { const index = listeners.indexOf(listener); if (index > -1) { listeners.splice(index, 1); } }; }; // Effect management (similar to React useEffect) const effect = (callback, dependencies = []) => { let cleanup; let prevDeps = []; const runEffect = () => { // Check if dependencies changed const depsChanged = dependencies.length === 0 || dependencies.some((dep, index) => dep !== prevDeps[index]); if (depsChanged) { // Cleanup previous effect if (cleanup && typeof cleanup === 'function') { cleanup(); } // Run new effect cleanup = callback(); prevDeps = [...dependencies]; } }; // Run initially runEffect(); // Subscribe to value changes const unsubscribe = subscribe(runEffect); return () => { if (cleanup && typeof cleanup === 'function') { cleanup(); } unsubscribe(); }; }; // Computed values const computed = (computeFn) => { let computedValue; let isComputed = false; const getComputed = () => { if (!isComputed) { computedValue = computeFn(value); isComputed = true; } return computedValue; }; // Invalidate computed value when state changes subscribe(() => { isComputed = false; }); return { get: getComputed, valueOf: getComputed }; }; // Async actions const async = { loading: false, error: null, execute: async (asyncFn) => { async.loading = true; async.error = null; try { const result = await asyncFn(value); state.set(result); return result; } catch (error) { async.error = error; console.error('Async hook error:', error); throw error; } finally { async.loading = false; } } }; // Storage integration const storage = { save: (key) => { try { localStorage.setItem(key, JSON.stringify(value)); } catch (error) { console.error('Storage save error:', error); } }, load: (key, defaultValue = initialValue) => { try { const stored = localStorage.getItem(key); if (stored !== null) { const parsed = JSON.parse(stored); state.set(parsed); return parsed; } } catch (error) { console.error('Storage load error:', error); } return defaultValue; }, clear: (key) => { try { localStorage.removeItem(key); } catch (error) { console.error('Storage clear error:', error); } } }; return { // Core state methods get: state.get, set: state.set, update: state.update, // Subscription subscribe, // Effects effect, // Computed computed, // Async async, // Storage storage, // Utilities reset: () => state.set(initialValue), destroy: () => { listeners = []; value = null; } }; } // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = use${name}; } // Auto-register hook globally if (typeof window !== 'undefined') { window.use${name} = use${name}; }`; } function generateUtilContent(name) { return `// MusPE Utility - ${name} const ${name}Utils = { // Device detection device: { isMobile: () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), isTablet: () => /(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(navigator.userAgent), isIOS: () => /iPad|iPhone|iPod/.test(navigator.userAgent), isAndroid: () => /Android/.test(navigator.userAgent), hasTouch: () => 'ontouchstart' in window || navigator.maxTouchPoints > 0 }, // DOM utilities dom: { select: (selector, context = document) => context.querySelector(selector), selectAll: (selector, context = document) => Array.from(context.querySelectorAll(selector)), create: (tag, attributes = {}, children = []) => { const element = document.createElement(tag); Object.entries(attributes).forEach(([key, value]) => { if (key === 'className') { element.className = value; } else if (key === 'style' && typeof value === 'object') { Object.assign(element.style, value); } else { element.setAttribute(key, value); } }); children.forEach(child => { if (typeof child === 'string') { element.appendChild(document.createTextNode(child)); } else if (child instanceof Node) { element.appendChild(child); } }); return element; }, remove: (element) => { if (element && element.parentNode) { element.parentNode.removeChild(element); } }, addClass: (element, className) => { if (element) element.classList.add(className); }, removeClass: (element, className) => { if (element) element.classList.remove(className); }, toggleClass: (element, className) => { if (element) element.classList.toggle(className); }, hasClass: (element, className) => { return element ? element.classList.contains(className) : false; } }, // Event utilities events: { on: (element, event, handler, options = {}) => { if (element) { element.addEventListener(event, handler, options); return () => element.removeEventListener(event, handler, options); } return () => {}; }, off: (element, event, handler, options = {}) => { if (element) { element.removeEventListener(event, handler, options); } }, once: (element, event, handler, options = {}) => { if (element) { const onceHandler = (e) => { handler(e); element.removeEventListener(event, onceHandler, options); }; element.addEventListener(event, onceHandler, options); return () => element.removeEventListener(event, onceHandler, options); } return () => {}; }, emit: (element, eventName, detail = {}) => { if (element) { const event = new CustomEvent(eventName, { detail }); element.dispatchEvent(event); } } }, // Animation utilities animation: { fadeIn: (element, duration = 300) => { if (!element) return Promise.resolve(); return new Promise(resolve => { element.style.opacity = '0'; element.style.transition = \`opacity \${duration}ms ease\`; requestAnimationFrame(() => { element.style.opacity = '1'; setTimeout(resolve, duration); }); }); }, fadeOut: (element, duration = 300) => { if (!element) return Promise.resolve(); return new Promise(resolve => { element.style.transition = \`opacity \${duration}ms ease\`; element.style.opacity = '0'; setTimeout(() => { element.style.display = 'none'; resolve(); }, duration); }); }, slideIn: (element, direction = 'down', duration = 300) => { if (!element) return Promise.resolve(); return new Promise(resolve => { const transforms = { down: 'translateY(-100%)', up: 'translateY(100%)', left: 'translateX(100%)', right: 'translateX(-100%)' }; element.style.transform = transforms[direction]; element.style.transition = \`transform \${duration}ms ease\`; requestAnimationFrame(() => { element.style.transform = 'translate(0, 0)'; setTimeout(resolve, duration); }); }); } }, // Storage utilities storage: { set: (key, value, type = 'local') => { try { const storage = type === 'session' ? sessionStorage : localStorage; storage.setItem(key, JSON.stringify(value)); return true; } catch (error) { console.error('Storage set error:', error); return false; } }, get: (key, defaultValue = null, type = 'local') => { try { const storage = type === 'session' ? sessionStorage : localStorage; const value = storage.getItem(key); return value !== null ? JSON.parse(value) : defaultValue; } catch (error) { console.error('Storage get error:', error); return defaultValue; } }, remove: (key, type = 'local') => { try { const storage = type === 'session' ? sessionStorage : localStorage; storage.removeItem(key); return true; } catch (error) { console.error('Storage remove error:', error); return false; } }, clear: (type = 'local') => { try { const storage = type === 'session' ? sessionStorage : localStorage; storage.clear(); return true; } catch (error) { console.error('Storage clear error:', error); return false; } } }, // Validation utilities validate: { email: (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email), phone: (phone) => /^[\+]?[1-9][\d]{0,15}$/.test(phone.replace(/\s/g, '')), url: (url) => { try { new URL(url); return true; } catch { return false; } }, required: (value) => value !== null && value !== undefined && value !== '', minLength: (value, min) => String(value).length >= min, maxLength: (value, max) => String(value).length <= max, pattern: (value, regex) => regex.test(String(value)) }, // Formatting utilities format: { currency: (amount, currency = 'USD', locale = 'en-US') => { return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount); }, date: (date, options = {}, locale = 'en-US') => { return new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long', day: 'numeric', ...options }).format(new Date(date)); }, number: (number, locale = 'en-US') => { return new Intl.NumberFormat(locale).format(number); }, capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(), kebabCase: (str) => str.replace(/[A-Z]/g, letter => \`-\${letter.toLowerCase()}\`), camelCase: (str) => str.replace(/-([a-z])/g, (g) => g[1].toUpperCase()) }, // Performance utilities performance: { debounce: (func, wait, immediate = false) => { let timeout; return function executedFunction(...args) { const later = () => { timeout = null; if (!immediate) func(...args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func(...args); }; }, throttle: (func, limit) => { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; }, memoize: (func) => { const cache = new Map(); return function(...args) { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } const result = func.apply(this, args); cache.set(key, result); return result; }; } } }; // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = ${name}Utils; } // Auto-register utility globally if (typeof window !== 'undefined') { window.${name}Utils = ${name}Utils; }`; } module.exports = { generateComponent };