@meonode/ui
Version:
A structured approach to component composition, direct CSS-first prop styling, built-in theming, smart prop handling (including raw property pass-through), and dynamic children.
2 lines (1 loc) • 7.05 kB
JavaScript
import{Fragment as e,createElement as t}from"react";import{isValidElementType as s,isFragment as n}from"./helper/react-is.helper.js";import{getGlobalState as r,getComponentType as o,hasNoStyleTag as i,getElementTypeName as a}from"./helper/common.helper.js";import l from"./components/styled-renderer.client.js";import{__DEBUG__ as c}from"./constant/common.const.js";import{MountTrackerUtil as d}from"./util/mount-tracker.util.js";import h from"./components/meonode-unmounter.client.js";import{NavigationCacheManagerUtil as m}from"./util/navigation-cache-manager.util.js";import{NodeUtil as p}from"./util/node.util.js";import{compileServerEmotionClassName as u}from"./util/server-emotion.util.js";import{setActiveServerTheme as f,registerServerThemeVariables as g,getActiveServerTheme as y,replaceThemeTokensWithCssVars as C}from"./util/server-theme.util.js";import{ThemeUtil as v}from"./util/theme.util.js";const S=Symbol.for("@meonode/ui/BaseNode/elementCache"),b=Symbol.for("@meonode/ui/BaseNode/navigationStarted"),w=Symbol.for("@meonode/ui/BaseNode/renderContextPool"),k=Symbol.for("@meonode/ui/BaseNode/cacheCleanupRegistry");class j{static _idCounter=0;instanceId="m"+ ++j._idCounter;element;rawProps={};isBaseNode=!0;_props;_deps;stableKey;lastPropsObj;lastSignature;static get elementCache(){return r(S,()=>new Map)}static get _navigationStarted(){return r(b,()=>({value:!1})).value}static set _navigationStarted(e){r(b,()=>({value:!1})).value=e}static get renderContextPool(){return r(w,()=>[])}static acquireRenderContext(){const e=j.renderContextPool;return e.length>0?e.pop():{workStack:new Array(512),renderedElements:new Map}}static releaseRenderContext(e){j.renderContextPool.length<50&&e.workStack.length<2048&&(e.workStack.length=0,e.renderedElements.clear(),j.renderContextPool.push(e))}constructor(e,t={},n){if(!s(e)){const t=o(e);if(p.isNodeInstance(e))throw new Error("Invalid element type: MeoNode UI instance provided!");throw new Error(`Invalid element type: ${t} provided!`)}if(this.element=e,this.rawProps=t,this._deps=n,p.isServer&&p.providesServerTheme(e)){const e=t.theme;if(e&&"object"==typeof e&&"system"in e){const t=e;f(t),g(t)}}const{ref:r,children:i,...a}=t;this.stableKey=this._getStableKey(a),p.isServer||j._navigationStarted||(m.getInstance().start(),j._navigationStarted=!0)}get props(){return this._props||(this._props=p.processProps(this.rawProps,this.stableKey)),this._props}get dependencies(){return this._deps}_getStableKey({key:e,...t}){if(p.isServer)return;if(this.lastPropsObj===t)return this.lastSignature;this.lastPropsObj=t;const s=Object.keys(t),n=s.length;if(n>100){const e=p.extractCriticalProps(t,s);this.lastSignature=p.createPropSignature(this.element,e),c&&n>200&&console.warn(`MeoNode: Large props (${n} keys) on "${a(this.element)}". Consider splitting.`)}else this.lastSignature=p.createPropSignature(this.element,t);return null!=e?`${String(e)}:${this.lastSignature}`:this.lastSignature}static get cacheCleanupRegistry(){return r(k,()=>new FinalizationRegistry(e=>{const{cacheKey:t,instanceId:s}=e,n=j.elementCache.get(t);n?.instanceId===s&&j.elementCache.delete(t),d.isMounted(t)&&d.untrackMount(t)}))}render(r=!1){const o=p.shouldCacheElement(this)?j.elementCache.get(this.stableKey):void 0,a=p.shouldNodeUpdate(o?.prevDeps,this._deps,r);if(!a&&o?.renderedElement)return o.accessCount+=1,o.renderedElement;const c=!a,d=j.acquireRenderContext();let{workStack:m}=d;const{renderedElements:f}=d;let g=0;try{const r=e=>{if(e>m.length){const t=Math.max(e,m.length<<1),s=new Array(t);for(let e=0;e<g;e++)s[e]=m[e];m=s}};for(m[g++]={node:this,isProcessed:!1,blocked:c,theme:void 0};g>0;){const o=m[g-1];if(!o){g--;continue}const{node:a,isProcessed:c,blocked:d,theme:h}=o,S=(e,t)=>{const s=e.theme;return s&&"object"==typeof s&&"system"in s?s:t??y()};if(c){g--;const{children:r,key:o,css:c,nativeProps:d,disableEmotion:m,as:y,...b}=a.props,w=S(a.props,h);let k=a.element;null!=y&&s(y)&&(k=y);let P=[];if(r){const e=Array.isArray(r)?r:[r],t=e.length;P=new Array(t);for(let s=0;s<t;s++){const t=e[s];if(p.isNodeInstance(t)){const e=f.get(t);if(!e)throw new Error(`[MeoNode] Missing rendered element for child node: ${t.stableKey}`);P[s]=e}else P[s]=t}}const _=C({...b,key:o,...d});let E;if(a.element===e||n(a.element))E=t(a.element,{key:o},...P);else{const e=!m&&(c||!i(k))&&Object.keys(c||{}).length>0,s=p.isServer&&"string"!=typeof k,n=e&&s&&p.isClientReference(k);if(e&&!s||n){const e=p.isServer?C(c):c;E=t(l,{element:k,..._,css:e},...P)}else if(e&&s&&!p.acceptsServerCss(k)){const e=v.resolveObjWithTheme(C(c),w,{processFunctions:!0}),s=v.resolveDefaultStyle(e),n=u(s),r=[_.className,n].filter(Boolean).join(" ")||void 0,o=r?{..._,className:r}:_;E=t(k,o,...P)}else{const n=e&&(!s||p.acceptsServerCss(k))?{..._,css:c}:_;E=t(k,n,...P)}}if(a!==this&&p.shouldCacheElement(a)){const e=j.elementCache.get(a.stableKey);if(e)e.prevDeps=a._deps,e.renderedElement=E,e.accessCount+=1;else{const e={prevDeps:a._deps,renderedElement:E,nodeRef:new WeakRef(a),createdAt:Date.now(),accessCount:1,instanceId:a.instanceId};j.elementCache.set(a.stableKey,e),j.cacheCleanupRegistry.register(a,{cacheKey:a.stableKey,instanceId:a.instanceId},a)}}f.set(a,E)}else{o.isProcessed=!0;const e=a.props.children,t=S(a.props,h);if(e){const s=Array.isArray(e)?e:[e];let n=0;for(let e=0;e<s.length;e++)p.isNodeInstance(s[e])&&n++;r(g+n);for(let e=s.length-1;e>=0;e--){const n=s[e];if(!p.isNodeInstance(n))continue;const r=p.shouldCacheElement(n)?j.elementCache.get(n.stableKey):void 0,o=p.shouldNodeUpdate(r?.prevDeps,n._deps,d);if(!o&&r?.renderedElement){f.set(n,r.renderedElement);continue}const i=d||!o;m[g++]={node:n,isProcessed:!1,blocked:i,theme:t}}}}}let o=f.get(this);if(!p.isServer&&this.stableKey&&(o=t(h,{node:this},o)),p.shouldCacheElement(this)){const e=j.elementCache.get(this.stableKey);if(e)e.prevDeps=this._deps,e.renderedElement=o,e.accessCount+=1;else{const e={prevDeps:this._deps,renderedElement:o,nodeRef:new WeakRef(this),createdAt:Date.now(),accessCount:1,instanceId:this.instanceId};j.elementCache.set(this.stableKey,e),j.cacheCleanupRegistry.register(this,{cacheKey:this.stableKey,instanceId:this.instanceId},this)}}return o}finally{for(let e=0;e<g;e++)m[e]=null;j.releaseRenderContext({workStack:m,renderedElements:f})}}static clearCaches(){const e=Array.from(j.elementCache.keys());c&&console.log(`[MeoNode] clearCaches: Clearing ${e.length} entries`);for(const t of e){const e=j.elementCache.get(t);if(e){const s=e.nodeRef?.deref();if(s)try{j.cacheCleanupRegistry.unregister(s),s.lastSignature=void 0,s.lastPropsObj=void 0}catch{c&&console.warn(`[MeoNode] Could not unregister ${t} from FinalizationRegistry`)}}}j.elementCache.clear(),d.cleanup(),c&&console.log("[MeoNode] All caches cleared")}}function P(e,t={},s){return new j(e,t,s)}function _(e,t){const s=(s,n)=>P(e,{...t,...s},n);return s.element=e,s}function E(e,t){const s=(s,n,r)=>P(e,{...t,...n,children:s},r);return s.element=e,s}P.clearCaches=j.clearCaches;export{j as BaseNode,P as Node,E as createChildrenFirstNode,_ as createNode};