@meonode/ui 0.4.9 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/dist/components/meonode-unmounter.client.d.ts.map +1 -1
- package/dist/components/meonode-unmounter.client.js +1 -1
- package/dist/core.node.d.ts +3 -0
- package/dist/core.node.d.ts.map +1 -1
- package/dist/core.node.js +28 -23
- package/dist/util/node.util.d.ts.map +1 -1
- package/dist/util/node.util.js +11 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.4.10
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
- **unmounter**: prevent redundant FinalizationRegistry callbacks ([`59f5adf`](https://github.com/l7aromeo/meonode-ui/commit/59f5adf2f553aa49a88d1b44366b004d829ca107))
|
|
9
|
+
- Explicitly unregister node from cacheCleanupRegistry on unmount
|
|
10
|
+
- Prevents double cleanup when node is both unmounted and GC'd
|
|
11
|
+
- Improves cleanup efficiency and reduces unnecessary registry callbacks
|
|
12
|
+
|
|
13
|
+
### Perf
|
|
14
|
+
- **util**: optimize prop processing and child handling ([`be26488`](https://github.com/l7aromeo/meonode-ui/commit/be26488e304629dd13851dfcaa7fedf43ad8b5c3))
|
|
15
|
+
- Conditional sort: only sort keys if length > 1 (eliminates unnecessary sorts)
|
|
16
|
+
- Replace for...in with Object.keys() iteration for better performance and safety
|
|
17
|
+
- Add fast path for single child processing (non-array or 1-element array)
|
|
18
|
+
- Avoid unnecessary array operations for common single-child case
|
|
19
|
+
- Performance impact:
|
|
20
|
+
- Reduced CPU overhead for small prop objects
|
|
21
|
+
- Faster iteration with Object.keys() vs for...in
|
|
22
|
+
- Eliminates array map() call for single child components
|
|
23
|
+
|
|
24
|
+
- **core**: add exception safety and optimize render method ([`5aad000`](https://github.com/l7aromeo/meonode-ui/commit/5aad000335ff29f078a9d40192d5a70fe9b61d12))
|
|
25
|
+
- Wrap render logic in try-finally to ensure renderContext is always released
|
|
26
|
+
- Null out workStack slots before releasing to help GC
|
|
27
|
+
- Pre-allocate finalChildren array to avoid resizing during iteration
|
|
28
|
+
- Replace non-null assertion with explicit error handling for better debugging
|
|
29
|
+
- Add object pooling for workStack and renderedElements (reduces GC pressure)
|
|
30
|
+
- Pool capped at 50 contexts with 2048-item limit to prevent memory issues
|
|
31
|
+
- Exception safety ensures cleanup even if rendering throws
|
|
32
|
+
|
|
33
|
+
### Test
|
|
34
|
+
- **perf**: add React comparison tests with memory tracking ([`bc66d54`](https://github.com/l7aromeo/meonode-ui/commit/bc66d540c4ffa4ad083322dbdbc201f652ea5314))
|
|
35
|
+
- Add comprehensive performance comparison between React.createElement and MeoNode
|
|
36
|
+
- Test both flat (10k nodes) and nested (5k nodes) structures
|
|
37
|
+
- Include memory usage measurements (initial + after 100 updates)
|
|
38
|
+
- Reduce nested nodes from 10k to 5k to prevent stack overflow with StyledRenderer
|
|
39
|
+
- Add table output with time and memory columns for clear comparison
|
|
40
|
+
- Add GC availability warning for accurate measurements
|
|
41
|
+
- Provides detailed performance and memory behavior insights during re-renders
|
|
42
|
+
- Avoids stack overflow in deeply nested test scenarios
|
|
43
|
+
|
|
44
|
+
- **performance**: enhance performance metrics report formatting and adjust thresholds ([`6f3abe4`](https://github.com/l7aromeo/meonode-ui/commit/6f3abe4442938aa6cd414341fdf2aba25a9ece58))
|
|
45
|
+
- Improve table formatting for performance metrics
|
|
46
|
+
- Adjust test thresholds based on optimization results
|
|
47
|
+
- Enhanced reporting for better visibility into performance characteristics
|
|
48
|
+
|
|
5
49
|
## [0.4.9] - 2025-11-19
|
|
6
50
|
|
|
7
51
|
### Feat
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meonode-unmounter.client.d.ts","sourceRoot":"","sources":["../../src/components/meonode-unmounter.client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAA;AAGjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"meonode-unmounter.client.d.ts","sourceRoot":"","sources":["../../src/components/meonode-unmounter.client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAA;AAGjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CAsBpH"}
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @param {NodeInstance} props.node The BaseNode instance associated with this component.
|
|
13
13
|
* @param {ReactNode} [props.children] The children to be rendered by this component.
|
|
14
14
|
* @returns {ReactNode} The `children` passed to the component.
|
|
15
|
-
*/export default function MeoNodeUnmounter({node:a,children:b}){const c=useEffectEvent(()=>{a.stableKey&&(BaseNode.elementCache.delete(a.stableKey),MountTrackerUtil.mountedNodes.has(a.stableKey)&&MountTrackerUtil.untrackMount(a.stableKey)),a.lastSignature=void 0});return useEffect(()=>()=>c(),[]),b}
|
|
15
|
+
*/export default function MeoNodeUnmounter({node:a,children:b}){const c=useEffectEvent(()=>{a.stableKey&&(BaseNode.elementCache.delete(a.stableKey),MountTrackerUtil.mountedNodes.has(a.stableKey)&&MountTrackerUtil.untrackMount(a.stableKey),BaseNode.cacheCleanupRegistry.unregister(a)),a.lastSignature=void 0});return useEffect(()=>()=>c(),[]),b}
|
package/dist/core.node.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export declare class BaseNode<E extends NodeElementType = NodeElementType> {
|
|
|
22
22
|
static propProcessingCache: Map<string, PropProcessingCache>;
|
|
23
23
|
static scheduledCleanup: boolean;
|
|
24
24
|
private static _navigationStarted;
|
|
25
|
+
private static renderContextPool;
|
|
26
|
+
private static acquireRenderContext;
|
|
27
|
+
private static releaseRenderContext;
|
|
25
28
|
constructor(element: E, rawProps?: Partial<NodeProps<E>>, deps?: DependencyList);
|
|
26
29
|
/**
|
|
27
30
|
* Lazily processes and retrieves the final, normalized props for the node.
|
package/dist/core.node.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.node.d.ts","sourceRoot":"","sources":["../src/core.node.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,YAAY,EAElB,MAAM,OAAO,CAAA;AACd,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,WAAW,EAEX,eAAe,EACf,YAAY,EACZ,UAAU,EACV,SAAS,EACT,mBAAmB,EACnB,OAAO,EAER,MAAM,yBAAyB,CAAA;AAWhC;;;;;;;GAOG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe;IACxD,UAAU,EAAE,MAAM,CAAgE;IAElF,OAAO,EAAE,CAAC,CAAA;IACV,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAK;IAC3C,SAAgB,UAAU,UAAO;IAEjC,OAAO,CAAC,MAAM,CAAC,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAgB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAGzB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAEtC,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,OAAc,YAAY,iCAAuC;IACjE,OAAc,mBAAmB,mCAAyC;IAG1E,OAAc,gBAAgB,UAAQ;IAGtC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAQ;
|
|
1
|
+
{"version":3,"file":"core.node.d.ts","sourceRoot":"","sources":["../src/core.node.ts"],"names":[],"mappings":"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"}
|
package/dist/core.node.js
CHANGED
|
@@ -5,7 +5,7 @@ const _excluded=["ref","children"],_excluded2=["key"],_excluded3=["children","ke
|
|
|
5
5
|
* It uses an iterative rendering approach to handle deeply nested structures without causing stack overflows.
|
|
6
6
|
* @class BaseNode
|
|
7
7
|
* @template E - The type of React element or component this node represents.
|
|
8
|
-
*/export class BaseNode{constructor(a,b={},c){// Element type validation is performed once at construction to prevent invalid nodes from being created.
|
|
8
|
+
*/export class BaseNode{static acquireRenderContext(){const a=BaseNode.renderContextPool;return 0<a.length?a.pop():{workStack:Array(512),renderedElements:new Map}}static releaseRenderContext(a){50>BaseNode.renderContextPool.length&&2048>a.workStack.length&&(a.workStack.length=0,a.renderedElements.clear(),BaseNode.renderContextPool.push(a))}constructor(a,b={},c){// Element type validation is performed once at construction to prevent invalid nodes from being created.
|
|
9
9
|
if(_defineProperty(this,"instanceId",Math.random().toString(36).slice(2)+Date.now().toString(36)),_defineProperty(this,"rawProps",{}),_defineProperty(this,"isBaseNode",!0),!isValidElementType(a)){const b=getComponentType(a);throw new Error(`Invalid element type: ${b} provided!`)}this.element=a,this.rawProps=b,this._deps=c;// Extract commonly handled props; the remaining `propsForSignature` are used to compute a stable hash.
|
|
10
10
|
const{ref:d,children:e}=b,f=_objectWithoutProperties(b,_excluded);// Generate or get cached stable key
|
|
11
11
|
this.stableKey=this._getStableKey(f),NodeUtil.isServer||BaseNode._navigationStarted||(NavigationCacheManagerUtil.getInstance().start(),BaseNode._navigationStarted=!0)}/**
|
|
@@ -55,31 +55,36 @@ this.stableKey=this._getStableKey(f),NodeUtil.isServer||BaseNode._navigationStar
|
|
|
55
55
|
const b=NodeUtil.isServer||!this.stableKey?void 0:BaseNode.elementCache.get(this.stableKey),c=NodeUtil.shouldNodeUpdate(b?.prevDeps,this._deps,a);// Decide whether this node (and its subtree) should update given dependency arrays.
|
|
56
56
|
// Fast return: if nothing should update and we have a cached element, reuse it.
|
|
57
57
|
if(!c&&b?.renderedElement)return b.accessCount+=1,b.renderedElement;// When this node doesn't need update, its children are considered "blocked" and may be skipped.
|
|
58
|
-
|
|
59
|
-
let
|
|
60
|
-
const
|
|
61
|
-
const b=Math.max(a,
|
|
62
|
-
for(let a=0;a<
|
|
63
|
-
|
|
64
|
-
const g=
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
f(b);for(let b=a.length-1;0<=b;b--){const c=a[b],f=NodeUtil.isServer||!c.stableKey?void 0:BaseNode.elementCache.get(c.stableKey),i=NodeUtil.shouldNodeUpdate(f?.prevDeps,c._deps,h);// Respect server/client differences for child cache lookup.
|
|
58
|
+
const d=BaseNode.acquireRenderContext();// Acquire context from pool to reduce allocation pressure
|
|
59
|
+
let{workStack:e}=d;const{renderedElements:f}=d;let g=0;try{// Fast capacity check with exponential growth
|
|
60
|
+
const a=a=>{if(a>e.length){// Double capacity or use exact requirement (whichever is larger)
|
|
61
|
+
const b=Math.max(a,e.length<<1),c=Array(b);// Manual copy is faster than Array methods for primitive/object arrays
|
|
62
|
+
for(let a=0;a<g;a++)c[a]=e[a];e=c}};// Push initial work item
|
|
63
|
+
// Iterative depth-first traversal with explicit begin/complete phases to avoid recursion.
|
|
64
|
+
for(e[g++]={node:this,isProcessed:!1,blocked:!c};0<g;){const b=e[g-1];if(!b){g--;continue}const{node:c,isProcessed:d,blocked:h}=b;if(!d){b.isProcessed=!0;const d=c.props.children;if(d){// Only consider BaseNode children for further traversal; primitives and React elements are terminal.
|
|
65
|
+
const b=(Array.isArray(d)?d:[d]).filter(NodeUtil.isNodeInstance),c=g+b.length;// --- Check capacity ONCE before loop ---
|
|
66
|
+
a(c);for(let a=b.length-1;0<=a;a--){const c=b[a],d=NodeUtil.isServer||!c.stableKey?void 0:BaseNode.elementCache.get(c.stableKey),i=NodeUtil.shouldNodeUpdate(d?.prevDeps,c._deps,h);// Respect server/client differences for child cache lookup.
|
|
68
67
|
// Determine whether the child should update given its deps and the parent's blocked state.
|
|
69
68
|
// If child doesn't need update and has cached element, reuse it immediately (no push).
|
|
70
|
-
if(!i&&
|
|
71
|
-
const j=h||!i;
|
|
72
|
-
const a=
|
|
69
|
+
if(!i&&d?.renderedElement){f.set(c,d.renderedElement);continue}// Otherwise push child for processing; childBlocked inherits parent's blocked state.
|
|
70
|
+
const j=h||!i;e[g++]={node:c,isProcessed:!1,blocked:j}}}}else{g--;// Extract node props. Non-present props default to undefined via destructuring.
|
|
71
|
+
const a=c.props,{children:b,key:d,css:e,nativeProps:h,disableEmotion:i}=a,j=_objectWithoutProperties(a,_excluded3);let k=[];if(b){// Convert child placeholders into concrete React nodes:
|
|
72
|
+
// - If it's a BaseNode, lookup its rendered ReactElement from the map.
|
|
73
|
+
// - If it's already a React element, use it directly.
|
|
74
|
+
// - Otherwise treat as primitive ReactNode.
|
|
75
|
+
const a=Array.isArray(b)?b:[b],c=a.length;// Pre-allocate array to avoid resizing during iteration
|
|
76
|
+
k=Array(c);for(let b=0;b<c;b++){const c=a[b];if(NodeUtil.isNodeInstance(c)){const a=f.get(c);if(!a)throw new Error(`[MeoNode] Missing rendered element for child node: ${c.stableKey}`);k[b]=a}else k[b]=isValidElement(c)?c:c}}// Merge element props: explicit other props + DOM native props + React key.
|
|
73
77
|
const l=_objectSpread(_objectSpread({},j),{},{key:d},h);let m;// Handle fragments specially: create fragment element with key and children.
|
|
74
|
-
if(
|
|
78
|
+
if(c.element===Fragment||isFragment(c.element))m=createElement(c.element,{key:d},...k);else{// StyledRenderer for emotion-based styling unless explicitly disabled or no styles are present.
|
|
75
79
|
// StyledRenderer handles SSR hydration and emotion CSS injection when css prop exists or element has style tags.
|
|
76
|
-
const a=!i&&(
|
|
77
|
-
if(!NodeUtil.isServer&&
|
|
78
|
-
const a={prevDeps:
|
|
79
|
-
BaseNode.elementCache.set(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
const a=!i&&(e||!hasNoStyleTag(c.element));m=a?createElement(StyledRenderer,_objectSpread(_objectSpread({element:c.element},l),{},{css:e,suppressHydrationWarning:!0}),...k):createElement(c.element,l,...k)}// Cache the generated element on client-side to speed up future renders.
|
|
81
|
+
if(!NodeUtil.isServer&&c.stableKey){const a=BaseNode.elementCache.get(c.stableKey);if(a)a.prevDeps=c._deps,a.renderedElement=m,a.accessCount+=1;else{// Create new cache entry and register for cleanup
|
|
82
|
+
const a={prevDeps:c._deps,renderedElement:m,nodeRef:new WeakRef(c),createdAt:Date.now(),accessCount:1,instanceId:c.instanceId};// Set new cache entry
|
|
83
|
+
BaseNode.elementCache.set(c.stableKey,a),BaseNode.cacheCleanupRegistry.register(c,{cacheKey:c.stableKey,instanceId:c.instanceId},c)}}// Store the rendered element so parent nodes can reference it.
|
|
84
|
+
f.set(c,m)}}// Get the final rendered element for the root node of this render cycle.
|
|
85
|
+
const b=f.get(this);return!NodeUtil.isServer&&this.stableKey?createElement(MeoNodeUnmounter,{node:this},b):b}finally{// Always release context back to pool, even if an exception occurred
|
|
86
|
+
// Null out workStack slots to help GC before releasing
|
|
87
|
+
for(let a=0;a<g;a++)e[a]=null;BaseNode.releaseRenderContext({workStack:e,renderedElements:f})}}/**
|
|
83
88
|
* Renders the node into a React Portal, mounting it directly under `document.body`.
|
|
84
89
|
* Returns a handle with `update` and `unmount` methods to control the portal's lifecycle.
|
|
85
90
|
* @method toPortal
|
|
@@ -107,7 +112,7 @@ BaseNode.propProcessingCache.clear(),BaseNode.elementCache.clear(),ThemeUtil.cle
|
|
|
107
112
|
* The primary factory function for creating a `BaseNode` instance.
|
|
108
113
|
* It's the simplest way to wrap a component or element.
|
|
109
114
|
* @function Node
|
|
110
|
-
*/_BaseNode=BaseNode,_defineProperty(BaseNode,"elementCache",new Map),_defineProperty(BaseNode,"propProcessingCache",new Map),_defineProperty(BaseNode,"scheduledCleanup",!1),_defineProperty(BaseNode,"_navigationStarted",!1),_defineProperty(BaseNode,"cacheCleanupRegistry",new FinalizationRegistry(a=>{const{cacheKey:b,instanceId:c}=a,d=_BaseNode.elementCache.get(b);d?.instanceId===c&&_BaseNode.elementCache.delete(b),MountTrackerUtil.mountedNodes.has(b)&&MountTrackerUtil.untrackMount(b)})),_defineProperty(BaseNode,"portalCleanupRegistry",new FinalizationRegistry(a=>{const{domElement:b,reactRoot:c}=a;__DEBUG__&&console.log("[MeoNode] FinalizationRegistry auto-cleaning portal");// Guard: Check if already unmounted
|
|
115
|
+
*/_BaseNode=BaseNode,_defineProperty(BaseNode,"elementCache",new Map),_defineProperty(BaseNode,"propProcessingCache",new Map),_defineProperty(BaseNode,"scheduledCleanup",!1),_defineProperty(BaseNode,"_navigationStarted",!1),_defineProperty(BaseNode,"renderContextPool",[]),_defineProperty(BaseNode,"cacheCleanupRegistry",new FinalizationRegistry(a=>{const{cacheKey:b,instanceId:c}=a,d=_BaseNode.elementCache.get(b);d?.instanceId===c&&_BaseNode.elementCache.delete(b),MountTrackerUtil.mountedNodes.has(b)&&MountTrackerUtil.untrackMount(b)})),_defineProperty(BaseNode,"portalCleanupRegistry",new FinalizationRegistry(a=>{const{domElement:b,reactRoot:c}=a;__DEBUG__&&console.log("[MeoNode] FinalizationRegistry auto-cleaning portal");// Guard: Check if already unmounted
|
|
111
116
|
try{c&&"function"==typeof c.unmount&&c.unmount()}catch(a){__DEBUG__&&console.error("[MeoNode] Portal auto-cleanup unmount error:",a)}// Guard: Check if DOM element still connected
|
|
112
117
|
try{b?.isConnected&&b.remove()}catch(a){__DEBUG__&&console.error("[MeoNode] Portal auto-cleanup DOM removal error:",a)}}));function Node(a,b={},c){return new BaseNode(a,b,c)}/**
|
|
113
118
|
* Static alias on the `Node` factory for clearing all internal caches used by `BaseNode`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.util.d.ts","sourceRoot":"","sources":["../../src/util/node.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAyC,KAAK,SAAS,EAAiC,MAAM,OAAO,CAAA;AACnH,OAAO,KAAK,EACV,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,cAAc,EACd,cAAc,EAEd,UAAU,EAEX,MAAM,yBAAyB,CAAA;AAKhC,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAExD;;;;;GAKG;AACH,qBAAa,QAAQ;IACnB,OAAO,eAAiB;IAGxB,OAAc,QAAQ,UAAgC;IAGtD,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAgC;IAGtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,OAAM;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,MAAK;IAGhD,OAAO,CAAC,MAAM,CAAC,SAAS,CAAgC;IAKxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAA2D;IAGjG,OAAc,oBAAoB;;;;;OAM/B;IAEH;;;;;;;;;;OAUG;IACH,OAAc,cAAc,wCAS3B;IAED;;;;;OAKG;IACH,OAAc,WAAW,yBAAgH;IAEzI;;;OAGG;IACH,OAAc,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAc5C;IAED,OAAO,CAAC,MAAM,CAAC,OAAO;IA0BtB;;;;;OAKG;IACH,OAAc,mBAAmB,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"node.util.d.ts","sourceRoot":"","sources":["../../src/util/node.util.ts"],"names":[],"mappings":"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"}
|
package/dist/util/node.util.js
CHANGED
|
@@ -18,7 +18,8 @@ const b=d[c],f=a[b],g=b.charCodeAt(0);e=(e<<5)-e+g,e&=e,"string"==typeof f&&(e=(
|
|
|
18
18
|
* This signature includes the element's type to prevent collisions between different components
|
|
19
19
|
* and handles primitive values in arrays and objects for better caching.
|
|
20
20
|
* @method createPropSignature
|
|
21
|
-
*/static createPropSignature(a,b){if(NodeUtil.isServer)return;const c=getElementTypeName(a),d=Object.keys(b)
|
|
21
|
+
*/static createPropSignature(a,b){if(NodeUtil.isServer)return;const c=getElementTypeName(a),d=Object.keys(b);// Optimization: Only sort if there's more than one key to ensure stability
|
|
22
|
+
1<d.length&&d.sort();const e=[`${c}:`];if("function"==typeof a){let b=NodeUtil._functionSignatureCache.get(a);b||(b=NodeUtil.hashString(a.toString()),NodeUtil._functionSignatureCache.set(a,b)),e.push(b)}for(const c of d){const a=b[c];let d;const f=typeof a;if("string"==f||"number"===f||"boolean"===f)d=`${c}:${a};`;else if(null===a)d=`${c}:null;`;else if(a===void 0)d=`${c}:undefined;`;else if("css"===c&&"object"==typeof a)d=`css:${this.hashCSS(a)};`;else if(Array.isArray(a)){// Hash primitive values in arrays for better cache hits
|
|
22
23
|
const b=a.filter(a=>{const b=typeof a;return"string"==b||"number"===b||"boolean"===b||null===a});d=b.length===a.length?`${c}:[${b.join(",")}];`:`${c}:[${a.length}];`}else if(a&&a.isBaseNode)d=`${c}:${a.stableKey};`;else{// Include sorted keys for object structure signature
|
|
23
24
|
const b=Object.keys(a).sort();d=`${c}:{${b.join(",")}};`}e.push(d)}return NodeUtil.hashString(e.join(","))}/**
|
|
24
25
|
* Extracts "critical" props from a given set of props. Critical props are those
|
|
@@ -54,20 +55,24 @@ return .3*(c/1e3)+.7*(1e3/(d+1))}/**
|
|
|
54
55
|
* @method processProps
|
|
55
56
|
*/static processProps(a,b={},c){const{ref:d,key:e,children:f,css:g,props:i={},disableEmotion:h}=b,j=_objectWithoutProperties(b,_excluded);// --- Fast Path Optimization ---
|
|
56
57
|
if(0===Object.keys(j).length&&!g)return omitUndefined({ref:d,key:e,disableEmotion:h,nativeProps:omitUndefined(i),children:NodeUtil._processChildren(f,h)});// --- Hybrid Caching Strategy ---
|
|
57
|
-
const k={},l={};// 1. Categorize props into cacheable (primitives) and non-cacheable (objects/functions).
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const k={},l={},m=Object.keys(j);// 1. Categorize props into cacheable (primitives) and non-cacheable (objects/functions).
|
|
59
|
+
// Optimization: Use Object.keys loop instead of for..in for better performance and safety
|
|
60
|
+
for(let d=0;d<m.length;d++){const a=m[d],b=j[a],c=typeof b;"string"===c||"number"===c||"boolean"===c?k[a]=b:l[a]=b}// 2. Pass element type to signature generation
|
|
61
|
+
const n=NodeUtil.createPropSignature(a,k),{cssProps:o}=NodeUtil.getCachedCssProps(k,n),p=getCSSProps(l),q=getDOMProps(j),r=_objectSpread(_objectSpread(_objectSpread({},o),p),g),s=NodeUtil._processChildren(f,h,c);// 3. Process non-cacheable props on every render to ensure correctness for functions and objects.
|
|
60
62
|
// DOM props are always processed fresh.
|
|
61
63
|
// 4. Assemble the final CSS object.
|
|
62
64
|
// --- Child Normalization ---
|
|
63
65
|
// --- Final Assembly ---
|
|
64
|
-
return omitUndefined(_objectSpread(_objectSpread({ref:d,key:e,css:
|
|
66
|
+
return omitUndefined(_objectSpread(_objectSpread({ref:d,key:e,css:r},q),{},{disableEmotion:h,nativeProps:omitUndefined(i),children:s}))}/**
|
|
65
67
|
* Processes and normalizes children of the node.
|
|
66
68
|
* Converts raw children (React elements, primitives, or other BaseNodes) into a consistent format.
|
|
67
69
|
* @param children The raw children to process.
|
|
68
70
|
* @param disableEmotion If true, emotion styling will be disabled for these children.
|
|
69
71
|
* @param parentStableKey The stable key of the parent node, used for generating unique keys for children.
|
|
70
|
-
*/static _processChildren(a,b,c){return a?"function"==typeof a?a:Array.isArray(a)?a.map((a,d)=>NodeUtil.processRawNode(a,b,`${c}_${d}`)):NodeUtil.processRawNode(a,b,c):void 0
|
|
72
|
+
*/static _processChildren(a,b,c){return a?"function"==typeof a?a:Array.isArray(a)?1===a.length?NodeUtil.processRawNode(a[0],b,`${c}_0`):a.map((a,d)=>NodeUtil.processRawNode(a,b,`${c}_${d}`)):NodeUtil.processRawNode(a,b,c):void 0;// Fast path for non-array (single child)
|
|
73
|
+
// Fast path for single element array
|
|
74
|
+
// General case: multiple children
|
|
75
|
+
}/**
|
|
71
76
|
* Determines if a node should update based on its dependency array.
|
|
72
77
|
* Uses a shallow comparison, similar to React's `useMemo` and `useCallback`.
|
|
73
78
|
* @method shouldNodeUpdate
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meonode/ui",
|
|
3
3
|
"description": "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.",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main.js",
|
|
7
7
|
"types": "./dist/main.d.ts",
|