|
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)return void t.appendChild(document.createTextNode(e));if(Array.isArray(e))return void e.forEach((e=>this.render(e,t)));if("function"==typeof e.type){const n=e.type,o=this.getComponentInstance(n);if(n.__isMemoized){const n=o.lastProps;if(n&&this.deepEqual(n,e.props)&&o.lastResult)return void this.render(o.lastResult,t)}this.componentStack.push(o),o.currentHook=0,o.lastProps=e.props;const s=n(e.props);return o.lastResult=s,this.componentStack.pop(),void this.render(s,t)}if("string"!=typeof e.type)return void console.error("Invalid element type:",e.type);const n=document.createElement(e.type);this.applyProps(n,e.props),(e.props.children||[]).forEach((e=>this.render(e,n))),t.appendChild(n)}catch(e){console.error("Render error:",e)}}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);e.addEventListener(o,t[n])}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),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!t.__isMemoized||!e.lastProps||!this.deepEqual(e.lastProps,t.props)}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(){this.isBatchingUpdates||(this.isBatchingUpdates=!0,this.pendingUpdates.push((()=>{if(this.rootElement){const e=this.dirtyInstances||new Set;for(this.components.forEach(((t,n)=>{const o=this.getComponentInstance(t);e.has(o)&&(n.innerHTML="",this.renderComponent(t,n))})),this.dirtyInstances=new Set;this.pendingEffects.length>0;){this.pendingEffects.shift()()}}})),this.hasScheduledFlush||(this.hasScheduledFlush=!0,queueMicrotask((()=>{this.flushUpdates(),this.isBatchingUpdates=!1}))))}flushUpdates(){for(;this.pendingUpdates.length>0;){this.pendingUpdates.shift()()}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++,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}}
|