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

310 lines (250 loc) 6.91 kB
// MusPE Reactive System - Advanced state management with fine-grained reactivity class MusPEReactivity { constructor() { this.targetStack = []; this.activeEffect = null; this.effectStack = []; this.shouldTrack = true; } // Create reactive proxy for objects reactive(target) { if (!this.isObject(target)) return target; if (target.__isMusPEReactive) return target; const proxy = new Proxy(target, { get: (target, key, receiver) => { if (key === '__isMusPEReactive') return true; const result = Reflect.get(target, key, receiver); // Track dependency this.track(target, key); // If result is object, make it reactive too if (this.isObject(result)) { return this.reactive(result); } return result; }, set: (target, key, value, receiver) => { const oldValue = target[key]; const result = Reflect.set(target, key, value, receiver); // Only trigger if value actually changed if (oldValue !== value) { this.trigger(target, key, value, oldValue); } return result; }, deleteProperty: (target, key) => { const hadKey = key in target; const result = Reflect.deleteProperty(target, key); if (hadKey) { this.trigger(target, key, undefined, target[key]); } return result; } }); return proxy; } // Create reactive reference for primitives ref(value) { return { __isMusPERef: true, _value: value, get value() { this.track(this, 'value'); return this._value; }, set value(newValue) { const oldValue = this._value; this._value = newValue; if (oldValue !== newValue) { this.trigger(this, 'value', newValue, oldValue); } } }; } // Computed properties with caching computed(getter) { let value; let dirty = true; const computedRef = { __isMusPEComputed: true, get value() { if (dirty) { value = this.runWithEffect(getter); dirty = false; } return value; } }; // Track computed as effect this.effect(() => { dirty = true; this.trigger(computedRef, 'value'); }); return computedRef; } // Effect system for reactive side effects effect(fn, options = {}) { const effect = { fn, options, deps: new Set(), active: true, run: () => { if (!effect.active) return; this.cleanup(effect); const prevEffect = this.activeEffect; this.activeEffect = effect; this.effectStack.push(effect); try { return fn(); } finally { this.effectStack.pop(); this.activeEffect = prevEffect; } }, stop: () => { this.cleanup(effect); effect.active = false; } }; if (!options.lazy) { effect.run(); } return effect; } // Watch for changes with deep watching support watch(source, callback, options = {}) { const { immediate = false, deep = false } = options; let getter; let oldValue; if (typeof source === 'function') { getter = source; } else if (source.__isMusPERef) { getter = () => source.value; } else if (this.isObject(source)) { getter = () => this.traverse(source); } else { getter = () => source; } if (immediate) { oldValue = getter(); callback(oldValue, undefined); } return this.effect(() => { const newValue = getter(); if (newValue !== oldValue || deep) { callback(newValue, oldValue); oldValue = newValue; } }); } // Track dependencies track(target, key) { if (!this.shouldTrack || !this.activeEffect) return; let depsMap = this.targetMap.get(target); if (!depsMap) { this.targetMap.set(target, (depsMap = new Map())); } let dep = depsMap.get(key); if (!dep) { depsMap.set(key, (dep = new Set())); } if (!dep.has(this.activeEffect)) { dep.add(this.activeEffect); this.activeEffect.deps.add(dep); } } // Trigger effects trigger(target, key, newValue, oldValue) { const depsMap = this.targetMap.get(target); if (!depsMap) return; const dep = depsMap.get(key); if (!dep) return; const effects = new Set(); dep.forEach(effect => { if (effect !== this.activeEffect) { effects.add(effect); } }); effects.forEach(effect => { if (effect.options.scheduler) { effect.options.scheduler(effect); } else { effect.run(); } }); } // Cleanup effect dependencies cleanup(effect) { effect.deps.forEach(dep => { dep.delete(effect); }); effect.deps.clear(); } // Traverse object deeply for watching traverse(obj, seen = new Set()) { if (!this.isObject(obj) || seen.has(obj)) return obj; seen.add(obj); for (const key in obj) { this.traverse(obj[key], seen); } return obj; } // Utility methods isObject(val) { return val !== null && typeof val === 'object'; } runWithEffect(fn) { const prevShouldTrack = this.shouldTrack; this.shouldTrack = false; try { return fn(); } finally { this.shouldTrack = prevShouldTrack; } } // Stop all tracking temporarily pauseTracking() { this.shouldTrack = false; } enableTracking() { this.shouldTrack = true; } // Reset tracking resetTracking() { this.shouldTrack = true; this.activeEffect = null; this.effectStack = []; this.targetMap = new WeakMap(); } } // Initialize global reactivity system MusPEReactivity.prototype.targetMap = new WeakMap(); // Create global instance const reactivity = new MusPEReactivity(); // Export reactive utilities const reactive = (target) => reactivity.reactive(target); const ref = (value) => reactivity.ref(value); const computed = (getter) => reactivity.computed(getter); const effect = (fn, options) => reactivity.effect(fn, options); const watch = (source, callback, options) => reactivity.watch(source, callback, options); // Export for module usage if (typeof module !== 'undefined' && module.exports) { module.exports = { MusPEReactivity, reactive, ref, computed, effect, watch, reactivity }; } // Make available globally if (typeof window !== 'undefined') { window.MusPEReactivity = MusPEReactivity; window.reactive = reactive; window.ref = ref; window.computed = computed; window.effect = effect; window.watch = watch; }