|
1
|
-
import stateHook from"./hooks/state.js";import effectHook from"./hooks/effect.js";import memoHook from"./hooks/memo.js";import callbackHook from"./hooks/callback.js";import reducerHook from"./hooks/reducer.js";import refHook from"./hooks/ref.js";import contextHook from"./hooks/context.js";class Olovav2{constructor(){this.rootElement=null,this.components=new WeakMap,this.componentStack=[],this.componentInstances=new WeakMap,this.pendingUpdates=new Set,this.pendingEffects=[],this.contextSubscriptions=new WeakMap,this.isBatchingUpdates=!1,this.hasScheduledFlush=!1,this.dirtyInstances=new Set,this.virtualDOM=null,this.errorBoundaries=new WeakMap,this.batchQueue=new Set,this.isProcessingBatch=!1,this.debugEnabled=!1}createElement(e,t,...n){return null==e?(console.error("Element type cannot be null or undefined"),null):"function"==typeof e||"string"==typeof e?{type:e,props:{...t,children:this.flattenChildren(n)}}:(console.error("Invalid element type:",e),null)}flattenChildren(e){const t=[],n=[...e];for(;n.length;){const e=n.pop();Array.isArray(e)?n.push(...e):t.push(e)}return t.reverse()}render(e,t){try{if(null==e)return;const n=this.createVirtualDOM(e);this.virtualDOM?this.updateDOM(t,this.virtualDOM,n):(t.innerHTML="",t.appendChild(this.createDOM(n))),this.virtualDOM=n}catch(e){console.error("Render error:",e)}}createVirtualDOM(e){if("string"==typeof e||"number"==typeof e)return e;if(Array.isArray(e))return e.map((e=>this.createVirtualDOM(e)));if("function"==typeof e.type){const t=e.type,n=this.getComponentInstance(t);this.componentStack.push(n),n.currentHook=0,n.lastProps=e.props;try{const o=t(e.props);return n.lastResult=o,this.createVirtualDOM(o)}finally{this.componentStack.pop()}}return"string"!=typeof e.type?(console.error("Invalid element type:",e.type),null):{type:e.type,props:e.props,children:(e.props.children||[]).map((e=>this.createVirtualDOM(e)))}}createDOM(e){if("string"==typeof e||"number"==typeof e)return document.createTextNode(e);if(Array.isArray(e)){const t=document.createDocumentFragment();return e.forEach((e=>t.appendChild(this.createDOM(e)))),t}const t=document.createElement(e.type);return this.applyProps(t,e.props),e.children.forEach((e=>t.appendChild(this.createDOM(e)))),t}updateDOM(e,t,n){if(!e)return void this.debug("Container is undefined in updateDOM");if(t===n)return;if("string"==typeof t||"number"==typeof t)return void(t!==n&&e.textContent!==String(n)&&(e.textContent=n));if(Array.isArray(t)&&Array.isArray(n))return void this.reconcileChildren(e,t,n);if(t?.type!==n?.type){const t=this.createDOM(n);return void e.parentNode?.replaceChild(t,e)}t?.props!==n?.props&&this.updateProps(e,t?.props||{},n?.props||{});const o=t?.children||[],r=n?.children||[];this.updateChildren(e,o,r)}reconcileChildren(e,t,n){const o=new Map,r=new Map;t.forEach(((e,t)=>{e?.props?.key&&o.set(e.props.key,t)})),n.forEach(((e,t)=>{e?.props?.key&&r.set(e.props.key,t)}));const s=[];n.forEach(((e,t)=>{const n=e?.props?.key;if(n&&o.has(n)){const e=o.get(n);e!==t&&s.push({from:e,to:t})}})),s.forEach((({from:t,to:n})=>{const o=e.childNodes[t],r=e.childNodes[n];e.insertBefore(o,r)})),this.updateChildNodes(e,t,n)}updateChildNodes(e,t,n){const o=Math.max(t.length,n.length);for(let r=0;r<o;r++){const o=e.childNodes[r];!o&&n[r]?e.appendChild(this.createDOM(n[r])):n[r]?this.updateDOM(o,t[r],n[r]):e.removeChild(o)}}updateProps(e,t,n){Object.keys(t).forEach((t=>{if(!(t in n))if(t.startsWith("on")){const n=t.toLowerCase().substring(2);e.removeEventListener(n,e[`_${n}`]),delete e[`_${n}`]}else"children"!==t&&(e[t]="")})),Object.keys(n).forEach((o=>{n[o]!==t[o]&&this.applyProp(e,o,n[o])}))}applyProp(e,t,n){if("ref"===t)n.current=e;else if(t.startsWith("on")){const o=t.toLowerCase().substring(2),r=e[`_${o}`];r&&e.removeEventListener(o,r);const s=n;e[`_${o}`]=s,e.addEventListener(o,s)}else"children"!==t&&("className"===t?e.setAttribute("class",n):e[t]=n)}updateChildren(e,t,n){const o=Math.max(t.length,n.length);for(let r=0;r<o;r++){const o=e.childNodes[r];!o&&n[r]?e.appendChild(this.createDOM(n[r])):n[r]?this.updateDOM(o,t[r],n[r]):e.removeChild(o)}}applyProps(e,t){Object.keys(t||{}).forEach((n=>{if("ref"===n&&t[n])t[n].current=e;else if(n.startsWith("on")){const o=n.toLowerCase().substring(2),r=e[`_${o}`];r&&e.removeEventListener(o,r);const s=e=>{t[n](e)};e[`_${o}`]=s,e.addEventListener(o,s)}else"children"!==n&&("className"===n?e.setAttribute("class",t[n]):e[n]=t[n])}))}getComponentInstance(e){return this.componentInstances.has(e)||this.componentInstances.set(e,{hooks:[],currentHook:0,effects:[],cleanups:new Map,pendingEffects:[],contextSubscriptions:new Set,lastProps:null,lastResult:null}),this.componentInstances.get(e)}renderComponent(e,t){const n=this.getComponentInstance(e);this.componentStack.push(n);try{n.currentHook=0;const o=e();n.lastResult=o;this.createVirtualDOM(o);return t&&this.render(o,t),n.pendingEffects.length>0&&(this.pendingEffects.push(...n.pendingEffects),n.pendingEffects=[]),o}finally{this.componentStack.pop()}}shouldComponentUpdate(e,t){return!t.__isMemoized||!e.lastProps||!this.deepEqual(e.lastProps,t.props)}getCurrentInstance(){return this.componentStack[this.componentStack.length-1]||null}createContext(e){const t={_currentValue:e,_defaultValue:e,_subscribers:new Set,_version:0,Provider:({value:e,children:n})=>{const o=this.getCurrentInstance(),r=o.currentHook++,s=o.hooks[r];return this.shallowEqual(s,e)||(t._currentValue=e,t._version++,t._subscribers.forEach((t=>{t.instance.hooks[t.hookIndex]=e,this.scheduleUpdate()}))),o.hooks[r]=e,n},Consumer:({children:e})=>{if("function"!=typeof e)throw new Error("Context.Consumer expects a function as a child");return e(t._currentValue)}};return t}scheduleUpdate(){this.isBatchingUpdates||(this.isBatchingUpdates=!0,queueMicrotask((()=>{if(this.rootElement){const e=this.components.get(this.rootElement);if(e){const t=this.createVirtualDOM(this.renderComponent(e));this.updateDOM(this.rootElement,this.virtualDOM,t),this.virtualDOM=t}}for(;this.pendingEffects.length>0;){this.pendingEffects.shift()()}this.isBatchingUpdates=!1})))}findComponentByInstance(e){for(const[t,n]of this.components.entries())if(this.componentInstances.get(n)===e)return n;return null}flushUpdates(){this.pendingUpdates.forEach((e=>e())),this.pendingUpdates.clear(),this.hasScheduledFlush=!1}mount(e,t){for(this.rootElement=t,this.components.set(t,e),this.renderComponent(e,t);this.pendingEffects.length>0;){this.pendingEffects.shift()()}}unmount(e){const t=this.components.get(e);if(t){const n=this.componentInstances.get(t);n&&(n.contextSubscriptions.forEach((e=>e())),n.contextSubscriptions.clear(),n.cleanups.forEach((e=>e())),n.cleanups.clear(),this.componentInstances.delete(t)),this.components.delete(e)}e.innerHTML=""}memo(e){const t=t=>{const n=this.getCurrentInstance();if(!n)return e(t);const o=n.currentHook++,r=n.hooks[o]||{props:null,result:null},s=!r.props||!this.deepEqual(t,r.props);if(!r.result||s){const r=e(t);return n.hooks[o]={props:t,result:r},r}return r.result};return t.__isMemoized=!0,t.__original=e,t}shallowEqual(e,t){if(e===t)return!0;if(!e||!t)return!1;if("object"!=typeof e||"object"!=typeof t)return e===t;const n=Object.keys(e),o=Object.keys(t);return n.length===o.length&&n.every((n=>t.hasOwnProperty(n)&&e[n]===t[n]))}deepEqual(e,t){if(e===t)return!0;if(!e||!t)return!1;if(typeof e!=typeof t)return!1;if("object"!=typeof e)return e===t;if(Array.isArray(e))return!(!Array.isArray(t)||e.length!==t.length)&&e.every(((e,n)=>this.deepEqual(e,t[n])));const n=Object.keys(e),o=Object.keys(t);return n.length===o.length&&n.every((n=>t.hasOwnProperty(n)&&this.deepEqual(e[n],t[n])))}findComponentElement(e){const t=document.querySelectorAll("*");return Array.from(t).find((t=>{const n=this.components.get(t);return n&&this.componentInstances.get(n)===e}))||null}setState(e){this.getCurrentInstance()&&this.scheduleUpdate()}setErrorBoundary(e,t){this.errorBoundaries.set(e,t)}handleError(e,t){let n=t;for(;n;){const t=this.errorBoundaries.get(n);if(t)return void t(e);n=this.findParentComponent(n)}console.error("Unhandled error:",e)}batchUpdates(e){const t=this.isBatchingUpdates;this.isBatchingUpdates=!0;try{e()}finally{t||(this.isBatchingUpdates=!1,this.flushBatchQueue())}}flushBatchQueue(){if(!this.isProcessingBatch){this.isProcessingBatch=!0;try{for(const e of this.batchQueue)e();this.batchQueue.clear()}finally{this.isProcessingBatch=!1}}}componentDidMount(e){e.onComponentDidMount&&e.onComponentDidMount()}componentWillUnmount(e){e.onComponentWillUnmount&&e.onComponentWillUnmount()}debug(...e){this.debugEnabled&&console.log("[Olova Debug]:",...e)}enableDebug(){this.debugEnabled=!0}disableDebug(){this.debugEnabled=!1}}const Olova=new Olovav2;export const h=Olova.createElement.bind(Olova);export const Fragment=e=>e?e.children:null;export const $state=e=>stateHook(Olova,e);export const $effect=(e,t)=>effectHook(Olova,e,t);export const $memo=(e,t)=>memoHook(Olova,e,t);export const $callback=(e,t)=>callbackHook(Olova,e,t);export const $reducer=(e,t)=>reducerHook(Olova,e,t);export const $ref=e=>refHook(Olova,e);export const $context=e=>contextHook(Olova,e);export const createContext=Olova.createContext.bind(Olova);export const memo=Olova.memo.bind(Olova);export default Olova;function isFunctionComponent(e){return"function"==typeof e}function renderComponent(e,t){try{return"function"==typeof e.type?e.type(e.props):"string"==typeof e?e:Array.isArray(e)?e.map((e=>renderComponent(e,t))):null==e?"":"object"==typeof e?e:String(e)}catch(e){return console.error("Error rendering component:",e),null}}export const enableDebug=Olova.enableDebug.bind(Olova);export const disableDebug=Olova.disableDebug.bind(Olova);export const setErrorBoundary=Olova.setErrorBoundary.bind(Olova);
|
|
1
|
+
import stateHook from"./hooks/state.js";import effectHook from"./hooks/effect.js";import memoHook from"./hooks/memo.js";import callbackHook from"./hooks/callback.js";import reducerHook from"./hooks/reducer.js";import refHook from"./hooks/ref.js";import contextHook from"./hooks/context.js";class Olovav2{constructor(){this.rootElement=null,this.components=new Map,this.componentStack=[],this.componentInstances=new Map,this.pendingUpdates=[],this.pendingEffects=[],this.contextSubscriptions=new WeakMap,this.isBatchingUpdates=!1,this.hasScheduledFlush=!1,this.dirtyInstances=null}createElement(e,t,...n){return null==e?(console.error("Element type cannot be null or undefined"),null):"function"==typeof e||"string"==typeof e?{type:e,props:{...t,children:n.flat()}}:(console.error("Invalid element type:",e),null)}render(e,t){try{if(null==e)return;if("string"==typeof e||"number"==typeof e){if(t.nodeType===Node.TEXT_NODE)t.textContent!==String(e)&&(t.textContent=String(e));else if(1===t.childNodes.length&&t.firstChild.nodeType===Node.TEXT_NODE)t.firstChild.textContent!==String(e)&&(t.firstChild.textContent=String(e));else{const n=document.createTextNode(String(e));t.innerHTML="",t.appendChild(n)}return}if(Array.isArray(e))return void this.reconcileChildren(t,e);if("function"==typeof e.type){const n=e.type,o=this.getComponentInstance(n);if(!n.__isMemoized||!o.lastProps||!this.shallowEqual(o.lastProps,e.props)){this.componentStack.push(o),o.currentHook=0,o.lastProps=e.props;const s=n(e.props);o.lastResult=s,this.componentStack.pop(),this.render(s,t)}else this.render(o.lastResult,t);return}if("string"==typeof e.type){let n;t.nodeType===Node.ELEMENT_NODE&&t.tagName.toLowerCase()===e.type?n=t:1===t.childNodes.length&&t.firstChild.nodeType===Node.ELEMENT_NODE&&t.firstChild.tagName.toLowerCase()===e.type?n=t.firstChild:(n=document.createElement(e.type),t.innerHTML="",t.appendChild(n)),this.applyProps(n,e.props);const o=e.props.children||[],s=Array.isArray(o)?o:[o];this.reconcileChildren(n,s)}}catch(e){console.error("Render error:",e)}}reconcileChildren(e,t){const n=Array.from(e.childNodes),o=Math.max(n.length,t.length);for(let s=0;s<o;s++)if(s>=t.length)e.removeChild(n[s]);else if(s>=n.length){const n="string"==typeof t[s]?.type?document.createElement(t[s].type):document.createTextNode("");e.appendChild(n),this.render(t[s],n)}else{const o=n[s],r=t[s];if("string"==typeof r?.type)if(o.nodeType===Node.ELEMENT_NODE&&o.tagName.toLowerCase()===r.type)this.render(r,o);else{const t=document.createElement(r.type);e.replaceChild(t,o),this.render(r,t)}else this.render(r,o)}}applyProps(e,t){const n=e._props||{},o=t||{};e._props=o,Object.keys(n).forEach((t=>{if(!(t in o)&&"children"!==t)if(t.startsWith("on")){const o=t.toLowerCase().substring(2);e.removeEventListener(o,n[t])}else"className"===t?e.removeAttribute("class"):e[t]=""})),Object.keys(o).forEach((t=>{if("ref"===t&&o[t])o[t].current=e;else if(t.startsWith("on")){const s=t.toLowerCase().substring(2);n[t]&&e.removeEventListener(s,n[t]),e.addEventListener(s,o[t])}else"children"!==t&&("className"===t?n[t]!==o[t]&&e.setAttribute("class",o[t]):n[t]!==o[t]&&(e[t]=o[t]))}))}getComponentInstance(e){return this.componentInstances.has(e)||this.componentInstances.set(e,{hooks:[],currentHook:0,effects:[],cleanups:new Map,pendingEffects:[],contextSubscriptions:new Set,lastProps:null,lastResult:null}),this.componentInstances.get(e)}renderComponent(e,t){const n=this.getComponentInstance(e);this.componentStack.push(n),n.currentHook=0;if(!n.lastResult||this.shouldComponentUpdate(n,e)){const o=e();n.lastResult=o,this.render(o,t),n.pendingEffects.length>0&&(this.pendingEffects.push(...n.pendingEffects),n.pendingEffects=[])}else this.render(n.lastResult,t);this.componentStack.pop()}shouldComponentUpdate(e,t){return!e.lastProps||!this.shallowEqual(e.lastProps,t)}getCurrentInstance(){return this.componentStack[this.componentStack.length-1]}createContext(e){const t={_currentValue:e,_defaultValue:e,_subscribers:new Set,_version:0,Provider:({value:e,children:n})=>{const o=this.getCurrentInstance(),s=o.currentHook++,r=o.hooks[s];return this.shallowEqual(r,e)||(t._currentValue=e,t._version++,t._subscribers.forEach((t=>{t.instance.hooks[t.hookIndex]=e,this.scheduleUpdate()}))),o.hooks[s]=e,n},Consumer:({children:e})=>{if("function"!=typeof e)throw new Error("Context.Consumer expects a function as a child");return e(t._currentValue)}};return t}scheduleUpdate(){if(!this.isBatchingUpdates){this.isBatchingUpdates=!0;const e=this.getCurrentInstance();if(e){this.dirtyInstances=new Set([e]);const t=this.componentStack[this.componentStack.length-2];t&&this.dirtyInstances.add(t)}this.hasScheduledFlush||(this.hasScheduledFlush=!0,queueMicrotask((()=>{this.flushUpdates(),this.isBatchingUpdates=!1})))}}flushUpdates(){try{if(this.rootElement&&this.dirtyInstances?.size>0)for(this.components.forEach(((e,t)=>{const n=this.getComponentInstance(e);this.dirtyInstances.has(n)&&this.renderComponent(e,t)}));this.pendingEffects.length>0;){this.pendingEffects.shift()()}}finally{this.dirtyInstances=new Set,this.hasScheduledFlush=!1,this.pendingUpdates=[]}}mount(e,t){for(this.rootElement=t,this.components.set(t,e),this.renderComponent(e,t);this.pendingEffects.length>0;){this.pendingEffects.shift()()}}unmount(e){const t=this.components.get(e);if(t){const n=this.componentInstances.get(t);n&&(n.contextSubscriptions.forEach((e=>e())),n.contextSubscriptions.clear(),n.cleanups.forEach((e=>e())),n.cleanups.clear(),this.componentInstances.delete(t)),this.components.delete(e)}e.innerHTML=""}memo(e){const t=t=>{const n=this.getCurrentInstance();if(!n)return e(t);const o=n.currentHook++,s=n.hooks[o]||{props:null,result:null},r=!s.props||!this.deepEqual(t,s.props);if(!s.result||r){const s=e(t);return n.hooks[o]={props:t,result:s},s}return s.result};return t.__isMemoized=!0,t.__original=e,t}shallowEqual(e,t){if(e===t)return!0;if(!e||!t)return!1;if("object"!=typeof e||"object"!=typeof t)return e===t;const n=Object.keys(e),o=Object.keys(t);return n.length===o.length&&n.every((n=>t.hasOwnProperty(n)&&e[n]===t[n]))}deepEqual(e,t){if(e===t)return!0;if(!e||!t)return!1;if(typeof e!=typeof t)return!1;if("object"!=typeof e)return e===t;if(Array.isArray(e))return!(!Array.isArray(t)||e.length!==t.length)&&e.every(((e,n)=>this.deepEqual(e,t[n])));const n=Object.keys(e),o=Object.keys(t);return n.length===o.length&&n.every((n=>t.hasOwnProperty(n)&&this.deepEqual(e[n],t[n])))}}const Olova=new Olovav2;export const h=Olova.createElement.bind(Olova);export const Fragment=e=>e?e.children:null;export const $state=e=>stateHook(Olova,e);export const $effect=(e,t)=>effectHook(Olova,e,t);export const $memo=(e,t)=>memoHook(Olova,e,t);export const $callback=(e,t)=>callbackHook(Olova,e,t);export const $reducer=(e,t)=>reducerHook(Olova,e,t);export const $ref=e=>refHook(Olova,e);export const $context=e=>contextHook(Olova,e);export const createContext=Olova.createContext.bind(Olova);export const memo=Olova.memo.bind(Olova);export default Olova;function isFunctionComponent(e){return"function"==typeof e}function renderComponent(e,t){try{return"function"==typeof e.type?e.type(e.props):"string"==typeof e?e:Array.isArray(e)?e.map((e=>renderComponent(e,t))):null==e?"":"object"==typeof e?e:String(e)}catch(e){return console.error("Error rendering component:",e),null}}
|