@meonode/ui 0.4.9 → 0.4.11
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 +87 -1
- package/dist/components/meonode-unmounter.client.d.ts +11 -9
- package/dist/components/meonode-unmounter.client.d.ts.map +1 -1
- package/dist/components/meonode-unmounter.client.js +12 -10
- 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 +62 -16
- package/dist/util/node.util.d.ts.map +1 -1
- package/dist/util/node.util.js +104 -27
- package/dist/util/theme.util.d.ts +1 -8
- package/dist/util/theme.util.d.ts.map +1 -1
- package/dist/util/theme.util.js +2 -8
- package/package.json +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.4.11
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
- **core**: enhance MeoNodeUnmounter cleanup logic and support additional props cloning ([`02c17f7`](https://github.com/l7aromeo/meonode-ui/commit/02c17f7))
|
|
9
|
+
- Refactor MeoNodeUnmounter to use useEffectEvent for stable cleanup on unmount
|
|
10
|
+
- Cleanup removes node from BaseNode.elementCache, untracks mount via MountTrackerUtil, unregisters from BaseNode.cacheCleanupRegistry, and clears lastSignature to prevent memory leaks
|
|
11
|
+
- Support cloning and forwarding additional props to valid React children elements
|
|
12
|
+
|
|
13
|
+
### Refactor
|
|
14
|
+
- **node.util**: enhance documentation for utility methods and improve clarity ([`ee42c24`](https://github.com/l7aromeo/meonode-ui/commit/ee42c24))
|
|
15
|
+
- **theme**: reorder ThemeResolverCache methods for clarity ([`cb842c8`](https://github.com/l7aromeo/meonode-ui/commit/cb842c8))
|
|
16
|
+
- Moved `_generateCacheKey` and `_evict` methods below the main logic in `ThemeResolverCache` for better readability and organization
|
|
17
|
+
- Removed duplicate declaration of `_instance` property
|
|
18
|
+
- Kept functionality unchanged, improving code structure and maintainability
|
|
19
|
+
|
|
20
|
+
### Test
|
|
21
|
+
- **perf**: add memory leak detection test for navigation cycles and improve formatMemory function ([`ba139fc`](https://github.com/l7aromeo/meonode-ui/commit/ba139fc))
|
|
22
|
+
- Added a new performance test to detect memory leaks during repeated navigation cycles between pages
|
|
23
|
+
- The test measures heap memory usage before, during, and after navigation, ensuring memory growth stays within acceptable limits
|
|
24
|
+
- Enhanced the formatMemory utility to correctly handle negative byte values and added JSDoc comments for clarity
|
|
25
|
+
- Removed an obsolete shallowly equal props performance test to streamline the test suite
|
|
26
|
+
- **unmounter**: add regression test for MeoNodeUnmounter to forward implicit props in MUI RadioGroup integration ([`2ecaabd`](https://github.com/l7aromeo/meonode-ui/commit/2ecaabd))
|
|
27
|
+
- Added a test to ensure MeoNodeUnmounter correctly forwards props injected via React.cloneElement, addressing issues with libraries like MUI where RadioGroup injects 'checked' and 'onChange' into Radio components
|
|
28
|
+
- This prevents swallowing of props and verifies proper behavior of controlled radio inputs
|
|
29
|
+
- Also updated an existing cache size assertion to allow equality, reflecting improved mount tracking
|
|
30
|
+
- **perf**: update react-createelement comparison tests with 5000 nested nodes and fix typings ([`b345ec0`](https://github.com/l7aromeo/meonode-ui/commit/b345ec0))
|
|
31
|
+
- Changed rerender to use React.cloneElement<any> for proper typing
|
|
32
|
+
- Updated NUM_NODES constant to 5000 for nested structure tests
|
|
33
|
+
- Removed redundant comments about node count reduction to prevent stack overflow
|
|
34
|
+
|
|
35
|
+
### Chore
|
|
36
|
+
- **deps**: upgrade devDependencies and update test scripts ([`2ea128e`](https://github.com/l7aromeo/meonode-ui/commit/2ea128e))
|
|
37
|
+
- add new devDependencies: @emotion/is-prop-valid@1.4.0, @emotion/styled@11.14.1, @mui/material@7.3.5, and related packages
|
|
38
|
+
- update yarn to version 4.11.0
|
|
39
|
+
- update test scripts to increase max-old-space-size to 8192 and include react-createelement-comparison.test.ts in test and perf runs
|
|
40
|
+
- update various package resolutions in yarn.lock to align with new versions and dependencies
|
|
41
|
+
- **babel**: add "builtIns": false to minify plugin configuration ([`e16cdfb`](https://github.com/l7aromeo/meonode-ui/commit/e16cdfb))
|
|
42
|
+
- **yarn**: update yarnPath to version 4.11.0 ([`ecb6c68`](https://github.com/l7aromeo/meonode-ui/commit/ecb6c68))
|
|
43
|
+
|
|
44
|
+
### Docs
|
|
45
|
+
- **CONTRIBUTING**: improve formatting and readability of contribution guidelines ([`a7462ed`](https://github.com/l7aromeo/meonode-ui/commit/a7462ed))
|
|
46
|
+
|
|
47
|
+
## 0.4.10
|
|
48
|
+
|
|
49
|
+
### Fix
|
|
50
|
+
- **unmounter**: prevent redundant FinalizationRegistry callbacks ([`59f5adf`](https://github.com/l7aromeo/meonode-ui/commit/59f5adf2f553aa49a88d1b44366b004d829ca107))
|
|
51
|
+
- Explicitly unregister node from cacheCleanupRegistry on unmount
|
|
52
|
+
- Prevents double cleanup when node is both unmounted and GC'd
|
|
53
|
+
- Improves cleanup efficiency and reduces unnecessary registry callbacks
|
|
54
|
+
|
|
55
|
+
### Perf
|
|
56
|
+
- **util**: optimize prop processing and child handling ([`be26488`](https://github.com/l7aromeo/meonode-ui/commit/be26488e304629dd13851dfcaa7fedf43ad8b5c3))
|
|
57
|
+
- Conditional sort: only sort keys if length > 1 (eliminates unnecessary sorts)
|
|
58
|
+
- Replace for...in with Object.keys() iteration for better performance and safety
|
|
59
|
+
- Add fast path for single child processing (non-array or 1-element array)
|
|
60
|
+
- Avoid unnecessary array operations for common single-child case
|
|
61
|
+
- Performance impact:
|
|
62
|
+
- Reduced CPU overhead for small prop objects
|
|
63
|
+
- Faster iteration with Object.keys() vs for...in
|
|
64
|
+
- Eliminates array map() call for single child components
|
|
65
|
+
|
|
66
|
+
- **core**: add exception safety and optimize render method ([`5aad000`](https://github.com/l7aromeo/meonode-ui/commit/5aad000335ff29f078a9d40192d5a70fe9b61d12))
|
|
67
|
+
- Wrap render logic in try-finally to ensure renderContext is always released
|
|
68
|
+
- Null out workStack slots before releasing to help GC
|
|
69
|
+
- Pre-allocate finalChildren array to avoid resizing during iteration
|
|
70
|
+
- Replace non-null assertion with explicit error handling for better debugging
|
|
71
|
+
- Add object pooling for workStack and renderedElements (reduces GC pressure)
|
|
72
|
+
- Pool capped at 50 contexts with 2048-item limit to prevent memory issues
|
|
73
|
+
- Exception safety ensures cleanup even if rendering throws
|
|
74
|
+
|
|
75
|
+
### Test
|
|
76
|
+
- **perf**: add React comparison tests with memory tracking ([`bc66d54`](https://github.com/l7aromeo/meonode-ui/commit/bc66d540c4ffa4ad083322dbdbc201f652ea5314))
|
|
77
|
+
- Add comprehensive performance comparison between React.createElement and MeoNode
|
|
78
|
+
- Test both flat (10k nodes) and nested (5k nodes) structures
|
|
79
|
+
- Include memory usage measurements (initial + after 100 updates)
|
|
80
|
+
- Reduce nested nodes from 10k to 5k to prevent stack overflow with StyledRenderer
|
|
81
|
+
- Add table output with time and memory columns for clear comparison
|
|
82
|
+
- Add GC availability warning for accurate measurements
|
|
83
|
+
- Provides detailed performance and memory behavior insights during re-renders
|
|
84
|
+
- Avoids stack overflow in deeply nested test scenarios
|
|
85
|
+
|
|
86
|
+
- **performance**: enhance performance metrics report formatting and adjust thresholds ([`6f3abe4`](https://github.com/l7aromeo/meonode-ui/commit/6f3abe4442938aa6cd414341fdf2aba25a9ece58))
|
|
87
|
+
- Improve table formatting for performance metrics
|
|
88
|
+
- Adjust test thresholds based on optimization results
|
|
89
|
+
- Enhanced reporting for better visibility into performance characteristics
|
|
90
|
+
|
|
5
91
|
## [0.4.9] - 2025-11-19
|
|
6
92
|
|
|
7
93
|
### Feat
|
|
@@ -843,4 +929,4 @@ All notable changes to this project will be documented in this file.
|
|
|
843
929
|
- This changelog covers the most recent development history available
|
|
844
930
|
- The project focuses on building React UIs with type-safe fluency without JSX syntax
|
|
845
931
|
- Recent development has emphasized Emotion integration, type safety improvements, and enhanced flexbox support
|
|
846
|
-
- For a complete history, view all commits on GitHub: [View all commits](https://github.com/l7aromeo/meonode-ui/commits)
|
|
932
|
+
- For a complete history, view all commits on GitHub: [View all commits](https://github.com/l7aromeo/meonode-ui/commits)
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { NodeInstance } from '../types/node.type.js';
|
|
3
3
|
/**
|
|
4
|
-
* `MeoNodeUnmounter` is a client-side React component responsible for
|
|
5
|
-
*
|
|
4
|
+
* `MeoNodeUnmounter` is a client-side React component responsible for performing cleanup
|
|
5
|
+
* operations when a `MeoNode` instance is unmounted from the React tree.
|
|
6
6
|
*
|
|
7
|
-
* It
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the node
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* It leverages `useEffectEvent` to create a stable cleanup function that is called
|
|
8
|
+
* when the component unmounts. This cleanup function performs the following actions:
|
|
9
|
+
* - Deletes the node from `BaseNode.elementCache` using its `stableKey`.
|
|
10
|
+
* - Untracks the node's mount status via `MountTrackerUtil.untrackMount`.
|
|
11
|
+
* - Unregisters the node from `BaseNode.cacheCleanupRegistry` to prevent redundant
|
|
12
|
+
* finalization callbacks.
|
|
13
|
+
* - Clears the `lastSignature` of the associated `BaseNode` instance to help prevent
|
|
14
|
+
* memory leaks from retained prop objects.
|
|
13
15
|
* @param {object} props The component's props.
|
|
14
16
|
* @param {NodeInstance} props.node The BaseNode instance associated with this component.
|
|
15
17
|
* @param {ReactNode} [props.children] The children to be rendered by this component.
|
|
16
18
|
* @returns {ReactNode} The `children` passed to the component.
|
|
17
19
|
*/
|
|
18
|
-
export default function MeoNodeUnmounter({ node, children }: {
|
|
20
|
+
export default function MeoNodeUnmounter({ node, children, ...rest }: {
|
|
19
21
|
node: NodeInstance;
|
|
20
22
|
children?: ReactNode;
|
|
21
23
|
}): ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meonode-unmounter.client.d.ts","sourceRoot":"","sources":["../../src/components/meonode-unmounter.client.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"meonode-unmounter.client.d.ts","sourceRoot":"","sources":["../../src/components/meonode-unmounter.client.ts"],"names":[],"mappings":"AACA,OAAO,EAAgC,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAA;AAG/F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CA0B7H"}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
"use client";import{useEffect,useEffectEvent}from"react";import{MountTrackerUtil}from"../util/mount-tracker.util.js";import{BaseNode}from"../core.node.js";/**
|
|
2
|
-
* `MeoNodeUnmounter` is a client-side React component responsible for
|
|
3
|
-
*
|
|
1
|
+
"use client";const _excluded=["node","children"];function _objectWithoutProperties(a,b){if(null==a)return{};var c,d,e=_objectWithoutPropertiesLoose(a,b);if(Object.getOwnPropertySymbols){var f=Object.getOwnPropertySymbols(a);for(d=0;d<f.length;d++)c=f[d],-1===b.indexOf(c)&&{}.propertyIsEnumerable.call(a,c)&&(e[c]=a[c])}return e}function _objectWithoutPropertiesLoose(a,b){if(null==a)return{};var c={};for(var d in a)if({}.hasOwnProperty.call(a,d)){if(-1!==b.indexOf(d))continue;c[d]=a[d]}return c}import{cloneElement,isValidElement,useEffect,useEffectEvent}from"react";import{MountTrackerUtil}from"../util/mount-tracker.util.js";import{BaseNode}from"../core.node.js";/**
|
|
2
|
+
* `MeoNodeUnmounter` is a client-side React component responsible for performing cleanup
|
|
3
|
+
* operations when a `MeoNode` instance is unmounted from the React tree.
|
|
4
4
|
*
|
|
5
|
-
* It
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the node
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* It leverages `useEffectEvent` to create a stable cleanup function that is called
|
|
6
|
+
* when the component unmounts. This cleanup function performs the following actions:
|
|
7
|
+
* - Deletes the node from `BaseNode.elementCache` using its `stableKey`.
|
|
8
|
+
* - Untracks the node's mount status via `MountTrackerUtil.untrackMount`.
|
|
9
|
+
* - Unregisters the node from `BaseNode.cacheCleanupRegistry` to prevent redundant
|
|
10
|
+
* finalization callbacks.
|
|
11
|
+
* - Clears the `lastSignature` of the associated `BaseNode` instance to help prevent
|
|
12
|
+
* memory leaks from retained prop objects.
|
|
11
13
|
* @param {object} props The component's props.
|
|
12
14
|
* @param {NodeInstance} props.node The BaseNode instance associated with this component.
|
|
13
15
|
* @param {ReactNode} [props.children] The children to be rendered by this component.
|
|
14
16
|
* @returns {ReactNode} The `children` passed to the component.
|
|
15
|
-
*/export default function MeoNodeUnmounter({node:
|
|
17
|
+
*/export default function MeoNodeUnmounter(a){let{node:b,children:c}=a,d=_objectWithoutProperties(a,_excluded);const e=useEffectEvent(()=>{b.stableKey&&(BaseNode.elementCache.delete(b.stableKey),MountTrackerUtil.mountedNodes.has(b.stableKey)&&MountTrackerUtil.untrackMount(b.stableKey),BaseNode.cacheCleanupRegistry.unregister(b)),b.lastSignature=void 0});return useEffect(()=>()=>e(),[]),isValidElement(c)?cloneElement(c,d):c}
|
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`.
|
package/dist/util/node.util.d.ts
CHANGED
|
@@ -38,19 +38,31 @@ export declare class NodeUtil {
|
|
|
38
38
|
* This check is performed only on the client-side by checking if the property exists in `document.body.style`.
|
|
39
39
|
* On the server-side, it always returns `false`.
|
|
40
40
|
* @param k The string to check.
|
|
41
|
+
* @returns True if the string is a valid CSS style property, false otherwise.
|
|
41
42
|
*/
|
|
42
43
|
static isStyleProp: (k: string) => boolean;
|
|
43
44
|
/**
|
|
44
45
|
* Combines FNV-1a and djb2 hash functions for a more robust signature.
|
|
45
|
-
*
|
|
46
|
+
* This hybrid approach provides better distribution than either algorithm alone.
|
|
47
|
+
* @param str The string to hash.
|
|
48
|
+
* @returns A combined hash string in base-36 format.
|
|
46
49
|
*/
|
|
47
50
|
static hashString(str: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Generates a fast structural hash for CSS objects without full serialization.
|
|
53
|
+
* This is an optimized hashing method that samples the first 10 keys for performance.
|
|
54
|
+
* @param css The CSS object to hash.
|
|
55
|
+
* @returns A hash string representing the CSS object structure.
|
|
56
|
+
*/
|
|
48
57
|
private static hashCSS;
|
|
49
58
|
/**
|
|
50
59
|
* Creates a unique, stable signature from the element type and props.
|
|
51
60
|
* This signature includes the element's type to prevent collisions between different components
|
|
52
61
|
* and handles primitive values in arrays and objects for better caching.
|
|
53
|
-
*
|
|
62
|
+
* On server environments, returns undefined as signatures are not needed for server-side rendering.
|
|
63
|
+
* @param element The element type to include in the signature.
|
|
64
|
+
* @param props The props object to include in the signature.
|
|
65
|
+
* @returns A unique signature string or undefined on the server.
|
|
54
66
|
*/
|
|
55
67
|
static createPropSignature(element: NodeElementType, props: Record<string, unknown>): string | undefined;
|
|
56
68
|
/**
|
|
@@ -59,12 +71,18 @@ export declare class NodeUtil {
|
|
|
59
71
|
* `aria-*` attributes, `data-*` attributes, `css`, `className`, and `style`.
|
|
60
72
|
* This method is used to optimize prop processing by focusing on props that are
|
|
61
73
|
* most likely to influence rendering or behavior.
|
|
74
|
+
* @param props The original props object.
|
|
75
|
+
* @param keys The keys to process from the props object.
|
|
76
|
+
* @returns An object containing only the critical props with an added count property.
|
|
62
77
|
*/
|
|
63
78
|
static extractCriticalProps(props: Record<string, unknown>, keys: string[]): Record<string, unknown>;
|
|
64
79
|
/**
|
|
65
80
|
* Retrieves computed CSS props from the cache with LRU tracking.
|
|
66
81
|
* Access time and hit count are tracked for smarter eviction.
|
|
67
|
-
*
|
|
82
|
+
* Falls back to direct computation if no signature is provided or running on server.
|
|
83
|
+
* @param cacheableProps The props to compute CSS properties from.
|
|
84
|
+
* @param signature The cache signature to use for lookup.
|
|
85
|
+
* @returns An object containing the CSS props.
|
|
68
86
|
*/
|
|
69
87
|
static getCachedCssProps(cacheableProps: Record<string, unknown>, signature?: string): {
|
|
70
88
|
cssProps: Record<string, unknown>;
|
|
@@ -72,46 +90,66 @@ export declare class NodeUtil {
|
|
|
72
90
|
/**
|
|
73
91
|
* Implements an LRU eviction strategy that removes multiple entries at once.
|
|
74
92
|
* It uses a scoring system where older and less frequently used entries have a higher eviction priority.
|
|
75
|
-
*
|
|
93
|
+
* This batch eviction approach improves performance by avoiding frequent cache cleanup operations.
|
|
76
94
|
*/
|
|
77
95
|
private static _evictLRUEntries;
|
|
78
96
|
/**
|
|
79
97
|
* Calculates an eviction score based on age and frequency of access.
|
|
80
98
|
* Higher scores mean more likelihood to be evicted.
|
|
81
|
-
*
|
|
99
|
+
* The scoring system uses weighted factors: 30% recency and 70% frequency.
|
|
100
|
+
* @param value The cache entry to score.
|
|
101
|
+
* @param now The current timestamp for calculating age.
|
|
102
|
+
* @returns A numeric score representing how likely the entry should be evicted.
|
|
82
103
|
*/
|
|
83
104
|
private static _calculateEvictionScore;
|
|
84
105
|
/**
|
|
85
106
|
* The main prop processing pipeline. It separates cacheable and non-cacheable props,
|
|
86
107
|
* generates a signature for caching, and assembles the final props object.
|
|
87
|
-
*
|
|
108
|
+
* This method applies optimizations like fast-path for simple props and hybrid caching strategy.
|
|
109
|
+
* @param element The element type for which props are being processed.
|
|
110
|
+
* @param rawProps The original props to process.
|
|
111
|
+
* @param stableKey The stable key used for child normalization (optional).
|
|
112
|
+
* @returns The processed props object ready for rendering.
|
|
88
113
|
*/
|
|
89
114
|
static processProps(element: NodeElementType, rawProps?: Partial<NodeProps<NodeElementType>>, stableKey?: string): FinalNodeProps;
|
|
90
115
|
/**
|
|
91
116
|
* Processes and normalizes children of the node.
|
|
92
117
|
* Converts raw children (React elements, primitives, or other BaseNodes) into a consistent format.
|
|
118
|
+
* Applies optimizations for single and multiple children scenarios.
|
|
93
119
|
* @param children The raw children to process.
|
|
94
120
|
* @param disableEmotion If true, emotion styling will be disabled for these children.
|
|
95
121
|
* @param parentStableKey The stable key of the parent node, used for generating unique keys for children.
|
|
122
|
+
* @returns The processed children in normalized format.
|
|
96
123
|
*/
|
|
97
124
|
private static _processChildren;
|
|
98
125
|
/**
|
|
99
126
|
* Determines if a node should update based on its dependency array.
|
|
100
127
|
* Uses a shallow comparison, similar to React's `useMemo` and `useCallback`.
|
|
101
|
-
*
|
|
128
|
+
* On server environments, always returns true since SSR has no concept of re-renders.
|
|
129
|
+
* @param prevDeps Previous dependency array to compare.
|
|
130
|
+
* @param newDeps New dependency array to compare.
|
|
131
|
+
* @param parentBlocked Flag indicating if the parent is blocked from updating.
|
|
132
|
+
* @returns True if the node should update, false otherwise.
|
|
102
133
|
*/
|
|
103
134
|
static shouldNodeUpdate(prevDeps: DependencyList | undefined, newDeps: DependencyList | undefined, parentBlocked: boolean): boolean;
|
|
104
135
|
/**
|
|
105
136
|
* The core normalization function for a single child. It takes any valid `NodeElement`
|
|
106
137
|
* (primitive, React element, function, `BaseNode` instance) and converts it into a standardized `BaseNode`
|
|
107
138
|
* instance if it isn't one already. This ensures a consistent structure for the iterative renderer.
|
|
108
|
-
*
|
|
139
|
+
* Handles various node types including primitives, BaseNode instances, function-as-children, React elements,
|
|
140
|
+
* component classes, and component instances.
|
|
141
|
+
* @param node The node element to process and normalize.
|
|
142
|
+
* @param disableEmotion If true, emotion styling will be disabled for this node.
|
|
143
|
+
* @param stableKey The stable key for positional information in parent-child relationships.
|
|
144
|
+
* @returns The normalized node element in BaseNode format.
|
|
109
145
|
*/
|
|
110
146
|
static processRawNode(node: NodeElement, disableEmotion?: boolean, stableKey?: string): NodeElement;
|
|
111
147
|
/**
|
|
112
148
|
* A helper to reliably identify if a given function is a "function-as-a-child" (render prop)
|
|
113
149
|
* rather than a standard Function Component.
|
|
114
|
-
*
|
|
150
|
+
* Distinguishes between render prop functions and component functions by checking for React component signatures.
|
|
151
|
+
* @param node The node to check.
|
|
152
|
+
* @returns True if the node is a function-as-a-child, false otherwise.
|
|
115
153
|
*/
|
|
116
154
|
static isFunctionChild<E extends NodeInstance | ReactNode>(node: NodeElement): node is NodeFunction<E>;
|
|
117
155
|
/**
|
|
@@ -122,11 +160,9 @@ export declare class NodeUtil {
|
|
|
122
160
|
*
|
|
123
161
|
* This allows `BaseNode` to support render props while maintaining its internal processing
|
|
124
162
|
* and normalization logic for the dynamically generated content.
|
|
125
|
-
* @
|
|
126
|
-
* @param
|
|
127
|
-
* @
|
|
128
|
-
* @param {boolean} [props.disableEmotion] Inherited flag to disable Emotion styling for children.
|
|
129
|
-
* @returns {ReactNode | null | undefined} The processed and rendered output of the render function.
|
|
163
|
+
* @param render The function-as-a-child to execute.
|
|
164
|
+
* @param disableEmotion Inherited flag to disable Emotion styling for children.
|
|
165
|
+
* @returns The processed and rendered output of the render function, or null if an error occurs.
|
|
130
166
|
*/
|
|
131
167
|
static functionRenderer<E extends ReactNode | NodeInstance>({ render, disableEmotion }: FunctionRendererProps<E>): ReactNode | null | undefined;
|
|
132
168
|
/**
|
|
@@ -137,7 +173,10 @@ export declare class NodeUtil {
|
|
|
137
173
|
*
|
|
138
174
|
* This method is part of the child processing pipeline, converting internal `NodeElement` representations
|
|
139
175
|
* into actual React elements that can be rendered by React.
|
|
140
|
-
* @
|
|
176
|
+
* @param processedElement The processed node element to render.
|
|
177
|
+
* @param passedKey Optional key to apply to the rendered element.
|
|
178
|
+
* @param disableEmotion Flag to disable emotion styling if needed.
|
|
179
|
+
* @returns The rendered ReactNode.
|
|
141
180
|
*/
|
|
142
181
|
static renderProcessedNode({ processedElement, passedKey, disableEmotion }: {
|
|
143
182
|
processedElement: NodeElement;
|
|
@@ -147,9 +186,16 @@ export declare class NodeUtil {
|
|
|
147
186
|
/**
|
|
148
187
|
* Ensures that the necessary DOM element and React root are available for portal rendering.
|
|
149
188
|
* This is only executed on the client-side.
|
|
150
|
-
*
|
|
189
|
+
* Handles cleanup of stale infrastructure and creates new infrastructure as needed.
|
|
190
|
+
* @param node The node instance that requires portal infrastructure.
|
|
191
|
+
* @returns True if portal infrastructure is ready, false on server or if setup fails.
|
|
151
192
|
*/
|
|
152
193
|
static ensurePortalInfrastructure(node: NodeInstance): boolean;
|
|
194
|
+
/**
|
|
195
|
+
* Cleans up portal infrastructure by unmounting the React root and removing the DOM element.
|
|
196
|
+
* This ensures proper memory cleanup and prevents memory leaks.
|
|
197
|
+
* @param infra The infrastructure object containing the DOM element and React root to clean up.
|
|
198
|
+
*/
|
|
153
199
|
static cleanupPortalInfra(infra: {
|
|
154
200
|
domElement: HTMLDivElement;
|
|
155
201
|
reactRoot: Root;
|
|
@@ -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
|
|
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
|
@@ -6,19 +6,30 @@ const _excluded=["ref","key","children","css","props","disableEmotion"],_exclude
|
|
|
6
6
|
*/export class NodeUtil{constructor(){}// Determines if the current environment is server-side (Node.js) or client-side (browser).
|
|
7
7
|
/**
|
|
8
8
|
* Combines FNV-1a and djb2 hash functions for a more robust signature.
|
|
9
|
-
*
|
|
9
|
+
* This hybrid approach provides better distribution than either algorithm alone.
|
|
10
|
+
* @param str The string to hash.
|
|
11
|
+
* @returns A combined hash string in base-36 format.
|
|
10
12
|
*/static hashString(a){let b=2166136261,c=5381;// FNV offset basis
|
|
11
13
|
// djb2 init
|
|
12
14
|
for(let d=0;d<a.length;d++){const e=a.charCodeAt(d);// FNV-1a
|
|
13
15
|
// djb2
|
|
14
|
-
b^=e,b=Math.imul(b,16777619),c=33*c^e}return`${(b>>>0).toString(36)}_${(c>>>0).toString(36)}`}
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
b^=e,b=Math.imul(b,16777619),c=33*c^e}return`${(b>>>0).toString(36)}_${(c>>>0).toString(36)}`}/**
|
|
17
|
+
* Generates a fast structural hash for CSS objects without full serialization.
|
|
18
|
+
* This is an optimized hashing method that samples the first 10 keys for performance.
|
|
19
|
+
* @param css The CSS object to hash.
|
|
20
|
+
* @returns A hash string representing the CSS object structure.
|
|
21
|
+
*/static hashCSS(a){const b=this._cssCache.get(a);if(b)return b;// Fast structural hash without full serialization
|
|
22
|
+
const c=Object.keys(a);let d=c.length;for(let b=0;b<Math.min(c.length,10);b++){// Sample first 10
|
|
23
|
+
const e=c[b],f=a[e],g=e.charCodeAt(0);d=(d<<5)-d+g,d&=d,"string"==typeof f&&(d=(d<<5)-d+f.length)}const e=d.toString(36);return this._cssCache.set(a,e),e}/**
|
|
17
24
|
* Creates a unique, stable signature from the element type and props.
|
|
18
25
|
* This signature includes the element's type to prevent collisions between different components
|
|
19
26
|
* and handles primitive values in arrays and objects for better caching.
|
|
20
|
-
*
|
|
21
|
-
|
|
27
|
+
* On server environments, returns undefined as signatures are not needed for server-side rendering.
|
|
28
|
+
* @param element The element type to include in the signature.
|
|
29
|
+
* @param props The props object to include in the signature.
|
|
30
|
+
* @returns A unique signature string or undefined on the server.
|
|
31
|
+
*/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
|
|
32
|
+
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
33
|
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
34
|
const b=Object.keys(a).sort();d=`${c}:{${b.join(",")}};`}e.push(d)}return NodeUtil.hashString(e.join(","))}/**
|
|
24
35
|
* Extracts "critical" props from a given set of props. Critical props are those
|
|
@@ -26,6 +37,9 @@ const b=Object.keys(a).sort();d=`${c}:{${b.join(",")}};`}e.push(d)}return NodeUt
|
|
|
26
37
|
* `aria-*` attributes, `data-*` attributes, `css`, `className`, and `style`.
|
|
27
38
|
* This method is used to optimize prop processing by focusing on props that are
|
|
28
39
|
* most likely to influence rendering or behavior.
|
|
40
|
+
* @param props The original props object.
|
|
41
|
+
* @param keys The keys to process from the props object.
|
|
42
|
+
* @returns An object containing only the critical props with an added count property.
|
|
29
43
|
*/static extractCriticalProps(a,b){const c={_keyCount:b.length};let d=0;for(const e of b){if(50<=d)break;// Fast path: direct Set check first (O(1))
|
|
30
44
|
if(NodeUtil.CRITICAL_PROPS.has(e)){c[e]=a[e],d++;continue}// Inline prefix checks using charCode (faster than startsWith for short prefixes)
|
|
31
45
|
const f=e.charCodeAt(0);// Check 'on' prefix (111 = 'o', 110 = 'n')
|
|
@@ -35,42 +49,62 @@ if(100===f&&97===e.charCodeAt(1)&&116===e.charCodeAt(2)&&97===e.charCodeAt(3)){c
|
|
|
35
49
|
100>=b.length&&NodeUtil.isStyleProp(e)&&(c[e]=a[e],d++)}return c}/**
|
|
36
50
|
* Retrieves computed CSS props from the cache with LRU tracking.
|
|
37
51
|
* Access time and hit count are tracked for smarter eviction.
|
|
38
|
-
*
|
|
52
|
+
* Falls back to direct computation if no signature is provided or running on server.
|
|
53
|
+
* @param cacheableProps The props to compute CSS properties from.
|
|
54
|
+
* @param signature The cache signature to use for lookup.
|
|
55
|
+
* @returns An object containing the CSS props.
|
|
39
56
|
*/static getCachedCssProps(a,b){if(NodeUtil.isServer||!b)return{cssProps:getCSSProps(a)};const c=BaseNode.propProcessingCache.get(b);if(c)return c.lastAccess=Date.now(),c.hitCount++,{cssProps:c.cssProps};const d=getCSSProps(a);return BaseNode.propProcessingCache.set(b,{cssProps:d,signature:b,lastAccess:Date.now(),hitCount:1}),BaseNode.propProcessingCache.size>NodeUtil.CACHE_SIZE_LIMIT&&!BaseNode.scheduledCleanup&&(BaseNode.scheduledCleanup=!0,"undefined"==typeof requestIdleCallback?setTimeout(()=>{NodeUtil._evictLRUEntries(),BaseNode.scheduledCleanup=!1},100):requestIdleCallback(()=>{NodeUtil._evictLRUEntries(),BaseNode.scheduledCleanup=!1},{timeout:2e3})),{cssProps:d}}/**
|
|
40
57
|
* Implements an LRU eviction strategy that removes multiple entries at once.
|
|
41
58
|
* It uses a scoring system where older and less frequently used entries have a higher eviction priority.
|
|
42
|
-
*
|
|
59
|
+
* This batch eviction approach improves performance by avoiding frequent cache cleanup operations.
|
|
43
60
|
*/static _evictLRUEntries(){const a=Date.now(),b=new MinHeap((c,a)=>a.score-c.score);// Create max-heap (using min-heap with inverted comparison) to get highest scores first
|
|
44
61
|
for(const[c,d]of BaseNode.propProcessingCache.entries()){const e=this._calculateEvictionScore(d,a);b.push({key:c,score:e})}// O(log n) eviction of top N entries
|
|
45
62
|
for(let a=0;a<NodeUtil.CACHE_CLEANUP_BATCH;a++){const a=b.pop();if(a)BaseNode.propProcessingCache.delete(a.key);else// No more items to evict
|
|
46
63
|
break}}/**
|
|
47
64
|
* Calculates an eviction score based on age and frequency of access.
|
|
48
65
|
* Higher scores mean more likelihood to be evicted.
|
|
49
|
-
*
|
|
66
|
+
* The scoring system uses weighted factors: 30% recency and 70% frequency.
|
|
67
|
+
* @param value The cache entry to score.
|
|
68
|
+
* @param now The current timestamp for calculating age.
|
|
69
|
+
* @returns A numeric score representing how likely the entry should be evicted.
|
|
50
70
|
*/static _calculateEvictionScore(a,b){const c=b-a.lastAccess,d=a.hitCount;// Weighted scoring: recency 30%, frequency 70% - favors frequently accessed items
|
|
51
71
|
return .3*(c/1e3)+.7*(1e3/(d+1))}/**
|
|
52
72
|
* The main prop processing pipeline. It separates cacheable and non-cacheable props,
|
|
53
73
|
* generates a signature for caching, and assembles the final props object.
|
|
54
|
-
*
|
|
74
|
+
* This method applies optimizations like fast-path for simple props and hybrid caching strategy.
|
|
75
|
+
* @param element The element type for which props are being processed.
|
|
76
|
+
* @param rawProps The original props to process.
|
|
77
|
+
* @param stableKey The stable key used for child normalization (optional).
|
|
78
|
+
* @returns The processed props object ready for rendering.
|
|
55
79
|
*/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
80
|
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
|
-
|
|
81
|
+
const k={},l={},m=Object.keys(j);// 1. Categorize props into cacheable (primitives) and non-cacheable (objects/functions).
|
|
82
|
+
// Optimization: Use Object.keys loop instead of for..in for better performance and safety
|
|
83
|
+
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
|
|
84
|
+
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
85
|
// DOM props are always processed fresh.
|
|
61
86
|
// 4. Assemble the final CSS object.
|
|
62
87
|
// --- Child Normalization ---
|
|
63
88
|
// --- Final Assembly ---
|
|
64
|
-
return omitUndefined(_objectSpread(_objectSpread({ref:d,key:e,css:
|
|
89
|
+
return omitUndefined(_objectSpread(_objectSpread({ref:d,key:e,css:r},q),{},{disableEmotion:h,nativeProps:omitUndefined(i),children:s}))}/**
|
|
65
90
|
* Processes and normalizes children of the node.
|
|
66
91
|
* Converts raw children (React elements, primitives, or other BaseNodes) into a consistent format.
|
|
92
|
+
* Applies optimizations for single and multiple children scenarios.
|
|
67
93
|
* @param children The raw children to process.
|
|
68
94
|
* @param disableEmotion If true, emotion styling will be disabled for these children.
|
|
69
95
|
* @param parentStableKey The stable key of the parent node, used for generating unique keys for children.
|
|
70
|
-
|
|
96
|
+
* @returns The processed children in normalized format.
|
|
97
|
+
*/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)
|
|
98
|
+
// Fast path for single element array
|
|
99
|
+
// General case: multiple children
|
|
100
|
+
}/**
|
|
71
101
|
* Determines if a node should update based on its dependency array.
|
|
72
102
|
* Uses a shallow comparison, similar to React's `useMemo` and `useCallback`.
|
|
73
|
-
*
|
|
103
|
+
* On server environments, always returns true since SSR has no concept of re-renders.
|
|
104
|
+
* @param prevDeps Previous dependency array to compare.
|
|
105
|
+
* @param newDeps New dependency array to compare.
|
|
106
|
+
* @param parentBlocked Flag indicating if the parent is blocked from updating.
|
|
107
|
+
* @returns True if the node should update, false otherwise.
|
|
74
108
|
*/static shouldNodeUpdate(a,b,c){// SSR has no concept of re-renders, so deps system doesn't apply
|
|
75
109
|
return!!NodeUtil.isServer||!c&&(!(void 0!==b)||!(void 0!==a)||b.length!==a.length||!!b.some((b,c)=>!Object.is(b,a[c])));// No deps array means always update.
|
|
76
110
|
// First render for this keyed component, or no previous deps.
|
|
@@ -81,7 +115,12 @@ return!!NodeUtil.isServer||!c&&(!(void 0!==b)||!(void 0!==a)||b.length!==a.lengt
|
|
|
81
115
|
* The core normalization function for a single child. It takes any valid `NodeElement`
|
|
82
116
|
* (primitive, React element, function, `BaseNode` instance) and converts it into a standardized `BaseNode`
|
|
83
117
|
* instance if it isn't one already. This ensures a consistent structure for the iterative renderer.
|
|
84
|
-
*
|
|
118
|
+
* Handles various node types including primitives, BaseNode instances, function-as-children, React elements,
|
|
119
|
+
* component classes, and component instances.
|
|
120
|
+
* @param node The node element to process and normalize.
|
|
121
|
+
* @param disableEmotion If true, emotion styling will be disabled for this node.
|
|
122
|
+
* @param stableKey The stable key for positional information in parent-child relationships.
|
|
123
|
+
* @returns The normalized node element in BaseNode format.
|
|
85
124
|
*/static processRawNode(a,b,c){// Primitives and null/undefined are returned as-is.
|
|
86
125
|
if(null===a||a===void 0||"string"==typeof a||"number"==typeof a||"boolean"==typeof a)return a;// If it's already a BaseNode, clone it with a positional key if available.
|
|
87
126
|
if(NodeUtil.isNodeInstance(a)){const d=c||b&&!a.rawProps.disableEmotion;if(d){// Create a new BaseNode instance.
|
|
@@ -94,7 +133,9 @@ return isReactClassComponent(a)||isMemo(a)||isForwardRef(a)?new BaseNode(a,{disa
|
|
|
94
133
|
}/**
|
|
95
134
|
* A helper to reliably identify if a given function is a "function-as-a-child" (render prop)
|
|
96
135
|
* rather than a standard Function Component.
|
|
97
|
-
*
|
|
136
|
+
* Distinguishes between render prop functions and component functions by checking for React component signatures.
|
|
137
|
+
* @param node The node to check.
|
|
138
|
+
* @returns True if the node is a function-as-a-child, false otherwise.
|
|
98
139
|
*/static isFunctionChild(a){if("function"!=typeof a||isReactClassComponent(a)||isMemo(a)||isForwardRef(a))return!1;try{return!(a.prototype&&"function"==typeof a.prototype.render)}catch(a){return __DEBUG__&&console.error("MeoNode: Error checking if a node is a function child.",a),!0}}/**
|
|
99
140
|
* A special internal React component used to render "function-as-a-child" (render prop) patterns.
|
|
100
141
|
* When a `BaseNode` receives a function as its `children` prop, it wraps that function
|
|
@@ -103,11 +144,9 @@ return isReactClassComponent(a)||isMemo(a)||isForwardRef(a)?new BaseNode(a,{disa
|
|
|
103
144
|
*
|
|
104
145
|
* This allows `BaseNode` to support render props while maintaining its internal processing
|
|
105
146
|
* and normalization logic for the dynamically generated content.
|
|
106
|
-
* @
|
|
107
|
-
* @param
|
|
108
|
-
* @
|
|
109
|
-
* @param {boolean} [props.disableEmotion] Inherited flag to disable Emotion styling for children.
|
|
110
|
-
* @returns {ReactNode | null | undefined} The processed and rendered output of the render function.
|
|
147
|
+
* @param render The function-as-a-child to execute.
|
|
148
|
+
* @param disableEmotion Inherited flag to disable Emotion styling for children.
|
|
149
|
+
* @returns The processed and rendered output of the render function, or null if an error occurs.
|
|
111
150
|
*/static functionRenderer({render:a,disableEmotion:b}){let c;try{// Execute the render prop function to get its output.
|
|
112
151
|
c=a()}catch(a){// If the render function throws, treat its output as null to prevent crashes.
|
|
113
152
|
__DEBUG__&&console.error("MeoNode: Error executing function-as-a-child.",a),c=null}// Handle null or undefined results directly, as they are valid React render outputs.
|
|
@@ -133,7 +172,10 @@ return d?NodeUtil.renderProcessedNode({processedElement:d,disableEmotion:b}):c;/
|
|
|
133
172
|
*
|
|
134
173
|
* This method is part of the child processing pipeline, converting internal `NodeElement` representations
|
|
135
174
|
* into actual React elements that can be rendered by React.
|
|
136
|
-
* @
|
|
175
|
+
* @param processedElement The processed node element to render.
|
|
176
|
+
* @param passedKey Optional key to apply to the rendered element.
|
|
177
|
+
* @param disableEmotion Flag to disable emotion styling if needed.
|
|
178
|
+
* @returns The rendered ReactNode.
|
|
137
179
|
*/static renderProcessedNode({processedElement:a,passedKey:b,disableEmotion:c}){// Initialize an object to hold common props that might be applied to the new BaseNode.
|
|
138
180
|
const d={};// If a `passedKey` is provided, add it to `commonBaseNodeProps`.
|
|
139
181
|
// This key is typically used for React's reconciliation process.
|
|
@@ -153,12 +195,47 @@ return isReactClassComponent(a)?new BaseNode(a,_objectSpread(_objectSpread({},d)
|
|
|
153
195
|
}/**
|
|
154
196
|
* Ensures that the necessary DOM element and React root are available for portal rendering.
|
|
155
197
|
* This is only executed on the client-side.
|
|
156
|
-
*
|
|
198
|
+
* Handles cleanup of stale infrastructure and creates new infrastructure as needed.
|
|
199
|
+
* @param node The node instance that requires portal infrastructure.
|
|
200
|
+
* @returns True if portal infrastructure is ready, false on server or if setup fails.
|
|
157
201
|
*/static ensurePortalInfrastructure(a){if(NodeUtil.isServer)return!1;let b=NodeUtil.portalInfrastructure.get(a);// Check if infrastructure exists and is still connected
|
|
158
202
|
if(b?.domElement?.isConnected&&b?.reactRoot)return!0;// Clean up stale or disconnected infrastructure
|
|
159
203
|
if(b&&(!b.domElement?.isConnected||!b.reactRoot)){try{b.reactRoot?.unmount?.()}catch(a){__DEBUG__&&console.error("MeoNode: Error unmounting stale portal root.",a)}NodeUtil.cleanupPortalInfra(b),NodeUtil.portalInfrastructure.delete(a),b=void 0}// Create new infrastructure
|
|
160
204
|
const c=document.createElement("div");document.body.appendChild(c);const d=createRoot(c),e={render:d.render.bind(d),unmount:d.unmount.bind(d),update:()=>{}// Placeholder, will be overridden
|
|
161
|
-
};return b={domElement:c,reactRoot:e},NodeUtil.portalInfrastructure.set(a,b),BaseNode.portalCleanupRegistry.register(a,{domElement:c,reactRoot:e},a),!0}
|
|
205
|
+
};return b={domElement:c,reactRoot:e},NodeUtil.portalInfrastructure.set(a,b),BaseNode.portalCleanupRegistry.register(a,{domElement:c,reactRoot:e},a),!0}/**
|
|
206
|
+
* Cleans up portal infrastructure by unmounting the React root and removing the DOM element.
|
|
207
|
+
* This ensures proper memory cleanup and prevents memory leaks.
|
|
208
|
+
* @param infra The infrastructure object containing the DOM element and React root to clean up.
|
|
209
|
+
*/static cleanupPortalInfra(a){try{a.reactRoot?.unmount&&a.reactRoot.unmount()}catch(a){__DEBUG__&&console.error("Portal cleanup error:",a)}try{a.domElement?.isConnected&&a.domElement.remove()}catch(a){__DEBUG__&&console.error("DOM removal error:",a)}}}/**
|
|
162
210
|
* A min-heap implementation for efficient priority queue operations.
|
|
163
211
|
* Used for O(log n) eviction in the LRU cache system.
|
|
164
|
-
*/_NodeUtil=NodeUtil,_defineProperty(NodeUtil,"isServer","undefined"==typeof window),_defineProperty(NodeUtil,"_functionSignatureCache",new WeakMap),_defineProperty(NodeUtil,"CACHE_SIZE_LIMIT",500),_defineProperty(NodeUtil,"CACHE_CLEANUP_BATCH",50),_defineProperty(NodeUtil,"_cssCache",new WeakMap),_defineProperty(NodeUtil,"CRITICAL_PROPS",new Set(["css","className","disableEmotion","props"])),_defineProperty(NodeUtil,"portalInfrastructure",new WeakMap),_defineProperty(NodeUtil,"isNodeInstance",a=>"object"==typeof a&&null!==a&&"element"in a&&"function"==typeof a.render&&"function"==typeof a.toPortal&&"isBaseNode"in a),_defineProperty(NodeUtil,"isStyleProp",_NodeUtil.isServer||"undefined"==typeof document?()=>!1:a=>a in document.body.style);class MinHeap{
|
|
212
|
+
*/_NodeUtil=NodeUtil,_defineProperty(NodeUtil,"isServer","undefined"==typeof window),_defineProperty(NodeUtil,"_functionSignatureCache",new WeakMap),_defineProperty(NodeUtil,"CACHE_SIZE_LIMIT",500),_defineProperty(NodeUtil,"CACHE_CLEANUP_BATCH",50),_defineProperty(NodeUtil,"_cssCache",new WeakMap),_defineProperty(NodeUtil,"CRITICAL_PROPS",new Set(["css","className","disableEmotion","props"])),_defineProperty(NodeUtil,"portalInfrastructure",new WeakMap),_defineProperty(NodeUtil,"isNodeInstance",a=>"object"==typeof a&&null!==a&&"element"in a&&"function"==typeof a.render&&"function"==typeof a.toPortal&&"isBaseNode"in a),_defineProperty(NodeUtil,"isStyleProp",_NodeUtil.isServer||"undefined"==typeof document?()=>!1:a=>a in document.body.style);class MinHeap{/**
|
|
213
|
+
* Constructs a new MinHeap with the provided comparator function.
|
|
214
|
+
* @param comparator A function that compares two elements and returns a negative value if the first is smaller,
|
|
215
|
+
* zero if they are equal, or a positive value if the first is larger.
|
|
216
|
+
*/constructor(a){_defineProperty(this,"heap",[]),this.comparator=a}/**
|
|
217
|
+
* Returns the number of elements in the heap.
|
|
218
|
+
* @returns The current size of the heap.
|
|
219
|
+
*/size(){return this.heap.length}/**
|
|
220
|
+
* Checks if the heap is empty.
|
|
221
|
+
* @returns True if the heap has no elements, false otherwise.
|
|
222
|
+
*/isEmpty(){return 0===this.size()}/**
|
|
223
|
+
* Adds a new value to the heap and maintains the heap property by bubbling it up to the correct position.
|
|
224
|
+
* @param value The value to add to the heap.
|
|
225
|
+
*/push(a){this.heap.push(a),this.bubbleUp()}/**
|
|
226
|
+
* Removes and returns the smallest element from the heap (the root).
|
|
227
|
+
* After removal, it maintains the heap property by bubbling down the new root.
|
|
228
|
+
* @returns The smallest element in the heap, or undefined if the heap is empty.
|
|
229
|
+
*/pop(){if(!this.isEmpty()){this.swap(0,this.size()-1);const a=this.heap.pop();return this.bubbleDown(),a}}/**
|
|
230
|
+
* Moves the element at the specified index up the heap until the heap property is restored.
|
|
231
|
+
* This is used after inserting a new element to maintain the heap structure.
|
|
232
|
+
* @param index The index of the element to bubble up. Defaults to the last element in the heap.
|
|
233
|
+
*/bubbleUp(a=this.size()-1){for(;0<a;){const b=Math.floor((a-1)/2);if(0>=this.comparator(this.heap[b],this.heap[a]))break;this.swap(b,a),a=b}}/**
|
|
234
|
+
* Moves the element at the specified index down the heap until the heap property is restored.
|
|
235
|
+
* This is used after removing the root element to maintain the heap structure.
|
|
236
|
+
* @param index The index of the element to bubble down. Defaults to the root element (index 0).
|
|
237
|
+
*/bubbleDown(a=0){for(const b=this.size()-1;;){const c=2*a+1,d=2*a+2;let e=a;if(c<=b&&0>this.comparator(this.heap[c],this.heap[e])&&(e=c),d<=b&&0>this.comparator(this.heap[d],this.heap[e])&&(e=d),e===a)break;this.swap(a,e),a=e}}/**
|
|
238
|
+
* Swaps the elements at the two specified indices in the heap array.
|
|
239
|
+
* @param i The index of the first element to swap.
|
|
240
|
+
* @param j The index of the second element to swap.
|
|
241
|
+
*/swap(a,b){[this.heap[a],this.heap[b]]=[this.heap[b],this.heap[a]]}}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import type { CSSProperties } from '@emotion/serialize';
|
|
2
2
|
import type { CssProp, Theme } from '../types/node.type.js';
|
|
3
|
-
/**
|
|
4
|
-
* Parsed flex shorthand components for CSS flex property
|
|
5
|
-
* @interface FlexComponents
|
|
6
|
-
* @property grow - The flex-grow value (how much the item should grow)
|
|
7
|
-
* @property shrink - The flex-shrink value (how much the item should shrink)
|
|
8
|
-
* @property basis - The flex-basis value (initial main size before free space is distributed)
|
|
9
|
-
*/
|
|
10
3
|
interface FlexComponents {
|
|
11
4
|
grow: number;
|
|
12
5
|
shrink: number;
|
|
13
6
|
basis: string | number;
|
|
14
7
|
}
|
|
15
8
|
export declare class ThemeUtil {
|
|
16
|
-
private constructor();
|
|
17
9
|
private static themeCache;
|
|
10
|
+
private constructor();
|
|
18
11
|
/**
|
|
19
12
|
* Parses a CSS flex shorthand property into its individual components.
|
|
20
13
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.util.d.ts","sourceRoot":"","sources":["../../src/util/theme.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAe,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"theme.util.d.ts","sourceRoot":"","sources":["../../src/util/theme.util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAe,MAAM,yBAAyB,CAAA;AA+F1E,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAmC;IAE5D,OAAO,eAAiB;IAExB;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAc,kBAAkB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,cAAc,GAAG,IAAI,CA2BnF;IAED,OAAc,aAAa,uDAM1B;IAED;;;;;;;;OAQG;IACH,OAAc,mBAAmB,GAAI,CAAC;;YA0HrC;IAED,OAAc,eAAe,aAE5B;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqDG;IACH,OAAc,mBAAmB,yBAmEhC;CACF"}
|
package/dist/util/theme.util.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
const _excluded=["flex"];var _ThemeUtil;function _objectWithoutProperties(a,b){if(null==a)return{};var c,d,e=_objectWithoutPropertiesLoose(a,b);if(Object.getOwnPropertySymbols){var f=Object.getOwnPropertySymbols(a);for(d=0;d<f.length;d++)c=f[d],-1===b.indexOf(c)&&{}.propertyIsEnumerable.call(a,c)&&(e[c]=a[c])}return e}function _objectWithoutPropertiesLoose(a,b){if(null==a)return{};var c={};for(var d in a)if({}.hasOwnProperty.call(a,d)){if(-1!==b.indexOf(d))continue;c[d]=a[d]}return c}function ownKeys(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);b&&(d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable})),c.push.apply(c,d)}return c}function _objectSpread(a){for(var b,c=1;c<arguments.length;c++)b=null==arguments[c]?{}:arguments[c],c%2?ownKeys(Object(b),!0).forEach(function(c){_defineProperty(a,c,b[c])}):Object.getOwnPropertyDescriptors?Object.defineProperties(a,Object.getOwnPropertyDescriptors(b)):ownKeys(Object(b)).forEach(function(c){Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))});return a}function _defineProperty(a,b,c){return(b=_toPropertyKey(b))in a?Object.defineProperty(a,b,{value:c,enumerable:!0,configurable:!0,writable:!0}):a[b]=c,a}function _toPropertyKey(a){var b=_toPrimitive(a,"string");return"symbol"==typeof b?b:b+""}function _toPrimitive(a,b){if("object"!=typeof a||!a)return a;var c=a[Symbol.toPrimitive];if(void 0!==c){var d=c.call(a,b||"default");if("object"!=typeof d)return d;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===b?String:Number)(a)}import{ObjHelper}from"../helper/obj.helper.js";import{getValueByPath}from"../helper/common.helper.js";/**
|
|
2
2
|
* Cache manager for theme resolution operations.
|
|
3
|
-
*/class ThemeResolverCache{constructor(){_defineProperty(this,"CACHE_SIZE_LIMIT",500),_defineProperty(this,"CACHE_EVICTION_BATCH_SIZE",50),_defineProperty(this,"_resolutionCache",new Map),_defineProperty(this,"_pathLookupCache",new Map),_defineProperty(this,"_themeRegex",/theme\.([a-zA-Z0-9_.-]+)/g)}static getInstance(){return ThemeResolverCache._instance||(ThemeResolverCache._instance=new ThemeResolverCache),ThemeResolverCache._instance}/**
|
|
3
|
+
*/class ThemeResolverCache{constructor(){_defineProperty(this,"CACHE_SIZE_LIMIT",500),_defineProperty(this,"CACHE_EVICTION_BATCH_SIZE",50),_defineProperty(this,"_resolutionCache",new Map),_defineProperty(this,"_pathLookupCache",new Map),_defineProperty(this,"_themeRegex",/theme\.([a-zA-Z0-9_.-]+)/g)}static getInstance(){return ThemeResolverCache._instance||(ThemeResolverCache._instance=new ThemeResolverCache),ThemeResolverCache._instance}getResolution(a,b){const c=this._generateCacheKey(a,b),d=this._resolutionCache.get(c);return d&&(this._resolutionCache.delete(c),this._resolutionCache.set(c,d)),d||null}setResolution(a,b,c){const d=this._generateCacheKey(a,b);this._resolutionCache.set(d,c),this._resolutionCache.size>this.CACHE_SIZE_LIMIT&&this._evict(this._resolutionCache)}getPathLookup(a,b){const c=`${ObjHelper.stringify(a)}_${b}`,d=this._pathLookupCache.get(c);return d&&(this._pathLookupCache.delete(c),this._pathLookupCache.set(c,d)),d??null}setPathLookup(a,b,c){const d=`${ObjHelper.stringify(a)}_${b}`;this._pathLookupCache.set(d,c),this._pathLookupCache.size>this.CACHE_SIZE_LIMIT&&this._evict(this._pathLookupCache)}getThemeRegex(){return this._themeRegex.lastIndex=0,this._themeRegex}shouldCache(){return"undefined"==typeof window}clear(){this._resolutionCache.clear(),this._pathLookupCache.clear()}/**
|
|
4
4
|
* Generate a stable cache key from object and theme, including the theme mode.
|
|
5
5
|
*/_generateCacheKey(a,b){// Including theme.mode is critical for cache correctness.
|
|
6
|
-
return`${ObjHelper.stringify(a)}_${b.mode}_${ObjHelper.stringify(b.system)}`}
|
|
7
|
-
* Parsed flex shorthand components for CSS flex property
|
|
8
|
-
* @interface FlexComponents
|
|
9
|
-
* @property grow - The flex-grow value (how much the item should grow)
|
|
10
|
-
* @property shrink - The flex-shrink value (how much the item should shrink)
|
|
11
|
-
* @property basis - The flex-basis value (initial main size before free space is distributed)
|
|
12
|
-
*/_defineProperty(ThemeResolverCache,"_instance",null);export class ThemeUtil{constructor(){}/**
|
|
6
|
+
return`${ObjHelper.stringify(a)}_${b.mode}_${ObjHelper.stringify(b.system)}`}_evict(a){const b=a.keys();for(let c=0;c<this.CACHE_EVICTION_BATCH_SIZE;c++){const c=b.next().value;if(c)a.delete(c);else break}}}_defineProperty(ThemeResolverCache,"_instance",null);export class ThemeUtil{constructor(){}/**
|
|
13
7
|
* Parses a CSS flex shorthand property into its individual components.
|
|
14
8
|
*
|
|
15
9
|
* The CSS flex property is a shorthand for flex-grow, flex-shrink, and flex-basis.
|
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.11",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main.js",
|
|
7
7
|
"types": "./dist/main.d.ts",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"scripts": {
|
|
32
32
|
"watch:build": "yarn dlx nodemon --watch src --ext ts,tsx,js,json --exec \"yarn build\"",
|
|
33
33
|
"lint": "eslint --fix",
|
|
34
|
-
"test": "NODE_OPTIONS='--expose-gc' node --stack-size=10000 --experimental-vm-modules
|
|
35
|
-
"test:perf": "NODE_OPTIONS='--expose-gc' node --stack-size=10000 --experimental-vm-modules
|
|
36
|
-
"test:all": "NODE_OPTIONS='--expose-gc' node --stack-size=10000 --experimental-vm-modules
|
|
34
|
+
"test": "NODE_OPTIONS='--expose-gc' node --stack-size=10000 --max-old-space-size=8192 --experimental-vm-modules $(yarn bin jest) --ci --verbose --testPathIgnorePatterns=\"tests/performance.test.ts\" --testPathIgnorePatterns=\"tests/react-createelement-comparison.test.ts\"",
|
|
35
|
+
"test:perf": "NODE_OPTIONS='--expose-gc' node --stack-size=10000 --max-old-space-size=8192 --experimental-vm-modules $(yarn bin jest) --ci --verbose tests/performance.test.ts tests/react-createelement-comparison.test.ts",
|
|
36
|
+
"test:all": "NODE_OPTIONS='--expose-gc' node --stack-size=10000 --max-old-space-size=8192 --experimental-vm-modules $(yarn bin jest) --ci --verbose",
|
|
37
37
|
"prebuild": "yarn lint && yarn test:all",
|
|
38
38
|
"build": "yarn prebuild && rm -rf ./dist && babel src --out-dir dist --extensions \".ts,.tsx,.js\" && tsgo -p tsconfig.build.json --diagnostics && tsc-alias -p tsconfig.build.json",
|
|
39
39
|
"publish:pre": "./prepublish.sh && yarn build && yarn version -i prerelease && yarn npm publish --tag next",
|
|
@@ -52,7 +52,9 @@
|
|
|
52
52
|
"@babel/preset-typescript": "^7.28.5",
|
|
53
53
|
"@emotion/cache": "^11.14.0",
|
|
54
54
|
"@emotion/jest": "^11.14.2",
|
|
55
|
+
"@emotion/styled": "^11.14.1",
|
|
55
56
|
"@eslint/js": "^9.39.1",
|
|
57
|
+
"@mui/material": "^7.3.5",
|
|
56
58
|
"@testing-library/dom": "^10.4.1",
|
|
57
59
|
"@testing-library/jest-dom": "^6.9.1",
|
|
58
60
|
"@testing-library/react": "^16.3.0",
|
|
@@ -81,7 +83,7 @@
|
|
|
81
83
|
"typescript": "^5.9.3",
|
|
82
84
|
"typescript-eslint": "^8.47.0"
|
|
83
85
|
},
|
|
84
|
-
"packageManager": "yarn@4.
|
|
86
|
+
"packageManager": "yarn@4.11.0",
|
|
85
87
|
"peerDependencies": {
|
|
86
88
|
"@emotion/cache": ">=11.14.0",
|
|
87
89
|
"react": ">=19.2.0",
|