UNPKG

@baicie/zeus

Version:

The progressive JavaScript framework for building modern web applications.

916 lines (906 loc) 25.9 kB
/** * zeus vundefined * (c) 2025 baicie * Released under the MIT License. **/ var ZeusJs = (function (exports) { 'use strict'; function initDev() { { { console.info( `You are running a development build of Zeus. Make sure to use the production build (*.prod.js) when deploying for production.` ); } } } var ReactiveFlags; (function (ReactiveFlags) { ReactiveFlags[ReactiveFlags["None"] = 0] = "None"; ReactiveFlags[ReactiveFlags["Mutable"] = 1] = "Mutable"; ReactiveFlags[ReactiveFlags["Watching"] = 2] = "Watching"; ReactiveFlags[ReactiveFlags["RecursedCheck"] = 4] = "RecursedCheck"; ReactiveFlags[ReactiveFlags["Recursed"] = 8] = "Recursed"; ReactiveFlags[ReactiveFlags["Dirty"] = 16] = "Dirty"; ReactiveFlags[ReactiveFlags["Pending"] = 32] = "Pending"; })(ReactiveFlags || (ReactiveFlags = {})); function createReactiveSystem({ update, notify, unwatched, }) { return { link, unlink, propagate, checkDirty, shallowPropagate, }; function link(dep, sub, version) { const prevDep = sub.depsTail; if (prevDep !== undefined && prevDep.dep === dep) { return; } const nextDep = prevDep !== undefined ? prevDep.nextDep : sub.deps; if (nextDep !== undefined && nextDep.dep === dep) { nextDep.version = version; sub.depsTail = nextDep; return; } const prevSub = dep.subsTail; if (prevSub !== undefined && prevSub.version === version && prevSub.sub === sub) { return; } const newLink = sub.depsTail = dep.subsTail = { version, dep, sub, prevDep, nextDep, prevSub, nextSub: undefined, }; if (nextDep !== undefined) { nextDep.prevDep = newLink; } if (prevDep !== undefined) { prevDep.nextDep = newLink; } else { sub.deps = newLink; } if (prevSub !== undefined) { prevSub.nextSub = newLink; } else { dep.subs = newLink; } } function unlink(link, sub = link.sub) { const dep = link.dep; const prevDep = link.prevDep; const nextDep = link.nextDep; const nextSub = link.nextSub; const prevSub = link.prevSub; if (nextDep !== undefined) { nextDep.prevDep = prevDep; } else { sub.depsTail = prevDep; } if (prevDep !== undefined) { prevDep.nextDep = nextDep; } else { sub.deps = nextDep; } if (nextSub !== undefined) { nextSub.prevSub = prevSub; } else { dep.subsTail = prevSub; } if (prevSub !== undefined) { prevSub.nextSub = nextSub; } else if ((dep.subs = nextSub) === undefined) { unwatched(dep); } return nextDep; } function propagate(link) { let next = link.nextSub; let stack; top: do { const sub = link.sub; let flags = sub.flags; if (!(flags & 60)) { sub.flags = flags | 32; } else if (!(flags & 12)) { flags = 0; } else if (!(flags & 4)) { sub.flags = (flags & -9) | 32; } else if (!(flags & 48) && isValidLink(link, sub)) { sub.flags = flags | 40; flags &= 1; } else { flags = 0; } if (flags & 2) { notify(sub); } if (flags & 1) { const subSubs = sub.subs; if (subSubs !== undefined) { const nextSub = (link = subSubs).nextSub; if (nextSub !== undefined) { stack = { value: next, prev: stack }; next = nextSub; } continue; } } if ((link = next) !== undefined) { next = link.nextSub; continue; } while (stack !== undefined) { link = stack.value; stack = stack.prev; if (link !== undefined) { next = link.nextSub; continue top; } } break; } while (true); } function checkDirty(link, sub) { let stack; let checkDepth = 0; let dirty = false; top: do { const dep = link.dep; const flags = dep.flags; if (sub.flags & 16) { dirty = true; } else if ((flags & 17) === 17) { if (update(dep)) { const subs = dep.subs; if (subs.nextSub !== undefined) { shallowPropagate(subs); } dirty = true; } } else if ((flags & 33) === 33) { if (link.nextSub !== undefined || link.prevSub !== undefined) { stack = { value: link, prev: stack }; } link = dep.deps; sub = dep; ++checkDepth; continue; } if (!dirty) { const nextDep = link.nextDep; if (nextDep !== undefined) { link = nextDep; continue; } } while (checkDepth--) { const firstSub = sub.subs; const hasMultipleSubs = firstSub.nextSub !== undefined; if (hasMultipleSubs) { link = stack.value; stack = stack.prev; } else { link = firstSub; } if (dirty) { if (update(sub)) { if (hasMultipleSubs) { shallowPropagate(firstSub); } sub = link.sub; continue; } dirty = false; } else { sub.flags &= -33; } sub = link.sub; const nextDep = link.nextDep; if (nextDep !== undefined) { link = nextDep; continue top; } } return dirty; } while (true); } function shallowPropagate(link) { do { const sub = link.sub; const flags = sub.flags; if ((flags & 48) === 32) { sub.flags = flags | 16; if (flags & 2) { notify(sub); } } } while ((link = link.nextSub) !== undefined); } function isValidLink(checkLink, sub) { let link = sub.depsTail; while (link !== undefined) { if (link === checkLink) { return true; } link = link.prevDep; } return false; } } const queuedEffects = []; const { link, unlink, propagate, checkDirty, shallowPropagate, } = createReactiveSystem({ update(node) { if (node.depsTail !== undefined) { return updateComputed(node); } else { return updateSignal(node); } }, notify, unwatched(node) { if (!(node.flags & 1)) { effectScopeOper.call(node); } else if (node.depsTail !== undefined) { node.depsTail = undefined; node.flags = 17; purgeDeps(node); } }, }); let cycle = 0; let batchDepth = 0; let notifyIndex = 0; let queuedEffectsLength = 0; let activeSub; function getActiveSub() { return activeSub; } function setActiveSub(sub) { const prevSub = activeSub; activeSub = sub; return prevSub; } function getBatchDepth() { return batchDepth; } function startBatch() { ++batchDepth; } function endBatch() { if (!--batchDepth) { flush(); } } function isSignal(fn) { return fn.name === 'bound ' + signalOper.name; } function isComputed(fn) { return fn.name === 'bound ' + computedOper.name; } function isEffect(fn) { return fn.name === 'bound ' + effectOper.name; } function isEffectScope(fn) { return fn.name === 'bound ' + effectScopeOper.name; } function signal(initialValue) { return signalOper.bind({ currentValue: initialValue, pendingValue: initialValue, subs: undefined, subsTail: undefined, flags: 1, }); } function computed(getter) { return computedOper.bind({ value: undefined, subs: undefined, subsTail: undefined, deps: undefined, depsTail: undefined, flags: 17, getter: getter, }); } function effect(fn) { const e = { fn, subs: undefined, subsTail: undefined, deps: undefined, depsTail: undefined, flags: 2, }; const prevSub = setActiveSub(e); if (prevSub !== undefined) { link(e, prevSub, 0); } try { e.fn(); } finally { activeSub = prevSub; } return effectOper.bind(e); } function effectScope(fn) { const e = { deps: undefined, depsTail: undefined, subs: undefined, subsTail: undefined, flags: 0, }; const prevSub = setActiveSub(e); if (prevSub !== undefined) { link(e, prevSub, 0); } try { fn(); } finally { activeSub = prevSub; } return effectScopeOper.bind(e); } function updateComputed(c) { ++cycle; c.depsTail = undefined; c.flags = 5; const prevSub = setActiveSub(c); try { const oldValue = c.value; return oldValue !== (c.value = c.getter(oldValue)); } finally { activeSub = prevSub; c.flags &= -5; purgeDeps(c); } } function updateSignal(s) { s.flags = 1; return s.currentValue !== (s.currentValue = s.pendingValue); } function notify(e) { const flags = e.flags; if (!(flags & 64)) { e.flags = flags | 64; const subs = e.subs; if (subs !== undefined) { notify(subs.sub); } else { queuedEffects[queuedEffectsLength++] = e; } } } function run(e, flags) { if (flags & 16 || (flags & 32 && (checkDirty(e.deps, e) || (e.flags = flags & -33, false)))) { ++cycle; e.depsTail = undefined; e.flags = 6; const prevSub = setActiveSub(e); try { e.fn(); } finally { activeSub = prevSub; e.flags &= -5; purgeDeps(e); } } else { let link = e.deps; while (link !== undefined) { const dep = link.dep; const depFlags = dep.flags; if (depFlags & 64) { run(dep, dep.flags = depFlags & -65); } link = link.nextDep; } } } function flush() { while (notifyIndex < queuedEffectsLength) { const effect = queuedEffects[notifyIndex]; queuedEffects[notifyIndex++] = undefined; run(effect, effect.flags &= -65); } notifyIndex = 0; queuedEffectsLength = 0; } function computedOper() { const flags = this.flags; if (flags & 16 || (flags & 32 && (checkDirty(this.deps, this) || (this.flags = flags & -33, false)))) { if (updateComputed(this)) { const subs = this.subs; if (subs !== undefined) { shallowPropagate(subs); } } } const sub = activeSub; if (sub !== undefined) { link(this, sub, cycle); } return this.value; } function signalOper(...value) { if (value.length) { if (this.pendingValue !== (this.pendingValue = value[0])) { this.flags = 17; const subs = this.subs; if (subs !== undefined) { propagate(subs); if (!batchDepth) { flush(); } } } } else { if (this.flags & 16) { if (updateSignal(this)) { const subs = this.subs; if (subs !== undefined) { shallowPropagate(subs); } } } let sub = activeSub; while (sub !== undefined) { if (sub.flags & 3) { link(this, sub, cycle); break; } sub = sub.subs?.sub; } return this.currentValue; } } function effectOper() { effectScopeOper.call(this); } function effectScopeOper() { this.depsTail = undefined; this.flags = 0; purgeDeps(this); const sub = this.subs; if (sub !== undefined) { unlink(sub); } } function purgeDeps(sub) { const depsTail = sub.depsTail; let dep = depsTail !== undefined ? depsTail.nextDep : sub.deps; while (dep !== undefined) { dep = unlink(dep, sub); } } function createElement(tag, attrs, ...children) { const element = document.createElement(tag); if (attrs) { setAttrs(element, attrs); } if (children.length > 0) { children.forEach((child) => { if (child != null) { element.appendChild(createNode(child)); } }); } return element; } function createText(text) { return document.createTextNode(String(text)); } function createFragment(...children) { const fragment = document.createDocumentFragment(); children.forEach((child) => { if (child != null) { fragment.appendChild(createNode(child)); } }); return fragment; } function createNode(node) { if (typeof node === "string" || typeof node === "number") { return createText(node); } return node; } function setAttrs(element, attrs) { Object.entries(attrs).forEach(([key, value]) => { if (value === null || value === void 0) { element.removeAttribute(key); } else if (typeof value === "boolean") { if (value) { element.setAttribute(key, ""); } else { element.removeAttribute(key); } } else { element.setAttribute(key, String(value)); } }); } function setAttr(element, name, value) { if (value === null || value === void 0) { element.removeAttribute(name); } else if (typeof value === "boolean") { if (value) { element.setAttribute(name, ""); } else { element.removeAttribute(name); } } else { element.setAttribute(name, String(value)); } } function getAttr(element, name) { return element.getAttribute(name); } function removeAttr(element, name) { element.removeAttribute(name); } function hasAttr(element, name) { return element.hasAttribute(name); } function setText(element, text) { element.textContent = String(text); } function insertText(element, text, index) { const textNode = createText(text); const children = element.childNodes; if (index === void 0 || index >= children.length) { element.appendChild(textNode); } else { element.insertBefore(textNode, children[index]); } } function mount(container, node) { container.appendChild(node); } function unmount(node) { if (node.parentNode) { node.parentNode.removeChild(node); } } function clear(container) { while (container.firstChild) { container.removeChild(container.firstChild); } } function replace(oldNode, newNode) { if (oldNode.parentNode) { oldNode.parentNode.replaceChild(newNode, oldNode); } } function insertBefore(referenceNode, newNode) { if (referenceNode.parentNode) { referenceNode.parentNode.insertBefore(newNode, referenceNode); } } function insertAfter(referenceNode, newNode) { if (referenceNode.parentNode) { const nextSibling = referenceNode.nextSibling; if (nextSibling) { referenceNode.parentNode.insertBefore(newNode, nextSibling); } else { referenceNode.parentNode.appendChild(newNode); } } } function addEventListener(element, event, handler, options) { element.addEventListener(event, handler, options); } function removeEventListener(element, event, handler, options) { element.removeEventListener(event, handler, options); } function delegateEvent(container, selector, event, handler, options) { const delegatedHandler = (e) => { const target = e.target; if (target && target.matches(selector)) { handler(e); } }; container.addEventListener(event, delegatedHandler, options); return () => { container.removeEventListener(event, delegatedHandler, options); }; } function useState(initialValue) { return signal(initialValue); } function useComputed(fn) { return computed(fn); } function useEffect(fn, deps) { return effect(fn); } function useRef(initialValue) { const refSignal = signal(initialValue); return { get current() { return refSignal(); }, set current(value) { refSignal(value); } }; } function useCallback(callback, deps) { const callbackSignal = useState(callback); useEffect(() => { callbackSignal(callback); }); return callbackSignal(); } function useMemo(factory, deps) { return useComputed(factory); } const lifecycle = { /** * 组件挂载时执行 */ onMounted: (fn) => { return useEffect(fn); }, /** * 组件卸载时执行 */ onUnmounted: (fn) => { return useEffect(() => { return fn; }); }, /** * 组件更新时执行 */ onUpdated: (fn) => { return useEffect(fn); } }; function useAttributes() { const attributesSignal = useState({}); return { get: (name) => attributesSignal()[name], set: (name, value) => { const current = attributesSignal(); attributesSignal(Object.assign({}, current, { [name]: value })); }, has: (name) => name in attributesSignal(), remove: (name) => { const current = attributesSignal(); const next = Object.assign({}, current); delete next[name]; attributesSignal(next); }, getAll: () => attributesSignal() }; } function useEvents() { const eventsSignal = useState({}); return { emit: (eventName, detail) => { const event = new CustomEvent(eventName, { detail }); const listeners = eventsSignal()[eventName] || []; listeners.forEach((listener) => listener(event)); }, on: (eventName, listener) => { const current = eventsSignal(); const existingListeners = current[eventName] || []; eventsSignal( Object.assign({}, current, { [eventName]: [...existingListeners, listener] }) ); }, off: (eventName, listener) => { const current = eventsSignal(); const existingListeners = current[eventName] || []; eventsSignal( Object.assign({}, current, { [eventName]: existingListeners.filter( (l) => l !== listener ) }) ); } }; } function createFunctionalWC(componentFn, options) { const { shadow = true, styles, observedAttributes = [], tagName } = options; class FunctionalWebComponent extends HTMLElement { constructor() { super(); this.renderer = null; this.context = { props: {}, container: shadow ? this.attachShadow({ mode: "open" }) : this, cleanup: [], mounted: false }; if (styles && shadow) { const styleElement = document.createElement("style"); styleElement.textContent = styles; this.context.container.appendChild(styleElement); } } static get observedAttributes() { return observedAttributes; } connectedCallback() { this.context.mounted = true; this.render(); } disconnectedCallback() { this.context.mounted = false; this.cleanup(); } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { this.context.props[name] = newValue; if (this.context.mounted) { this.render(); } } } render() { if (!this.context.mounted) return; this.cleanup(); this.renderer = () => { if (!this.context.mounted) return; if (this.context.container !== this) { this.context.container.innerHTML = ""; } else { while (this.context.container.firstChild) { this.context.container.removeChild( this.context.container.firstChild ); } } const result = componentFn(this.context.props); if (result instanceof Element) { this.context.container.appendChild(result); } else if (typeof result === "string") { this.context.container.innerHTML = result; } }; this.renderer(); } cleanup() { this.context.cleanup.forEach((fn) => fn()); this.context.cleanup = []; this.renderer = null; } // 提供 props 更新方法 updateProps(newProps) { Object.assign(this.context.props, newProps); if (this.context.mounted) { this.render(); } } } customElements.define(tagName, FunctionalWebComponent); return FunctionalWebComponent; } function defineFunctionalWC(tagName, componentFn, options = {}) { return createFunctionalWC( componentFn, Object.assign({}, options, { tagName }) ); } const hooks = { /** * 创建状态 */ useState: (initialValue) => { return signal(initialValue); }, /** * 创建计算属性 */ useComputed: (fn) => { return computed(fn); }, /** * 创建副作用 */ useEffect: (fn, deps) => { return effect(fn); }, /** * 获取当前组件的 props */ useProps: () => { throw new Error( "useProps must be used within a functional component context" ); } }; const version = "1.0.0"; { initDev(); } exports.addEventListener = addEventListener; exports.clear = clear; exports.computed = computed; exports.createElement = createElement; exports.createFragment = createFragment; exports.createFunctionalWC = createFunctionalWC; exports.createNode = createNode; exports.createText = createText; exports.defineFunctionalWC = defineFunctionalWC; exports.delegateEvent = delegateEvent; exports.effect = effect; exports.effectScope = effectScope; exports.endBatch = endBatch; exports.getActiveSub = getActiveSub; exports.getAttr = getAttr; exports.getBatchDepth = getBatchDepth; exports.hasAttr = hasAttr; exports.hooks = hooks; exports.insertAfter = insertAfter; exports.insertBefore = insertBefore; exports.insertText = insertText; exports.isComputed = isComputed; exports.isEffect = isEffect; exports.isEffectScope = isEffectScope; exports.isSignal = isSignal; exports.lifecycle = lifecycle; exports.mount = mount; exports.removeAttr = removeAttr; exports.removeEventListener = removeEventListener; exports.replace = replace; exports.setActiveSub = setActiveSub; exports.setAttr = setAttr; exports.setAttrs = setAttrs; exports.setText = setText; exports.signal = signal; exports.startBatch = startBatch; exports.unmount = unmount; exports.useAttributes = useAttributes; exports.useCallback = useCallback; exports.useComputed = useComputed; exports.useEffect = useEffect; exports.useEvents = useEvents; exports.useMemo = useMemo; exports.useRef = useRef; exports.useState = useState; exports.version = version; return exports; })({});