@baicie/zeus
Version:
The progressive JavaScript framework for building modern web applications.
916 lines (906 loc) • 25.9 kB
JavaScript
/**
* 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;
})({});