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
JavaScript
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
(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 */
(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 };