nexora
Version:
A lightweight, production-ready JavaScript library for building user interfaces, supporting JSX.
388 lines (387 loc) • 11.6 kB
JavaScript
var A = Object.defineProperty;
var I = (e, t, n) => t in e ? A(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
var y = (e, t, n) => I(e, typeof t != "symbol" ? t + "" : t, n);
function L(e, t) {
if (e === t)
return !0;
if (typeof e != "object" || e === null || typeof t != "object" || t === null)
return !1;
const n = Object.keys(e), r = Object.keys(t);
if (n.length !== r.length)
return !1;
for (const s of n)
if (!t.hasOwnProperty(s) || e[s] !== t[s])
return !1;
return !0;
}
function p(e) {
return e ? e._dom ? e._dom : e._rendered ? p(e._rendered) : null : null;
}
function K(e) {
let t = null;
return (n) => !n && !t ? e(n) : !t || !L(t, n) ? (t = { ...n }, e(n)) : null;
}
const C = /* @__PURE__ */ new Map(), E = /* @__PURE__ */ new WeakMap(), T = /* @__PURE__ */ new WeakMap();
function B(e) {
var o;
const t = h.currentComponentFn;
if (!t)
throw new Error("onInit must be called within a component");
C.has(t) || (C.set(t, []), E.set(t, /* @__PURE__ */ new Map())), (o = C.get(t)) == null || o.push(e);
const n = E.get(t);
if (!n.has(e)) {
const [i, c] = h.createState(null);
n.set(e, [i, c]), e().then((l) => {
c(l), h.triggerUpdate(t);
});
}
const [r, s] = n.get(e);
return [r(), s];
}
function v(e) {
const t = C.get(e);
return t && (h.currentComponentFn = e, t.forEach((n) => {
const r = Promise.resolve(n()).then((s) => (s !== void 0 && E.set(e, s), s));
T.set(e, r);
})), E.get(e);
}
function S(e, t, n = null) {
var c;
const r = e.type, s = e.props;
v(r);
const o = (c = s.children) == null ? void 0 : c[0], i = m(o, t, n);
return e._rendered = o, e._dom = i, i;
}
function R(e, t, n) {
Object.keys(t).forEach((r) => {
if (r !== "children" && !(r in n))
if (r.startsWith("on")) {
const s = r.toLowerCase().substring(2);
e.removeEventListener(s, t[r]);
} else
e[r] = "";
}), Object.keys(n).forEach((r) => {
if (r !== "children" && t[r] !== n[r])
if (r.startsWith("on")) {
const s = r.toLowerCase().substring(2);
t[r] && e.removeEventListener(s, t[r]), e.addEventListener(s, n[r]);
} else
e[r] = n[r];
});
}
function m(e, t, n = null) {
var s;
if (Array.isArray(e)) {
const o = document.createDocumentFragment();
return e.forEach((i) => m(i, o, null)), t.appendChild(o), o.firstChild;
}
if (e.type === "reactive-wrapper") {
const o = (s = e.props.children) == null ? void 0 : s[0];
if (!o) throw new Error("Reactive wrapper must have a child");
e.props._componentFn && (o.props = o.props || {}, o.props._componentFn = e.props._componentFn);
const i = m(o, t, n);
return e._dom = i, i;
}
if (typeof e.type == "function")
return S(e, t, n);
const r = e.type === "TEXT_ELEMENT" ? document.createTextNode(e.props.nodeValue) : document.createElement(e.type);
return r._vnode = e, e._dom = r, r instanceof HTMLElement && R(r, {}, e.props), r instanceof HTMLElement && e.props.children && e.props.children.forEach((o) => {
o && m(o, r);
}), n ? t.insertBefore(r, n) : t.appendChild(r), r;
}
function g(e, t, n) {
var s, o, i, c, l;
if (Array.isArray(t) && Array.isArray(n)) {
const u = Math.max(t.length, n.length);
for (let a = 0; a < u; a++)
if (a < t.length && a < n.length)
g(e, t[a], n[a]);
else if (a >= t.length)
m(n[a], e);
else {
const f = p(t[a]);
(s = f == null ? void 0 : f.parentNode) == null || s.removeChild(f);
}
return;
}
if (Array.isArray(t) || Array.isArray(n)) {
const u = Array.isArray(t) ? p(t[0]) : p(t);
if (u) {
const a = u.parentNode;
a && (Array.isArray(t) ? t.forEach((f) => {
const d = p(f);
d && a.removeChild(d);
}) : a.removeChild(u), m(n, e));
}
return;
}
if (t.type === "reactive-wrapper" && n.type === "reactive-wrapper") {
const u = (o = t.props.children) == null ? void 0 : o[0], a = (i = n.props.children) == null ? void 0 : i[0];
if (!u || !a) return;
if (t.props._renderKey !== n.props._renderKey) {
const f = p(t), d = m(a, e);
f && ((c = f.parentNode) == null || c.replaceChild(d, f)), n._dom = d;
return;
}
g(e, u, a), n._dom = t._dom;
return;
}
if (t.type !== n.type) {
const u = p(t), a = m(n, e);
u && ((l = u.parentNode) == null || l.replaceChild(a, u));
return;
}
if (n.type === "TEXT_ELEMENT") {
const u = p(t);
u && t.props.nodeValue !== n.props.nodeValue && (u.nodeValue = n.props.nodeValue), n._dom = u;
return;
}
const r = p(t);
r && (R(r, t.props, n.props), n._dom = r, b(r, t.props.children || [], n.props.children || []));
}
function b(e, t, n) {
const r = Math.max(t.length, n.length);
for (let s = 0; s < r; s++) {
const o = t[s], i = n[s];
if (!i && o) {
const c = p(o);
c && e.removeChild(c);
} else !o && i ? m(i, e) : o && i && g(e, o, i);
}
}
function w(e, t) {
const n = t._vnode;
n ? (g(t, n, e), n.props = {}, n.ref = null) : m(e, t), t._vnode = e;
}
class k {
constructor() {
y(this, "stateIndexes", /* @__PURE__ */ new WeakMap());
y(this, "currentComponentFn", null);
y(this, "rootElement", null);
y(this, "states", /* @__PURE__ */ new WeakMap());
}
/**
* Creates a state for the given component.
* @param initialValue - The initial value of the state.
* @returns A tuple containing the getter and setter for the state.
*/
createState(t) {
if (this.rootElement ?? (this.rootElement = document.getElementById("app")), !this.currentComponentFn)
throw new Error("createState must be called within a component");
const n = this.currentComponentFn;
this.stateIndexes.has(n) || this.stateIndexes.set(n, 0);
const r = this.stateIndexes.get(n);
this.stateIndexes.set(n, r + 1), this.states.has(n) || this.states.set(n, /* @__PURE__ */ new Map());
const s = this.states.get(n);
return s.has(r) || s.set(r, t), [() => {
const c = this.states.get(n);
return c == null ? void 0 : c.get(r);
}, (c) => {
const l = this.states.get(n);
if (!l) return;
const u = l.get(r), a = typeof c == "function" ? c(u) : c;
Object.is(u, a) || (l.set(r, a), queueMicrotask(() => {
const f = this.render(n);
if (this.rootElement) {
const d = this.findElementByComponent(this.rootElement, n);
d && d._vnode && w(f, d);
}
}));
}];
}
/**
* Checks if the given component function has state.
* @param componentFn - The component function to check.
* @returns True if the component function has state, false otherwise.
*/
hasState(t) {
return this.states.has(t);
}
/**
* Triggers an update for the given component function.
* @param componentFn - The component function to trigger an update for.
*/
triggerUpdate(t) {
queueMicrotask(() => {
const n = this.render(t);
if (this.rootElement) {
const r = this.findElementByComponent(this.rootElement, t);
r && r._vnode && w(n, r);
}
});
}
/**
* Finds the element by the given component function.
* @param root - The root element to search within.
* @param componentFn - The component function to search for.
* @returns The element that matches the component function.
*/
findElementByComponent(t, n) {
var s, o;
if (((o = (s = t._vnode) == null ? void 0 : s.props) == null ? void 0 : o._componentFn) === n)
return t;
const r = (i) => {
var c, l;
for (const u of Array.from(i.children)) {
if (((l = (c = u._vnode) == null ? void 0 : c.props) == null ? void 0 : l._componentFn) === n)
return u;
const a = r(u);
if (a) return a;
}
return null;
};
return r(t);
}
/**
* Renders the given component function.
* @param ComponentFn - The component function to render.
* @returns The VNode of the component.
*/
render(t, n) {
const r = this.currentComponentFn;
this.currentComponentFn = t;
try {
this.stateIndexes.set(t, 0);
const s = t(n || {}), o = Date.now();
return s && typeof s == "object" && Object.keys(s).forEach((i) => {
i.startsWith("_temp") && delete s[i];
}), {
type: "reactive-wrapper",
props: {
children: [s],
_componentFn: t,
_renderKey: o
},
key: null,
ref: null
};
} finally {
this.currentComponentFn = r;
}
}
}
const h = new k();
function F(e) {
return h.createState(e);
}
function _(e, t, ...n) {
if (typeof e == "function") {
const o = h.currentComponentFn;
h.currentComponentFn = e;
try {
h.stateIndexes.set(e, 0);
const i = e({ ...t, children: n });
return t && Object.keys(t).forEach((c) => {
c.startsWith("_temp") && delete t[c];
}), h.hasState(e) ? {
type: "reactive-wrapper",
props: {
children: [i],
_componentFn: e,
_renderKey: Date.now()
},
key: null,
ref: null
} : {
type: e,
props: {
...t,
children: [i],
_componentFn: e
},
key: null,
ref: null
};
} finally {
h.currentComponentFn = o;
}
}
const r = t ? { ...t } : {};
r.style && typeof r.style == "object" && (r.style = Object.entries(r.style).reduce((o, [i, c]) => {
const l = i.replace(/([A-Z])/g, "-$1").toLowerCase();
return o ? `${o};${l}:${c}` : `${l}:${c}`;
}, ""));
const s = n.map(
(o) => typeof o == "object" ? o : {
type: "TEXT_ELEMENT",
props: { nodeValue: o }
}
);
return {
type: e,
props: {
...r,
children: s
}
};
}
let x = {
getRouterContext: () => ({
currentPath: window.location.pathname,
params: {}
}),
setRouterContext: () => {
}
};
function O({ value: e, children: t }) {
return x = e, t;
}
function M() {
if (!x)
throw new Error("useRouter must be used within a Router");
return x;
}
function D(e) {
window.history.pushState({}, "", e), M().setRouterContext((n) => ({
...n,
currentPath: e
}));
}
function j({ to: e, children: t, className: n }) {
return /* @__PURE__ */ _("a", { href: e, onClick: (s) => {
s.preventDefault(), D(e);
}, className: n }, t);
}
function U({ path: e, component: t }) {
const { getRouterContext: n } = M(), { currentPath: r } = n();
return W(e, r) ? /* @__PURE__ */ _(t, null) : null;
}
function W(e, t) {
const n = e.replace(/:[^\s/]+/g, "([^/]+)").replace(/\*/g, ".*");
return new RegExp(`^${n}$`).test(t);
}
function q({ children: e }) {
console.log("Router");
const [t, n] = F({
currentPath: window.location.pathname,
params: {}
}), r = () => {
n((s) => ({
...s,
currentPath: window.location.pathname
}));
};
return typeof window < "u" && window.addEventListener("popstate", r), /* @__PURE__ */ _(O, { value: { getRouterContext: t, setRouterContext: n } }, /* @__PURE__ */ _("div", null, e));
}
export {
j as Link,
_ as Nexora,
U as Route,
q as Router,
O as RouterProvider,
F as createState,
b as diffChildren,
v as executeInitCallbacks,
p as findDom,
K as freeze,
m as mount,
S as mountComponent,
D as navigate,
B as onInit,
g as patch,
h as reactive,
w as render,
L as shallowEqual,
R as updateProps,
M as useRouter
};