@meonode/ui 0.4.13 → 0.4.14
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 +423 -208
- package/dist/cjs/core.node.js +1 -1
- package/dist/cjs/util/node.util.js +1 -1
- package/dist/esm/core.node.d.ts.map +1 -1
- package/dist/esm/core.node.js +1 -1
- package/dist/esm/util/node.util.d.ts +12 -0
- package/dist/esm/util/node.util.d.ts.map +1 -1
- package/dist/esm/util/node.util.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,236 +2,354 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.4.14] - 2025-11-23
|
|
6
|
+
|
|
7
|
+
### Perf
|
|
8
|
+
|
|
9
|
+
- **cache**: enforce dependency-based caching with shouldCacheElement helper ([
|
|
10
|
+
`0df9b3b`](https://github.com/l7aromeo/meonode-ui/commit/0df9b3b28c7874acbf33826ab7bb17f8b0464250))
|
|
11
|
+
- Introduces NodeUtil.shouldCacheElement() helper to centralize and enforce the opt-in caching strategy where only
|
|
12
|
+
nodes with explicit dependencies are cached
|
|
13
|
+
- Completes the memory optimization by closing loopholes where nodes without dependencies were still being cached
|
|
14
|
+
based on stableKey alone
|
|
15
|
+
- Replaces 4 inconsistent cache eligibility checks in BaseNode.render():
|
|
16
|
+
- Cache lookup for parent nodes
|
|
17
|
+
- Cache lookup for child nodes
|
|
18
|
+
- Cache storage during rendering
|
|
19
|
+
- MeoNodeUnmounter wrapping decision
|
|
20
|
+
- Impact: Reduces memory usage, ensures mount tracking and cache operations stay in sync, improves code
|
|
21
|
+
maintainability
|
|
22
|
+
|
|
23
|
+
### Test
|
|
24
|
+
|
|
25
|
+
- **memoization**: refine test to assert precise cache size after rendering components ([
|
|
26
|
+
`8ded697`](https://github.com/l7aromeo/meonode-ui/commit/8ded6974cc1565c384abe1a3ca54e1f7bc8a9619))
|
|
27
|
+
|
|
28
|
+
### Chore
|
|
29
|
+
|
|
30
|
+
- **type**: remove src/types/env.d.ts as it is no longer needed
|
|
31
|
+
|
|
5
32
|
## [0.4.13] - 2025-11-23
|
|
6
33
|
|
|
7
34
|
### Fix
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
35
|
+
|
|
36
|
+
- **props**: improve prop handling and prevent leakage ([
|
|
37
|
+
`73cc696`](https://github.com/l7aromeo/meonode-ui/commit/73cc696b3df8c1bd2ddef789de58febc6cd2f1c5))
|
|
38
|
+
- This commit refactors prop handling within the MeoNode ecosystem to ensure that internal processing props are not
|
|
39
|
+
leaked to the DOM.
|
|
40
|
+
- Key changes:
|
|
41
|
+
- The `MeoNodeUnmounter` is updated to correctly isolate and pass through props intended for the underlying DOM
|
|
42
|
+
element, improving compatibility with libraries like MUI.
|
|
43
|
+
- Internal props such as `node`, `css`, and `disableEmotion` are now explicitly prevented from being rendered as
|
|
44
|
+
HTML attributes.
|
|
45
|
+
- Added comprehensive tests to verify that standard HTML attributes are passed through while internal props are
|
|
46
|
+
successfully filtered out.
|
|
47
|
+
- This improves the robustness and predictability of component rendering.
|
|
15
48
|
|
|
16
49
|
### Test
|
|
17
|
-
|
|
18
|
-
|
|
50
|
+
|
|
51
|
+
- **props**: add tests for prop handling and leakage ([
|
|
52
|
+
`a508e10`](https://github.com/l7aromeo/meonode-ui/commit/a508e107539d9ce84e8d99b63a0af329b28f3249))
|
|
53
|
+
- Added new tests to verify that component props are correctly passed as HTML attributes, handle createNode and
|
|
54
|
+
Node() correctly, and crucially, that internal MeoNode props are not leaked to the DOM.
|
|
19
55
|
|
|
20
56
|
### Chore
|
|
21
|
-
|
|
57
|
+
|
|
58
|
+
- **core**: remove unnecessary type assertion from finalChildren assignment ([
|
|
59
|
+
`827b3ef`](https://github.com/l7aromeo/meonode-ui/commit/827b3ef4490bca08d58ef5fe1fd885aadbbb1524))
|
|
22
60
|
|
|
23
61
|
## [0.4.12] - 2025-11-21
|
|
24
62
|
|
|
25
63
|
### Feat
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
64
|
+
|
|
65
|
+
- **build**: migrate from Babel to Rollup with ESM and CJS support ([
|
|
66
|
+
`70326a1`](https://github.com/l7aromeo/meonode-ui/commit/70326a107259c095d571b838dda15ffbf845af1d))
|
|
67
|
+
- Replace Babel build system with Rollup configuration to prevent output legacy javascript code
|
|
68
|
+
- Add support for both ESM and CJS output formats
|
|
69
|
+
- Update package.json exports to point to new build outputs
|
|
70
|
+
- Add Rollup plugins for TypeScript, commonjs, minification, and preserve directives
|
|
71
|
+
- Remove Babel-related dependencies and configuration files
|
|
72
|
+
- Update tsconfig.json to use 'preserve' module setting and bundler resolution
|
|
73
|
+
- Configure build to output to separate ESM and CJS directories
|
|
34
74
|
|
|
35
75
|
## [0.4.11] - 2025-11-21
|
|
36
76
|
|
|
37
77
|
### Fix
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
|
|
78
|
+
|
|
79
|
+
- **core**: enhance MeoNodeUnmounter cleanup logic and support additional props cloning ([
|
|
80
|
+
`02c17f7`](https://github.com/l7aromeo/meonode-ui/commit/02c17f7))
|
|
81
|
+
- Refactor MeoNodeUnmounter to use useEffectEvent for stable cleanup on unmount
|
|
82
|
+
- Cleanup removes node from BaseNode.elementCache, untracks mount via MountTrackerUtil, unregisters from
|
|
83
|
+
BaseNode.cacheCleanupRegistry, and clears lastSignature to prevent memory leaks
|
|
84
|
+
- Support cloning and forwarding additional props to valid React children elements
|
|
42
85
|
|
|
43
86
|
### Refactor
|
|
44
|
-
|
|
45
|
-
- **
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
87
|
+
|
|
88
|
+
- **node.util**: enhance documentation for utility methods and improve clarity ([
|
|
89
|
+
`ee42c24`](https://github.com/l7aromeo/meonode-ui/commit/ee42c24))
|
|
90
|
+
- **theme**: reorder ThemeResolverCache methods for clarity ([
|
|
91
|
+
`cb842c8`](https://github.com/l7aromeo/meonode-ui/commit/cb842c8))
|
|
92
|
+
- Moved `_generateCacheKey` and `_evict` methods below the main logic in `ThemeResolverCache` for better readability
|
|
93
|
+
and organization
|
|
94
|
+
- Removed duplicate declaration of `_instance` property
|
|
95
|
+
- Kept functionality unchanged, improving code structure and maintainability
|
|
49
96
|
|
|
50
97
|
### Test
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
98
|
+
|
|
99
|
+
- **perf**: add memory leak detection test for navigation cycles and improve formatMemory function ([
|
|
100
|
+
`ba139fc`](https://github.com/l7aromeo/meonode-ui/commit/ba139fc))
|
|
101
|
+
- Added a new performance test to detect memory leaks during repeated navigation cycles between pages
|
|
102
|
+
- The test measures heap memory usage before, during, and after navigation, ensuring memory growth stays within
|
|
103
|
+
acceptable limits
|
|
104
|
+
- Enhanced the formatMemory utility to correctly handle negative byte values and added JSDoc comments for clarity
|
|
105
|
+
- Removed an obsolete shallowly equal props performance test to streamline the test suite
|
|
106
|
+
- **unmounter**: add regression test for MeoNodeUnmounter to forward implicit props in MUI RadioGroup integration ([
|
|
107
|
+
`2ecaabd`](https://github.com/l7aromeo/meonode-ui/commit/2ecaabd))
|
|
108
|
+
- Added a test to ensure MeoNodeUnmounter correctly forwards props injected via React.cloneElement, addressing
|
|
109
|
+
issues with libraries like MUI where RadioGroup injects 'checked' and 'onChange' into Radio components
|
|
110
|
+
- This prevents swallowing of props and verifies proper behavior of controlled radio inputs
|
|
111
|
+
- Also updated an existing cache size assertion to allow equality, reflecting improved mount tracking
|
|
112
|
+
- **perf**: update react-createelement comparison tests with 5000 nested nodes and fix typings ([
|
|
113
|
+
`b345ec0`](https://github.com/l7aromeo/meonode-ui/commit/b345ec0))
|
|
114
|
+
- Changed rerender to use React.cloneElement<any> for proper typing
|
|
115
|
+
- Updated NUM_NODES constant to 5000 for nested structure tests
|
|
116
|
+
- Removed redundant comments about node count reduction to prevent stack overflow
|
|
64
117
|
|
|
65
118
|
### Chore
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
-
|
|
119
|
+
|
|
120
|
+
- **deps**: upgrade devDependencies and update test scripts ([
|
|
121
|
+
`2ea128e`](https://github.com/l7aromeo/meonode-ui/commit/2ea128e))
|
|
122
|
+
- add new devDependencies: @emotion/is-prop-valid@1.4.0, @emotion/styled@11.14.1, @mui/material@7.3.5, and related
|
|
123
|
+
packages
|
|
124
|
+
- update yarn to version 4.11.0
|
|
125
|
+
- update test scripts to increase max-old-space-size to 8192 and include react-createelement-comparison.test.ts in
|
|
126
|
+
test and perf runs
|
|
127
|
+
- update various package resolutions in yarn.lock to align with new versions and dependencies
|
|
128
|
+
- **babel**: add "builtIns": false to minify plugin configuration ([
|
|
129
|
+
`e16cdfb`](https://github.com/l7aromeo/meonode-ui/commit/e16cdfb))
|
|
72
130
|
- **yarn**: update yarnPath to version 4.11.0 ([`ecb6c68`](https://github.com/l7aromeo/meonode-ui/commit/ecb6c68))
|
|
73
131
|
|
|
74
132
|
### Docs
|
|
75
|
-
|
|
133
|
+
|
|
134
|
+
- **CONTRIBUTING**: improve formatting and readability of contribution guidelines ([
|
|
135
|
+
`a7462ed`](https://github.com/l7aromeo/meonode-ui/commit/a7462ed))
|
|
76
136
|
|
|
77
137
|
## [0.4.10] - 2025-11-20
|
|
78
138
|
|
|
79
139
|
### Fix
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
140
|
+
|
|
141
|
+
- **unmounter**: prevent redundant FinalizationRegistry callbacks ([
|
|
142
|
+
`59f5adf`](https://github.com/l7aromeo/meonode-ui/commit/59f5adf2f553aa49a88d1b44366b004d829ca107))
|
|
143
|
+
- Explicitly unregister node from cacheCleanupRegistry on unmount
|
|
144
|
+
- Prevents double cleanup when node is both unmounted and GC'd
|
|
145
|
+
- Improves cleanup efficiency and reduces unnecessary registry callbacks
|
|
84
146
|
|
|
85
147
|
### Perf
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
148
|
+
|
|
149
|
+
- **util**: optimize prop processing and child handling ([
|
|
150
|
+
`be26488`](https://github.com/l7aromeo/meonode-ui/commit/be26488e304629dd13851dfcaa7fedf43ad8b5c3))
|
|
151
|
+
- Conditional sort: only sort keys if length > 1 (eliminates unnecessary sorts)
|
|
152
|
+
- Replace for...in with Object.keys() iteration for better performance and safety
|
|
153
|
+
- Add fast path for single child processing (non-array or 1-element array)
|
|
154
|
+
- Avoid unnecessary array operations for common single-child case
|
|
155
|
+
- Performance impact:
|
|
156
|
+
- Reduced CPU overhead for small prop objects
|
|
157
|
+
- Faster iteration with Object.keys() vs for...in
|
|
158
|
+
- Eliminates array map() call for single child components
|
|
159
|
+
|
|
160
|
+
- **core**: add exception safety and optimize render method ([
|
|
161
|
+
`5aad000`](https://github.com/l7aromeo/meonode-ui/commit/5aad000335ff29f078a9d40192d5a70fe9b61d12))
|
|
162
|
+
- Wrap render logic in try-finally to ensure renderContext is always released
|
|
163
|
+
- Null out workStack slots before releasing to help GC
|
|
164
|
+
- Pre-allocate finalChildren array to avoid resizing during iteration
|
|
165
|
+
- Replace non-null assertion with explicit error handling for better debugging
|
|
166
|
+
- Add object pooling for workStack and renderedElements (reduces GC pressure)
|
|
167
|
+
- Pool capped at 50 contexts with 2048-item limit to prevent memory issues
|
|
168
|
+
- Exception safety ensures cleanup even if rendering throws
|
|
104
169
|
|
|
105
170
|
### Test
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
-
|
|
171
|
+
|
|
172
|
+
- **perf**: add React comparison tests with memory tracking ([
|
|
173
|
+
`bc66d54`](https://github.com/l7aromeo/meonode-ui/commit/bc66d540c4ffa4ad083322dbdbc201f652ea5314))
|
|
174
|
+
- Add comprehensive performance comparison between React.createElement and MeoNode
|
|
175
|
+
- Test both flat (10k nodes) and nested (5k nodes) structures
|
|
176
|
+
- Include memory usage measurements (initial + after 100 updates)
|
|
177
|
+
- Reduce nested nodes from 10k to 5k to prevent stack overflow with StyledRenderer
|
|
178
|
+
- Add table output with time and memory columns for clear comparison
|
|
179
|
+
- Add GC availability warning for accurate measurements
|
|
180
|
+
- Provides detailed performance and memory behavior insights during re-renders
|
|
181
|
+
- Avoids stack overflow in deeply nested test scenarios
|
|
182
|
+
|
|
183
|
+
- **performance**: enhance performance metrics report formatting and adjust thresholds ([
|
|
184
|
+
`6f3abe4`](https://github.com/l7aromeo/meonode-ui/commit/6f3abe4442938aa6cd414341fdf2aba25a9ece58))
|
|
185
|
+
- Improve table formatting for performance metrics
|
|
186
|
+
- Adjust test thresholds based on optimization results
|
|
187
|
+
- Enhanced reporting for better visibility into performance characteristics
|
|
120
188
|
|
|
121
189
|
## [0.4.9] - 2025-11-19
|
|
122
190
|
|
|
123
191
|
### Feat
|
|
124
|
-
|
|
192
|
+
|
|
193
|
+
- **build**: add support for @tests alias and include tests folder in build and test configs ([
|
|
194
|
+
`4dfd165`](https://github.com/l7aromeo/meonode-ui/commit/4dfd165fa52f93fe63ac7338344b91dfa5622c4b))
|
|
125
195
|
|
|
126
196
|
### Perf
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
197
|
+
|
|
198
|
+
- **core**: optimize rendering, caching, and memory management ([
|
|
199
|
+
`4f599be`](https://github.com/l7aromeo/meonode-ui/commit/4f599be44458fef208a30849545606b060c4ec6b))
|
|
200
|
+
- Reworks the core rendering loop to use a more efficient, manually-managed work stack with exponential growth,
|
|
201
|
+
reducing reallocations and improving performance for deep and wide node trees.
|
|
202
|
+
- Optimizes stable key generation by removing expensive shallow equality checks in favor of strict reference
|
|
203
|
+
equality, significantly speeding up prop processing for cached components.
|
|
204
|
+
- Implements a high-performance MinHeap-based LRU cache eviction strategy for prop processing, replacing a slower
|
|
205
|
+
sort-based method. This ensures that the most relevant props are kept in the cache with minimal overhead.
|
|
206
|
+
- Introduces CSS object hashing to accelerate prop signature generation, avoiding costly serialization of large
|
|
207
|
+
style objects.
|
|
208
|
+
- Fixes several memory leaks by ensuring proper cleanup of node properties (lastSignature, lastPropsObj) and
|
|
209
|
+
unregistering nodes from the cache cleanup registry upon eviction.
|
|
210
|
+
- Decouples element cache deletion from mount tracking to prevent race conditions and ensure reliable cleanup during
|
|
211
|
+
component unmounting.
|
|
134
212
|
|
|
135
213
|
### Refactor
|
|
136
|
-
|
|
214
|
+
|
|
215
|
+
- **build**: rename constants directory from constants to constant and update imports accordingly ([
|
|
216
|
+
`9531947`](https://github.com/l7aromeo/meonode-ui/commit/9531947af9b304c11c0865e8deafa1a633220753))
|
|
137
217
|
|
|
138
218
|
### Test
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
219
|
+
|
|
220
|
+
- **perf**: overhaul performance test suite ([
|
|
221
|
+
`e3bd6ac`](https://github.com/l7aromeo/meonode-ui/commit/e3bd6ac6ceca474f935da644ff0c23b2f1de7692))
|
|
222
|
+
- Introduces a new, structured performance reporting system using cli-table3 for clear, grouped metrics.
|
|
223
|
+
- Refactors the entire performance test suite into logical groups: Layout Rendering, Memory Management, and Prop
|
|
224
|
+
Processing.
|
|
225
|
+
- Adds comprehensive memory leak detection tests for heavy state changes and repeated mount/unmount cycles, using
|
|
226
|
+
forced garbage collection for more accurate heap analysis.
|
|
227
|
+
- Extracts the large, complex CSS object into a dedicated test constant for better separation of concerns.
|
|
228
|
+
- Updates memoization tests to align with the new, weighted LRU cache eviction scoring.
|
|
145
229
|
|
|
146
230
|
### Chore
|
|
147
|
-
|
|
231
|
+
|
|
232
|
+
- **deps**: update dependencies including typescript-eslint to 8.47.0 and add cli-table3 0.6.5 ([
|
|
233
|
+
`6064555`](https://github.com/l7aromeo/meonode-ui/commit/6064555f0108ed47f9b31e98c4758f7449a67ff2))
|
|
148
234
|
|
|
149
235
|
## [0.4.8] - 2025-11-18
|
|
150
236
|
|
|
151
237
|
### Feat
|
|
152
|
-
|
|
153
|
-
- **
|
|
154
|
-
-
|
|
155
|
-
|
|
156
|
-
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
238
|
+
|
|
239
|
+
- **client**: add generic type parameter to render function for stronger type safety ([
|
|
240
|
+
`90a770e`](https://github.com/l7aromeo/meonode-ui/commit/90a770e))
|
|
241
|
+
- **core**: improve BaseNode rendering with iterative traversal and memory optimizations ([
|
|
242
|
+
`1d5330a`](https://github.com/l7aromeo/meonode-ui/commit/1d5330a))
|
|
243
|
+
- Introduce WorkItem interface to represent nodes in the iterative work stack during BaseNode rendering.
|
|
244
|
+
- Replace recursive render traversal with an iterative depth-first approach using a preallocated workStack array for
|
|
245
|
+
better performance and reduced call stack usage.
|
|
246
|
+
- Implement dynamic resizing of workStack array to handle arbitrary tree sizes efficiently.
|
|
247
|
+
- Update BaseNode’s internal caching fields (`lastPropsRef` and `lastSignature`) to be public and consistently used
|
|
248
|
+
for stable key generation.
|
|
249
|
+
- Modify MeoNodeUnmounter component to accept BaseNode instance and clear its `lastPropsRef` and `lastSignature` on
|
|
250
|
+
unmount to prevent memory leaks.
|
|
251
|
+
- Refine type annotations across node utilities and factory functions for stronger type safety (
|
|
252
|
+
`Record<string, unknown>` instead of `any`).
|
|
253
|
+
- Optimize critical prop extraction logic by replacing Set and startsWith checks with faster inline charCode
|
|
254
|
+
comparisons.
|
|
255
|
+
- Clean up and clarify utility methods related to prop signature creation, shallow equality, and portal management.
|
|
256
|
+
- Improve node.util.ts by adjusting caching strategies, prop categorization, and React element handling for better
|
|
257
|
+
robustness and maintainability.
|
|
163
258
|
|
|
164
259
|
### Fixes
|
|
165
|
-
|
|
260
|
+
|
|
261
|
+
- **navigation-cache-manager**: add proper typing and global window declaration for cleanup flag ([
|
|
262
|
+
`6180d40`](https://github.com/l7aromeo/meonode-ui/commit/6180d40))
|
|
166
263
|
|
|
167
264
|
### Refactor
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
265
|
+
|
|
266
|
+
- improve typings and type safety in theme util and styled renderer ([
|
|
267
|
+
`dbe1f33`](https://github.com/l7aromeo/meonode-ui/commit/dbe1f33))
|
|
268
|
+
- Added explicit TypeScript types (e.g., CssProp, Record<string, unknown>) for variables and function signatures in
|
|
269
|
+
`styled-renderer.client.ts` and `theme.util.ts`.
|
|
270
|
+
- Updated cache maps to use more precise generic types for better type inference and safety.
|
|
271
|
+
- Enhanced `resolveObjWithTheme` and related theme resolution logic with stronger typing and nullish coalescing.
|
|
272
|
+
- Improved error handling for invalid theme path values.
|
|
273
|
+
- Applied copy-on-write pattern with properly typed arrays and objects during theme resolution.
|
|
274
|
+
- Strengthened type guards, e.g., `isPlainObject` type predicate.
|
|
275
|
+
- Minor fixes to variable declarations with explicit types for clarity and consistency.
|
|
176
276
|
|
|
177
277
|
### Chore
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
-
|
|
181
|
-
|
|
182
|
-
|
|
278
|
+
|
|
279
|
+
- **babel**: update preset-env targets and expand plugin exclusions ([
|
|
280
|
+
`f38cd24`](https://github.com/l7aromeo/meonode-ui/commit/f38cd24))
|
|
281
|
+
- Set preset-env targets to support ES modules
|
|
282
|
+
- Enable bugfixes option
|
|
283
|
+
- Add multiple plugins to exclude list for better optimization
|
|
284
|
+
- Clean up formatting of root, alias, extensions, and exclude fields
|
|
183
285
|
|
|
184
286
|
### Test
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
287
|
+
|
|
288
|
+
- **performance**: add comprehensive performance tests and metrics reporting ([
|
|
289
|
+
`c3d7a81`](https://github.com/l7aromeo/meonode-ui/commit/c3d7a81))
|
|
290
|
+
- Add detailed performance tests measuring render times for realistic layouts, 10,000 flat nodes, and 10,000 deeply
|
|
291
|
+
nested nodes.
|
|
292
|
+
- Introduce a heavy state changes test to detect memory leaks and ensure responsiveness under frequent updates.
|
|
293
|
+
- Collect and log performance metrics including median render times and memory usage for analysis.
|
|
294
|
+
- Add tests for stableKey generation performance with identical, shallowly equal, unique, large, and complex CSS
|
|
295
|
+
props.
|
|
296
|
+
- Enhance test suite with CSS styling for better visualization and interaction during tests.
|
|
297
|
+
- Improve cleanup and reporting to avoid resource leaks and provide clearer performance insights.
|
|
192
298
|
|
|
193
299
|
## [0.4.7] - 2025-11-17
|
|
194
300
|
|
|
195
301
|
### Fixes
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
302
|
+
|
|
303
|
+
- **core/cache:** optimize navigation cache debounce timing and enhance stableKey handling ([
|
|
304
|
+
`fff6f207`](https://github.com/l7aromeo/meonode-ui/commit/fff6f2070a06cc5ad461a2f992b111fb957fae6f))
|
|
305
|
+
- Adjust navigation cleanup debounce duration dynamically based on cache size for better performance.
|
|
306
|
+
- Change stableKey and _lastSignature to be optional to better represent their possible undefined state.
|
|
307
|
+
- Refactor _getStableKey to return undefined on server instead of empty string.
|
|
308
|
+
- Optimize critical props extraction by introducing NodeUtil.extractCriticalProps helper.
|
|
309
|
+
- Improve client-side caching logic to avoid lookups when stableKey is absent.
|
|
310
|
+
- Remove unused imports and redundant code in node.util.ts; improve shallowEqual implementation.
|
|
311
|
+
- Update createPropSignature to return undefined on server and use getElementTypeName directly.
|
|
312
|
+
- Add detailed comments and refine hashString combining FNV-1a and djb2 hashes for robustness.
|
|
205
313
|
|
|
206
314
|
## [0.4.6] - 2025-11-17
|
|
207
315
|
|
|
208
316
|
### Fixes
|
|
209
|
-
|
|
317
|
+
|
|
318
|
+
- **core/cache:** enhance memoization & caching system to prevent memory leaks and ensure safe lifecycle management ([
|
|
319
|
+
`253d7d00`](https://github.com/l7aromeo/meonode-ui/commit/253d7d006121dc588a51580d5120c7123a5f8777))
|
|
210
320
|
This introduces a robust, three-layered cleanup strategy to ensure cache integrity:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
321
|
+
1. An immediate, lifecycle-driven cleanup via the new `MeoNodeUnmounter` component.
|
|
322
|
+
2. A debounced, event-driven cleanup for SPA navigations via `NavigationCacheManagerUtil`.
|
|
323
|
+
3. A final, GC-driven safety net using the `FinalizationRegistry` API.
|
|
214
324
|
|
|
215
325
|
### Refactor
|
|
326
|
+
|
|
216
327
|
- **core:** migrate core logic from `src/helper/` to a new `src/util/` directory for better separation of concerns.
|
|
217
328
|
- **core:** extract internal utility functions from `core.node.ts` into `node.util.ts`.
|
|
218
329
|
- **core:** make internal caches on `BaseNode` public to support the new externalized management architecture.
|
|
219
330
|
|
|
220
331
|
### Chore
|
|
332
|
+
|
|
221
333
|
- **tooling:** enable TypeScript's `strict: true` mode and update codebase to be fully compliant.
|
|
222
334
|
- **tooling:** add CommonJS (`require`) exports to `package.json` for improved module compatibility.
|
|
223
|
-
- **tooling:** add `NODE_OPTIONS='--expose-gc'` to the test script to allow for explicit garbage collection during
|
|
224
|
-
|
|
335
|
+
- **tooling:** add `NODE_OPTIONS='--expose-gc'` to the test script to allow for explicit garbage collection during
|
|
336
|
+
tests.
|
|
337
|
+
- **tests:** refine test suite by standardizing `afterEach` hooks and updating memoization tests to directly validate
|
|
338
|
+
internal caching.
|
|
225
339
|
|
|
226
340
|
## [0.4.5] - 2025-11-15
|
|
227
341
|
|
|
228
342
|
### Feat
|
|
229
|
-
|
|
343
|
+
|
|
344
|
+
- **cache**: implement robust cache management and theme caching ([
|
|
345
|
+
`9ed749f6`](https://github.com/l7aromeo/meonode-ui/commit/9ed749f6d877fdc8b6a736788add13225b07dd63))
|
|
230
346
|
Refactor NavigationCacheManager for robustness, add memory monitoring and auto-cleanup.
|
|
231
347
|
Implement LRU eviction for ThemeResolverCache and integrate with BaseNode cache clearing.
|
|
232
348
|
|
|
233
349
|
### Refactor
|
|
234
|
-
|
|
350
|
+
|
|
351
|
+
- **test**: split node.test.ts into smaller, more focused test files ([
|
|
352
|
+
`930f998e`](https://github.com/l7aromeo/meonode-ui/commit/930f998e5f91faef3ff42fcafc6b02fc23f422ff))
|
|
235
353
|
This commit refactors the test suite by splitting the monolithic `node.test.ts`
|
|
236
354
|
file into several smaller, more focused test files, each covering a specific
|
|
237
355
|
aspect of the BaseNode functionality.
|
|
@@ -243,152 +361,241 @@ All notable changes to this project will be documented in this file.
|
|
|
243
361
|
preserving the test's original intent of verifying cache collision prevention.
|
|
244
362
|
|
|
245
363
|
### Fix
|
|
246
|
-
|
|
364
|
+
|
|
365
|
+
- **core**: pass disableEmotion flag to _renderProcessedNode for improved processing ([
|
|
366
|
+
`b68e3d4`](https://github.com/l7aromeo/meonode-ui/commit/b68e3d49a732ee590805a0298f733b800a9b172d))
|
|
247
367
|
|
|
248
368
|
### Chore
|
|
249
|
-
|
|
369
|
+
|
|
370
|
+
- **test**: adjust performance test cleanup by removing cache clearing from afterEach ([
|
|
371
|
+
`f72cea5e`](https://github.com/l7aromeo/meonode-ui/commit/f72cea5ef983fdaba012a7d446b58c7da06f5e1a))
|
|
250
372
|
|
|
251
373
|
## [0.4.4] - 2025-11-15
|
|
252
374
|
|
|
253
375
|
### Perf
|
|
254
|
-
- **core**: implement intelligent caching and memory management ([`0e5671b`](https://github.com/l7aromeo/meonode-ui/commit/0e5671b36189c964d66676ef633f3ccdbd9004e2))
|
|
255
|
-
Introduces a sophisticated caching and memory management system to prevent memory leaks and improve performance in Single Page Applications (SPAs).
|
|
256
376
|
|
|
257
|
-
|
|
377
|
+
- **core**: implement intelligent caching and memory management ([
|
|
378
|
+
`0e5671b`](https://github.com/l7aromeo/meonode-ui/commit/0e5671b36189c964d66676ef633f3ccdbd9004e2))
|
|
379
|
+
Introduces a sophisticated caching and memory management system to prevent memory leaks and improve performance in
|
|
380
|
+
Single Page Applications (SPAs).
|
|
381
|
+
|
|
382
|
+
This new system intelligently tracks mounted components and automatically cleans up caches of unmounted components
|
|
383
|
+
during navigation events.
|
|
258
384
|
|
|
259
385
|
Key features and improvements include:
|
|
260
386
|
|
|
261
|
-
|
|
387
|
+
- **Navigation-aware Cache Eviction:** A new `NavigationCacheManager` listens for browser navigation events (
|
|
388
|
+
popstate, pushState, etc.) and triggers a safe cleanup of the element cache. This prevents the cache from growing
|
|
389
|
+
indefinitely with stale entries from previous pages.
|
|
262
390
|
|
|
263
|
-
|
|
391
|
+
- **Mount Tracking:** A `MountTracker` class now keeps a record of all mounted `BaseNode` instances. This allows the
|
|
392
|
+
cache eviction logic to accurately determine which components are safe to remove from the cache.
|
|
264
393
|
|
|
265
|
-
|
|
394
|
+
- **Advanced Eviction Policies:** The `SafeCacheManager` implements several eviction policies, including evicting
|
|
395
|
+
unmounted components, old unmounted components, and an emergency eviction policy for high memory pressure
|
|
396
|
+
scenarios.
|
|
266
397
|
|
|
267
|
-
|
|
398
|
+
- **Memory-Safe Portal System:** The portal implementation has been refactored to use a `WeakMap`. This ensures that
|
|
399
|
+
portal-related DOM elements and React roots are automatically garbage collected when the corresponding `BaseNode`
|
|
400
|
+
instance is no longer in use, preventing a common source of memory leaks.
|
|
268
401
|
|
|
269
|
-
|
|
402
|
+
- **Improved Cache Entry Metadata:** The element cache entries now store additional metadata, such as creation
|
|
403
|
+
timestamp, access count, and a `WeakRef` to the node instance, enabling more intelligent eviction decisions.
|
|
270
404
|
|
|
271
|
-
|
|
405
|
+
- **Enhanced Stability:** Component identifiers for caching are now generated using a `WeakMap`, providing more
|
|
406
|
+
stable and reliable keys than relying on component names, which can be affected by minification.
|
|
272
407
|
|
|
273
|
-
|
|
408
|
+
- **Comprehensive Test Coverage:** Added a suite of new integration tests to validate the caching and memory
|
|
409
|
+
management system. These tests cover key scenarios including cache collision, rapid navigation, React 18 Strict
|
|
410
|
+
Mode compatibility, large prop object fingerprinting, and LRU eviction logic.
|
|
274
411
|
|
|
275
412
|
### Fix
|
|
276
|
-
|
|
413
|
+
|
|
414
|
+
- **core**: simplify property assignment in common helper ([
|
|
415
|
+
`312af57`](https://github.com/l7aromeo/meonode-ui/commit/312af574712202a25bdd62fab94441a937f159f2))
|
|
277
416
|
|
|
278
417
|
### Refactor
|
|
279
|
-
|
|
418
|
+
|
|
419
|
+
- **core**: add ElementCacheEntry interface for memoization and update css prop type ([
|
|
420
|
+
`6a8381c`](https://github.com/l7aromeo/meonode-ui/commit/6a8381c4c85cb22df4ba398637401d420461e413))
|
|
280
421
|
|
|
281
422
|
## [0.4.3] - 2025-11-14
|
|
282
423
|
|
|
283
424
|
### Docs
|
|
284
|
-
|
|
425
|
+
|
|
426
|
+
- **core**: add detailed comments to rendering methods ([
|
|
427
|
+
`731c83e`](https://github.com/l7aromeo/meonode-ui/commit/731c83e))
|
|
285
428
|
|
|
286
429
|
### Fix
|
|
287
|
-
|
|
430
|
+
|
|
431
|
+
- **core**: adjust isStyledComponent logic to improve style handling ([
|
|
432
|
+
`ff7a59e`](https://github.com/l7aromeo/meonode-ui/commit/ff7a59e))
|
|
288
433
|
|
|
289
434
|
### Refactor
|
|
290
|
-
|
|
435
|
+
|
|
436
|
+
- **core**: simplify _processProps by removing style prop handling ([
|
|
437
|
+
`b3570b4`](https://github.com/l7aromeo/meonode-ui/commit/b3570b4))
|
|
291
438
|
|
|
292
439
|
## [0.4.2] - 2025-11-14
|
|
293
440
|
|
|
294
441
|
### Fix
|
|
295
|
-
|
|
442
|
+
|
|
443
|
+
- **core**: remove deps property from props since it should not be passed to element attribute ([
|
|
444
|
+
`6b01cbe`](https://github.com/l7aromeo/meonode-ui/commit/6b01cbe))
|
|
296
445
|
|
|
297
446
|
## [0.4.1] - 2025-11-14
|
|
298
447
|
|
|
299
448
|
### Test
|
|
300
|
-
|
|
449
|
+
|
|
450
|
+
- **node**: enhance dependency and memoization tests with real-world scenarios ([
|
|
451
|
+
`d7452fa`](https://github.com/l7aromeo/meonode-ui/commit/d7452fae9b3ef22a82dc83210851849d82de479f))
|
|
301
452
|
|
|
302
453
|
### Fix
|
|
303
|
-
|
|
454
|
+
|
|
455
|
+
- **core**: enhance error handling and improve style property detection ([
|
|
456
|
+
`ca79c27`](https://github.com/l7aromeo/meonode-ui/commit/ca79c277cdfea5b62b54779ec2492518681639d1))
|
|
304
457
|
|
|
305
458
|
### Chore
|
|
306
|
-
|
|
459
|
+
|
|
460
|
+
- **deps**: update dependencies in package.json for improved stability and performance ([
|
|
461
|
+
`eba2108`](https://github.com/l7aromeo/meonode-ui/commit/eba21080c953b1c6b6c1bbb6a401257845116f09))
|
|
307
462
|
|
|
308
463
|
### Perf
|
|
309
|
-
|
|
464
|
+
|
|
465
|
+
- **core**: Optimize prop processing and caching with new signature generation ([
|
|
466
|
+
`8cf0319`](https://github.com/l7aromeo/meonode-ui/commit/8cf0319fd99c3c8496b6e63207cb378b6c521ae2))
|
|
310
467
|
|
|
311
468
|
### Refactor
|
|
312
|
-
|
|
469
|
+
|
|
470
|
+
- **types**: move node.type.ts to types directory and update imports ([
|
|
471
|
+
`ccf769a`](https://github.com/l7aromeo/meonode-ui/commit/ccf769a2670e4546bfa776034a4fa8925ca2d27d))
|
|
313
472
|
|
|
314
473
|
## [0.4.0] - 2025-11-13
|
|
315
474
|
|
|
316
475
|
### Feature
|
|
476
|
+
|
|
317
477
|
- **core**: Implemented an advanced memoization and caching system to optimize rendering performance. This includes:
|
|
318
|
-
- **Dependency-Based Memoization**: Nodes can now be created with a dependency array, similar to React's `useMemo`,
|
|
319
|
-
|
|
320
|
-
|
|
478
|
+
- **Dependency-Based Memoization**: Nodes can now be created with a dependency array, similar to React's `useMemo`,
|
|
479
|
+
to prevent unnecessary re-renders of the node and its entire subtree if dependencies have not changed. ([
|
|
480
|
+
`3b0a110`](https://github.com/l7aromeo/meonode-ui/commit/3b0a110eb3db25862476d020182be9f0dba663e4))
|
|
481
|
+
- **Enhanced Prop Caching**: The prop signature generation is now more robust, and the cache uses an advanced LRU
|
|
482
|
+
eviction strategy to remain efficient. ([
|
|
483
|
+
`3b0a110`](https://github.com/l7aromeo/meonode-ui/commit/3b0a110eb3db25862476d020182be9f0dba663e4))
|
|
484
|
+
- **API Updates**: The `Node`, `createNode`, `createChildrenFirstNode`, and `Component` HOCs have been updated to
|
|
485
|
+
accept an optional `deps` array to enable memoization. ([
|
|
486
|
+
`3b0a110`](https://github.com/l7aromeo/meonode-ui/commit/3b0a110eb3db25862476d020182be9f0dba663e4))
|
|
321
487
|
|
|
322
488
|
### Test
|
|
323
|
-
|
|
489
|
+
|
|
490
|
+
- **core**: Added a comprehensive suite of tests for the new memoization and caching system, covering dependency-based
|
|
491
|
+
memoization, reactive and non-reactive children, complex state updates, and memoization of Higher-Order Components (
|
|
492
|
+
HOCs). ([`6bcd1b1`](https://github.com/l7aromeo/meonode-ui/commit/6bcd1b1bc6b2450c3d4296cb4af326f61cfee401))
|
|
324
493
|
|
|
325
494
|
## [0.3.18] - 2025-11-12
|
|
326
495
|
|
|
327
496
|
### Fixed
|
|
328
|
-
|
|
497
|
+
|
|
498
|
+
- **core**: refine prop caching to handle dynamic props correctly ([
|
|
499
|
+
`4c0641e`](https://github.com/l7aromeo/meonode-ui/commit/4c0641e892f934551f100629cac72fc3f4649ab0))
|
|
329
500
|
|
|
330
501
|
## [0.3.17] - 2025-11-12
|
|
331
502
|
|
|
332
503
|
### Perf
|
|
333
|
-
|
|
334
|
-
- **
|
|
504
|
+
|
|
505
|
+
- **core**: implement iterative renderer and prop caching ([
|
|
506
|
+
`8a3a264`](https://github.com/l7aromeo/meonode-ui/commit/8a3a264be68bd041b6340636f5f7ee2b0caa63ff))
|
|
507
|
+
- **helper**: refactor theme resolution logic for improved performance and cache correctness ([
|
|
508
|
+
`9614cb8`](https://github.com/l7aromeo/meonode-ui/commit/9614cb8d2aeae0d9bd2f9cf3edd51c022cd93273))
|
|
335
509
|
|
|
336
510
|
### Chore
|
|
337
|
-
|
|
338
|
-
-
|
|
339
|
-
|
|
340
|
-
-
|
|
341
|
-
|
|
342
|
-
-
|
|
343
|
-
|
|
344
|
-
- update
|
|
511
|
+
|
|
512
|
+
- fix typo in JSDoc comment for useTheme hook ([
|
|
513
|
+
`de0ddd9`](https://github.com/l7aromeo/meonode-ui/commit/de0ddd9a6308f4a76b6ad843a6139d42bd3fcf53))
|
|
514
|
+
- add deprecation notice to usePortal hook for future removal ([
|
|
515
|
+
`f8a2923`](https://github.com/l7aromeo/meonode-ui/commit/f8a29230cad3962addb8cf28ed3538e6de236181))
|
|
516
|
+
- update PortalProps type definition to provide a default type parameter ([
|
|
517
|
+
`de73ba5`](https://github.com/l7aromeo/meonode-ui/commit/de73ba5b9d9dd51637b24b0309d681309d9338ae))
|
|
518
|
+
- update isNodeInstance type guard to use BaseNode instead of NodeInstance ([
|
|
519
|
+
`2c69d05`](https://github.com/l7aromeo/meonode-ui/commit/2c69d05b3d1593a976e439ca7404696b781e5012))
|
|
520
|
+
- rename jest.config.mjs to jest.config.ts and update configuration for TypeScript support ([
|
|
521
|
+
`a3213eb`](https://github.com/l7aromeo/meonode-ui/commit/a3213eb5b91a55364cb4f5362005bc2a46934de5))
|
|
522
|
+
- **scripts**: increase stack size for jest test and fix build commands ([
|
|
523
|
+
`e046cdf`](https://github.com/l7aromeo/meonode-ui/commit/e046cdf397e2cf418e09e149a9e0cf1e48f3d926))
|
|
524
|
+
- update tsconfig.json to exclude dist and node_modules directories ([
|
|
525
|
+
`eeb9577`](https://github.com/l7aromeo/meonode-ui/commit/eeb957722ab7a26cbe59047c068f9955b082502e))
|
|
526
|
+
- update tsconfig.json with enhanced compiler options and path mappings for better development experience ([
|
|
527
|
+
`89bc1a4`](https://github.com/l7aromeo/meonode-ui/commit/89bc1a42c23f015acfed1bcb860ebb6a4c684fc1))
|
|
345
528
|
|
|
346
529
|
## [0.3.16] - 2025-11-05
|
|
347
530
|
|
|
348
531
|
### Added
|
|
349
|
-
|
|
350
|
-
- **
|
|
351
|
-
|
|
352
|
-
- **core**:
|
|
353
|
-
|
|
532
|
+
|
|
533
|
+
- **tests**: add tests for Fragment, Suspense, and Activity components to verify styling prop handling ([
|
|
534
|
+
`2af386f`](https://github.com/l7aromeo/meonode-ui/commit/2af386f))
|
|
535
|
+
- **core**: enhance NodeProps type to conditionally include built-in React components ([
|
|
536
|
+
`3b8a4cb`](https://github.com/l7aromeo/meonode-ui/commit/3b8a4cb))
|
|
537
|
+
- **react**: add REACT_ACTIVITY_TYPE to react-is helper ([
|
|
538
|
+
`e91e48f`](https://github.com/l7aromeo/meonode-ui/commit/e91e48f))
|
|
539
|
+
- **core**: export NO_STYLE_TAGS type for better type inference ([
|
|
540
|
+
`a6db6e8`](https://github.com/l7aromeo/meonode-ui/commit/a6db6e8))
|
|
541
|
+
- **react**: add Fragment component to create a container without extra DOM elements ([
|
|
542
|
+
`d5e376a`](https://github.com/l7aromeo/meonode-ui/commit/d5e376a))
|
|
354
543
|
|
|
355
544
|
## [0.3.15] - 2025-11-04
|
|
356
545
|
|
|
357
546
|
### Added
|
|
358
|
-
|
|
359
|
-
- **
|
|
547
|
+
|
|
548
|
+
- **core**: add disableEmotion prop to disable emotion styling and propagate to children ([
|
|
549
|
+
`377a9e9`](https://github.com/l7aromeo/meonode-ui/commit/377a9e9d4844ba7869155e686c9b31f0f9ce2329))
|
|
550
|
+
- **react**: enhance isContextProvider and isReactClassComponent checks ([
|
|
551
|
+
`e8839e4`](https://github.com/l7aromeo/meonode-ui/commit/e8839e4c231bdd66686f7b43d9889a18cd9fc791))
|
|
360
552
|
|
|
361
553
|
## [0.3.14] - 2025-10-30
|
|
362
554
|
|
|
363
555
|
### Added
|
|
364
|
-
|
|
556
|
+
|
|
557
|
+
- **core**: Add handling for Suspense and Activity components ([
|
|
558
|
+
`0f9fcb1`](https://github.com/l7aromeo/meonode-ui/commit/0f9fcb171fdce28b5a880e69e2d591543e3af817))
|
|
365
559
|
|
|
366
560
|
## [0.3.13] - 2025-10-30
|
|
367
561
|
|
|
368
562
|
### Fixed
|
|
369
|
-
|
|
563
|
+
|
|
564
|
+
- **theme.helper**: process theme strings returned from functions ([
|
|
565
|
+
`286fd89`](https://github.com/l7aromeo/meonode-ui/commit/286fd89e28cc10b467a208be4cdf1b7508d0be8c))
|
|
370
566
|
|
|
371
567
|
## [0.3.12] - 2025-10-23
|
|
372
568
|
|
|
373
569
|
### Added
|
|
374
|
-
- **react**: add Suspense component and JSDoc for Activity and Suspense ([`c1760fd`](https://github.com/l7aromeo/meonode-ui/commit/c1760fd))
|
|
375
570
|
|
|
571
|
+
- **react**: add Suspense component and JSDoc for Activity and Suspense ([
|
|
572
|
+
`c1760fd`](https://github.com/l7aromeo/meonode-ui/commit/c1760fd))
|
|
376
573
|
|
|
377
574
|
## [0.3.11] - 2025-10-19
|
|
378
575
|
|
|
379
576
|
### Added
|
|
380
|
-
|
|
381
|
-
- **
|
|
577
|
+
|
|
578
|
+
- **components**: add react activity node and export it in main ([
|
|
579
|
+
`aadbc2d`](https://github.com/l7aromeo/meonode-ui/commit/aadbc2d08a928f1ba88bd4572b45eed8cb100a87))
|
|
580
|
+
- **theme.helper**: update resolveObjWithTheme to improve object type checking ([
|
|
581
|
+
`da1ce4c`](https://github.com/l7aromeo/meonode-ui/commit/da1ce4cd53ccbe2d2a562a49730151434177dc59))
|
|
382
582
|
|
|
383
583
|
### Changed
|
|
384
|
-
|
|
385
|
-
- **chore**:
|
|
584
|
+
|
|
585
|
+
- **chore**: update dependencies in package.json and yarn.lock ([
|
|
586
|
+
`0c0ced6`](https://github.com/l7aromeo/meonode-ui/commit/0c0ced68662bb701634d49dc79da86e4ddce5392))
|
|
587
|
+
- **chore**: remove \'use strict\' directive from multiple files ([
|
|
588
|
+
`17d79dc`](https://github.com/l7aromeo/meonode-ui/commit/17d79dcb105a8c2062695071c3f587f6db9a5711))
|
|
386
589
|
|
|
387
590
|
### Docs
|
|
388
|
-
|
|
591
|
+
|
|
592
|
+
- **docs**: update Node.js version requirement in CONTRIBUTING.md ([
|
|
593
|
+
`4c577c3`](https://github.com/l7aromeo/meonode-ui/commit/4c577c3e23294bdc188cda5b14375af1cb967888))
|
|
389
594
|
|
|
390
595
|
### Test
|
|
391
|
-
|
|
596
|
+
|
|
597
|
+
- **node**: add test case for preserving Node instances passed through props and theme resolution ([
|
|
598
|
+
`f4d1344`](https://github.com/l7aromeo/meonode-ui/commit/f4d1344355f2a4631ccdf04998bcf618d4ce1dc6))
|
|
392
599
|
|
|
393
600
|
## [0.3.10] - 2025-10-09
|
|
394
601
|
|
|
@@ -410,11 +617,14 @@ All notable changes to this project will be documented in this file.
|
|
|
410
617
|
|
|
411
618
|
### Added
|
|
412
619
|
|
|
413
|
-
- **styling**: Enabled theme-aware functions for css props, allowing for more dynamic styling (e.g.,
|
|
620
|
+
- **styling**: Enabled theme-aware functions for css props, allowing for more dynamic styling (e.g.,
|
|
621
|
+
`color: theme => theme.system.colors.primary`).
|
|
414
622
|
|
|
415
623
|
### Refactor
|
|
416
624
|
|
|
417
|
-
- **core**: Refactored the style resolution logic (`resolveObjWithTheme` and `StyledRenderer`) to selectively process
|
|
625
|
+
- **core**: Refactored the style resolution logic (`resolveObjWithTheme` and `StyledRenderer`) to selectively process
|
|
626
|
+
the `css` prop. This enables the new theme-function feature while ensuring that other props (like `children`) are not
|
|
627
|
+
processed, maintaining compatibility with Next.js Server Components.
|
|
418
628
|
|
|
419
629
|
## [0.3.7] - 2025-09-27
|
|
420
630
|
|
|
@@ -530,7 +740,8 @@ All notable changes to this project will be documented in this file.
|
|
|
530
740
|
|
|
531
741
|
### Added
|
|
532
742
|
|
|
533
|
-
- **feat:** update NodeFunction type to improve dynamic node content generation and update FunctionRendererProps
|
|
743
|
+
- **feat:** update NodeFunction type to improve dynamic node content generation and update FunctionRendererProps
|
|
744
|
+
interface to use NodeFunction
|
|
534
745
|
|
|
535
746
|
### Fixed
|
|
536
747
|
|
|
@@ -538,6 +749,7 @@ All notable changes to this project will be documented in this file.
|
|
|
538
749
|
- **fix(node.type.ts):** remove unused processRawNode property from Node type
|
|
539
750
|
|
|
540
751
|
### Changed
|
|
752
|
+
|
|
541
753
|
- **package:** update dependencies to latest versions
|
|
542
754
|
|
|
543
755
|
## [0.2.20] - 2025-09-22
|
|
@@ -625,7 +837,8 @@ All notable changes to this project will be documented in this file.
|
|
|
625
837
|
|
|
626
838
|
### Added
|
|
627
839
|
|
|
628
|
-
- **feat(common.helper)**: add omit and omitUndefined utility functions to create object copies without specified keys
|
|
840
|
+
- **feat(common.helper)**: add omit and omitUndefined utility functions to create object copies without specified keys
|
|
841
|
+
or undefined values
|
|
629
842
|
- **feat**: integrate omit and omitUndefined functions to manage finalProps for standard HTML tags and custom components
|
|
630
843
|
|
|
631
844
|
### Refactor
|
|
@@ -641,14 +854,16 @@ All notable changes to this project will be documented in this file.
|
|
|
641
854
|
|
|
642
855
|
### Removed
|
|
643
856
|
|
|
644
|
-
- **types**: removed redundant `key` prop from `FinalNodeProps` and default `key` from `NodeProps` types for cleaner
|
|
857
|
+
- **types**: removed redundant `key` prop from `FinalNodeProps` and default `key` from `NodeProps` types for cleaner
|
|
858
|
+
type definitions
|
|
645
859
|
|
|
646
860
|
## [0.2.16] - 2025-09-13
|
|
647
861
|
|
|
648
862
|
### Added
|
|
649
863
|
|
|
650
864
|
- **feat(core)**: introduce static _isServer property to optimize server-side checks in caching methods
|
|
651
|
-
- **feat(core)**: remove passedKey from _functionRenderer and simplify function-as-child wrapper; augment toPortal
|
|
865
|
+
- **feat(core)**: remove passedKey from _functionRenderer and simplify function-as-child wrapper; augment toPortal
|
|
866
|
+
unmount to clean portal container
|
|
652
867
|
|
|
653
868
|
### Refactor
|
|
654
869
|
|
package/dist/cjs/core.node.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("react"),t=require("./helper/react-is.helper.js"),r=require("./helper/common.helper.js"),n=require("./components/styled-renderer.client.js"),o=require("./constant/common.const.js"),s=require("./util/mount-tracker.util.js"),a=require("./components/meonode-unmounter.client.js"),l=require("./util/navigation-cache-manager.util.js"),i=require("./util/node.util.js"),c=require("./util/theme.util.js");class d{instanceId=Math.random().toString(36).slice(2)+Date.now().toString(36);element;rawProps={};isBaseNode=!0;_props;_deps;stableKey;lastPropsObj;lastSignature;static elementCache=new Map;static propProcessingCache=new Map;static scheduledCleanup=!1;static _navigationStarted=!1;static renderContextPool=[];static acquireRenderContext(){const e=d.renderContextPool;return e.length>0?e.pop():{workStack:new Array(512),renderedElements:new Map}}static releaseRenderContext(e){d.renderContextPool.length<50&&e.workStack.length<2048&&(e.workStack.length=0,e.renderedElements.clear(),d.renderContextPool.push(e))}constructor(e,n={},o){if(!t.isValidElementType(e)){const t=r.getComponentType(e);throw new Error(`Invalid element type: ${t} provided!`)}this.element=e,this.rawProps=n,this._deps=o;const{ref:s,children:a,...c}=n;this.stableKey=this._getStableKey(c),i.NodeUtil.isServer||d._navigationStarted||(l.NavigationCacheManagerUtil.getInstance().start(),d._navigationStarted=!0)}get props(){return this._props||(this._props=i.NodeUtil.processProps(this.element,this.rawProps,this.stableKey)),this._props}get dependencies(){return this._deps}_getStableKey({key:e,...t}){if(i.NodeUtil.isServer)return;if(this.lastPropsObj===t)return this.lastSignature;this.lastPropsObj=t;const n=Object.keys(t),s=n.length;if(s>100){const e=i.NodeUtil.extractCriticalProps(t,n);this.lastSignature=i.NodeUtil.createPropSignature(this.element,e),o.__DEBUG__&&s>200&&console.warn(`MeoNode: Large props (${s} keys) on "${r.getElementTypeName(this.element)}". Consider splitting.`)}else this.lastSignature=i.NodeUtil.createPropSignature(this.element,t);return null!=e?`${String(e)}:${this.lastSignature}`:this.lastSignature}static cacheCleanupRegistry=new FinalizationRegistry(e=>{const{cacheKey:t,instanceId:r}=e,n=d.elementCache.get(t);n?.instanceId===r&&d.elementCache.delete(t),s.MountTrackerUtil.mountedNodes.has(t)&&s.MountTrackerUtil.untrackMount(t)});static portalCleanupRegistry=new FinalizationRegistry(e=>{const{domElement:t,reactRoot:r}=e;o.__DEBUG__&&console.log("[MeoNode] FinalizationRegistry auto-cleaning portal");try{r&&"function"==typeof r.unmount&&r.unmount()}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal auto-cleanup unmount error:",e)}try{t?.isConnected&&t.remove()}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal auto-cleanup DOM removal error:",e)}});render(o=!1){!i.NodeUtil.isServer&&this.stableKey&&s.MountTrackerUtil.trackMount(this.stableKey);const l=i.NodeUtil.
|
|
1
|
+
"use strict";var e=require("react"),t=require("./helper/react-is.helper.js"),r=require("./helper/common.helper.js"),n=require("./components/styled-renderer.client.js"),o=require("./constant/common.const.js"),s=require("./util/mount-tracker.util.js"),a=require("./components/meonode-unmounter.client.js"),l=require("./util/navigation-cache-manager.util.js"),i=require("./util/node.util.js"),c=require("./util/theme.util.js");class d{instanceId=Math.random().toString(36).slice(2)+Date.now().toString(36);element;rawProps={};isBaseNode=!0;_props;_deps;stableKey;lastPropsObj;lastSignature;static elementCache=new Map;static propProcessingCache=new Map;static scheduledCleanup=!1;static _navigationStarted=!1;static renderContextPool=[];static acquireRenderContext(){const e=d.renderContextPool;return e.length>0?e.pop():{workStack:new Array(512),renderedElements:new Map}}static releaseRenderContext(e){d.renderContextPool.length<50&&e.workStack.length<2048&&(e.workStack.length=0,e.renderedElements.clear(),d.renderContextPool.push(e))}constructor(e,n={},o){if(!t.isValidElementType(e)){const t=r.getComponentType(e);throw new Error(`Invalid element type: ${t} provided!`)}this.element=e,this.rawProps=n,this._deps=o;const{ref:s,children:a,...c}=n;this.stableKey=this._getStableKey(c),i.NodeUtil.isServer||d._navigationStarted||(l.NavigationCacheManagerUtil.getInstance().start(),d._navigationStarted=!0)}get props(){return this._props||(this._props=i.NodeUtil.processProps(this.element,this.rawProps,this.stableKey)),this._props}get dependencies(){return this._deps}_getStableKey({key:e,...t}){if(i.NodeUtil.isServer)return;if(this.lastPropsObj===t)return this.lastSignature;this.lastPropsObj=t;const n=Object.keys(t),s=n.length;if(s>100){const e=i.NodeUtil.extractCriticalProps(t,n);this.lastSignature=i.NodeUtil.createPropSignature(this.element,e),o.__DEBUG__&&s>200&&console.warn(`MeoNode: Large props (${s} keys) on "${r.getElementTypeName(this.element)}". Consider splitting.`)}else this.lastSignature=i.NodeUtil.createPropSignature(this.element,t);return null!=e?`${String(e)}:${this.lastSignature}`:this.lastSignature}static cacheCleanupRegistry=new FinalizationRegistry(e=>{const{cacheKey:t,instanceId:r}=e,n=d.elementCache.get(t);n?.instanceId===r&&d.elementCache.delete(t),s.MountTrackerUtil.mountedNodes.has(t)&&s.MountTrackerUtil.untrackMount(t)});static portalCleanupRegistry=new FinalizationRegistry(e=>{const{domElement:t,reactRoot:r}=e;o.__DEBUG__&&console.log("[MeoNode] FinalizationRegistry auto-cleaning portal");try{r&&"function"==typeof r.unmount&&r.unmount()}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal auto-cleanup unmount error:",e)}try{t?.isConnected&&t.remove()}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal auto-cleanup DOM removal error:",e)}});render(o=!1){!i.NodeUtil.isServer&&this.stableKey&&s.MountTrackerUtil.trackMount(this.stableKey);const l=i.NodeUtil.shouldCacheElement(this)?d.elementCache.get(this.stableKey):void 0,c=i.NodeUtil.shouldNodeUpdate(l?.prevDeps,this._deps,o);if(!c&&l?.renderedElement)return l.accessCount+=1,l.renderedElement;const u=!c,h=d.acquireRenderContext();let{workStack:p}=h;const{renderedElements:m}=h;let g=0;try{const o=e=>{if(e>p.length){const t=Math.max(e,p.length<<1),r=new Array(t);for(let e=0;e<g;e++)r[e]=p[e];p=r}};for(p[g++]={node:this,isProcessed:!1,blocked:u};g>0;){const s=p[g-1];if(!s){g--;continue}const{node:a,isProcessed:l,blocked:c}=s;if(l){g--;const{children:o,key:s,css:l,nativeProps:c,disableEmotion:u,...h}=a.props;let p=[];if(o){const t=Array.isArray(o)?o:[o],r=t.length;p=new Array(r);for(let n=0;n<r;n++){const r=t[n];if(i.NodeUtil.isNodeInstance(r)){const e=m.get(r);if(!e)throw new Error(`[MeoNode] Missing rendered element for child node: ${r.stableKey}`);p[n]=e}else e.isValidElement(r),p[n]=r}}const _={...h,key:s,...c};let y;if(a.element===e.Fragment||t.isFragment(a.element))y=e.createElement(a.element,{key:s},...p);else{y=!u&&(l||!r.hasNoStyleTag(a.element))?e.createElement(n.default,{element:a.element,..._,css:l,suppressHydrationWarning:!0},...p):e.createElement(a.element,_,...p)}if(i.NodeUtil.shouldCacheElement(a)){const e=d.elementCache.get(a.stableKey);if(e)e.prevDeps=a._deps,e.renderedElement=y,e.accessCount+=1;else{const e={prevDeps:a._deps,renderedElement:y,nodeRef:new WeakRef(a),createdAt:Date.now(),accessCount:1,instanceId:a.instanceId};d.elementCache.set(a.stableKey,e),d.cacheCleanupRegistry.register(a,{cacheKey:a.stableKey,instanceId:a.instanceId},a)}}m.set(a,y)}else{s.isProcessed=!0;const e=a.props.children;if(e){const t=(Array.isArray(e)?e:[e]).filter(i.NodeUtil.isNodeInstance);o(g+t.length);for(let e=t.length-1;e>=0;e--){const r=t[e],n=i.NodeUtil.shouldCacheElement(r)?d.elementCache.get(r.stableKey):void 0,o=i.NodeUtil.shouldNodeUpdate(n?.prevDeps,r._deps,c);if(!o&&n?.renderedElement){m.set(r,n.renderedElement);continue}const s=c||!o;p[g++]={node:r,isProcessed:!1,blocked:s}}}}}const s=m.get(this);return i.NodeUtil.shouldCacheElement(this)?e.createElement(a.default,{node:this},s):s}finally{for(let e=0;e<g;e++)p[e]=null;d.releaseRenderContext({workStack:p,renderedElements:m})}}toPortal(){if(!i.NodeUtil.ensurePortalInfrastructure(this))throw new Error("toPortal() can only be called in a client-side environment");const e=i.NodeUtil.portalInfrastructure.get(this),{domElement:t,reactRoot:r}=e;(()=>{try{r.render(this.render())}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal render error:",e)}})();let n=!1;const s=r.unmount.bind(r);return r.update=e=>{if(n)o.__DEBUG__&&console.warn("[MeoNode] Attempt to update already-unmounted portal");else try{const t=i.NodeUtil.isNodeInstance(e)?e.render():e;r.render(t)}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal update error:",e)}},r.unmount=()=>{if(n)o.__DEBUG__&&console.warn("[MeoNode] Portal already unmounted");else{n=!0;try{d.portalCleanupRegistry.unregister(this)}catch(e){o.__DEBUG__&&console.warn("[MeoNode] Portal unregister warning:",e)}i.NodeUtil.portalInfrastructure.delete(this);try{t?.isConnected&&s()}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal unmount error:",e)}try{t?.isConnected&&t.remove()}catch(e){o.__DEBUG__&&console.error("[MeoNode] Portal DOM cleanup error:",e)}}},r}static clearCaches(){const e=Array.from(d.elementCache.keys());o.__DEBUG__&&console.log(`[MeoNode] clearCaches: Clearing ${e.length} entries`);for(const t of e){const e=d.elementCache.get(t);if(e){const r=e.nodeRef?.deref();if(r)try{d.cacheCleanupRegistry.unregister(r),r.lastSignature=void 0,r.lastPropsObj=void 0}catch{o.__DEBUG__&&console.warn(`[MeoNode] Could not unregister ${t} from FinalizationRegistry`)}}}d.propProcessingCache.clear(),d.elementCache.clear(),c.ThemeUtil.clearThemeCache(),s.MountTrackerUtil.cleanup(),o.__DEBUG__&&console.log("[MeoNode] All caches cleared")}}function u(e,t={},r){return new d(e,t,r)}u.clearCaches=d.clearCaches,exports.BaseNode=d,exports.Node=u,exports.createChildrenFirstNode=function(e,t){const r=(r,n,o)=>u(e,{...t,...n,children:r},o);return r.element=e,r},exports.createNode=function(e,t){const r=(r,n)=>u(e,{...t,...r},n);return r.element=e,r};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("react"),t=require("../helper/react-is.helper.js"),o=require("../helper/common.helper.js"),r=require("../constant/common.const.js"),s=require("../core.node.js"),n=require("react-dom/client");class i{constructor(){}static isServer="undefined"==typeof window;static _functionSignatureCache=new WeakMap;static CACHE_SIZE_LIMIT=500;static CACHE_CLEANUP_BATCH=50;static _cssCache=new WeakMap;static CRITICAL_PROPS=new Set(["css","className","disableEmotion","props"]);static portalInfrastructure=new WeakMap;static isNodeInstance=e=>"object"==typeof e&&null!==e&&"element"in e&&"function"==typeof e.render&&"function"==typeof e.toPortal&&"isBaseNode"in e;static isStyleProp=i.isServer||"undefined"==typeof document?()=>!1:e=>e in document.body.style;static hashString(e){let t=2166136261,o=5381;for(let r=0;r<e.length;r++){const s=e.charCodeAt(r);t^=s,t=Math.imul(t,16777619),o=33*o^s}return`${(t>>>0).toString(36)}_${(o>>>0).toString(36)}`}static hashCSS(e){const t=this._cssCache.get(e);if(t)return t;const o=Object.keys(e);let r=o.length;for(let t=0;t<Math.min(o.length,10);t++){const s=o[t],n=e[s];r=(r<<5)-r+s.charCodeAt(0),r&=r,"string"==typeof n&&(r=(r<<5)-r+n.length)}const s=r.toString(36);return this._cssCache.set(e,s),s}static createPropSignature(e,t){if(i.isServer)return;const r=o.getElementTypeName(e),s=Object.keys(t);s.length>1&&s.sort();const n=[`${r}:`];if("function"==typeof e){let t=i._functionSignatureCache.get(e);t||(t=i.hashString(e.toString()),i._functionSignatureCache.set(e,t)),n.push(t)}for(const e of s){const o=t[e];let r;const s=typeof o;if("string"===s||"number"===s||"boolean"===s)r=`${e}:${o};`;else if(null===o)r=`${e}:null;`;else if(void 0===o)r=`${e}:undefined;`;else if("css"===e&&"object"==typeof o)r=`css:${this.hashCSS(o)};`;else if(Array.isArray(o)){const t=o.filter(e=>{const t=typeof e;return"string"===t||"number"===t||"boolean"===t||null===e});r=t.length===o.length?`${e}:[${t.join(",")}];`:`${e}:[${o.length}];`}else if(o&&o.isBaseNode)r=`${e}:${o.stableKey};`;else{r=`${e}:{${Object.keys(o).sort().join(",")}};`}n.push(r)}return i.hashString(n.join(","))}static extractCriticalProps(e,t){const o={_keyCount:t.length};let r=0;for(const s of t){if(r>=50)break;if(i.CRITICAL_PROPS.has(s)){o[s]=e[s],r++;continue}const n=s.charCodeAt(0);111!==n||110!==s.charCodeAt(1)?!(97===n&&114===s.charCodeAt(1)&&105===s.charCodeAt(2)&&97===s.charCodeAt(3)||100===n&&97===s.charCodeAt(1)&&116===s.charCodeAt(2)&&97===s.charCodeAt(3))?t.length<=100&&i.isStyleProp(s)&&(o[s]=e[s],r++):(o[s]=e[s],r++):(o[s]=e[s],r++)}return o}static getCachedCssProps(e,t){if(i.isServer||!t)return{cssProps:o.getCSSProps(e)};const r=s.BaseNode.propProcessingCache.get(t);if(r)return r.lastAccess=Date.now(),r.hitCount++,{cssProps:r.cssProps};const n=o.getCSSProps(e);return s.BaseNode.propProcessingCache.set(t,{cssProps:n,signature:t,lastAccess:Date.now(),hitCount:1}),s.BaseNode.propProcessingCache.size>i.CACHE_SIZE_LIMIT&&!s.BaseNode.scheduledCleanup&&(s.BaseNode.scheduledCleanup=!0,"undefined"!=typeof requestIdleCallback?requestIdleCallback(()=>{i._evictLRUEntries(),s.BaseNode.scheduledCleanup=!1},{timeout:2e3}):setTimeout(()=>{i._evictLRUEntries(),s.BaseNode.scheduledCleanup=!1},100)),{cssProps:n}}static _evictLRUEntries(){const e=Date.now(),t=new a((e,t)=>t.score-e.score);for(const[o,r]of s.BaseNode.propProcessingCache.entries()){const s=this._calculateEvictionScore(r,e);t.push({key:o,score:s})}for(let e=0;e<i.CACHE_CLEANUP_BATCH;e++){const e=t.pop();if(!e)break;s.BaseNode.propProcessingCache.delete(e.key)}}static _calculateEvictionScore(e,t){return(t-e.lastAccess)/1e3*.3+1e3/(e.hitCount+1)*.7}static processProps(e,t={},r){const{ref:s,key:n,children:a,css:c,props:d={},disableEmotion:l,...p}=t;if(0===Object.keys(p).length&&!c)return o.omitUndefined({ref:s,key:n,disableEmotion:l,nativeProps:o.omitUndefined(d),children:i._processChildren(a,l)});const u={},h={},f=Object.keys(p);for(let e=0;e<f.length;e++){const t=f[e],o=p[t],r=typeof o;"string"===r||"number"===r||"boolean"===r?u[t]=o:h[t]=o}const m=i.createPropSignature(e,u),{cssProps:C}=i.getCachedCssProps(u,m),y=o.getCSSProps(h),b=o.getDOMProps(p),E={...C,...y,...c},g=i._processChildren(a,l,r);return o.omitUndefined({ref:s,key:n,css:E,...b,disableEmotion:l,nativeProps:o.omitUndefined(d),children:g})}static _processChildren(e,t,o){if(e)return"function"==typeof e?e:Array.isArray(e)?1===e.length?i.processRawNode(e[0],t,`${o}_0`):e.map((e,r)=>i.processRawNode(e,t,`${o}_${r}`)):i.processRawNode(e,t,o)}static shouldNodeUpdate(e,t,o){return!!i.isServer||!o&&(void 0===t||(void 0===e||(t.length!==e.length||!!t.some((t,o)=>!Object.is(t,e[o])))))}static processRawNode(o,r,n){if(null==o||"string"==typeof o||"number"==typeof o||"boolean"==typeof o)return o;if(i.isNodeInstance(o)){if(n||r&&!o.rawProps.disableEmotion){const e=new s.BaseNode(o.element,o.rawProps,o.dependencies);return e.stableKey=`${n}:${e.stableKey}`,r&&!e.rawProps.disableEmotion&&(e.rawProps.disableEmotion=!0),e}return o}if(i.isFunctionChild(o))return new s.BaseNode(i.functionRenderer,{props:{render:o,disableEmotion:r}},void 0);if(e.isValidElement(o)){const{style:e,...t}=o.props,n={...t,...e||{}};return new s.BaseNode(o.type,{...n,...null!==o.key&&void 0!==o.key?{key:o.key}:{},disableEmotion:r},void 0)}return t.isReactClassComponent(o)||t.isMemo(o)||t.isForwardRef(o)?new s.BaseNode(o,{disableEmotion:r},void 0):o instanceof e.Component?i.processRawNode(o.render(),r,n):o}static isFunctionChild(e){if("function"!=typeof e||t.isReactClassComponent(e)||t.isMemo(e)||t.isForwardRef(e))return!1;try{return!(e.prototype&&"function"==typeof e.prototype.render)}catch(e){return r.__DEBUG__&&console.error("MeoNode: Error checking if a node is a function child.",e),!0}}static functionRenderer({render:t,disableEmotion:n}){let a;try{a=t()}catch(e){r.__DEBUG__&&console.error("MeoNode: Error executing function-as-a-child.",e),a=null}if(null==a)return a;if(i.isNodeInstance(a))return n&&!a.rawProps.disableEmotion?new s.BaseNode(a.element,{...a.rawProps,disableEmotion:!0}).render():a.render();if(Array.isArray(a)){const e=(e,t)=>{try{return`${o.getElementTypeName(e)}-${t}`}catch(e){return r.__DEBUG__&&console.error("MeoNode: Could not determine element type name for key in function-as-a-child.",e),`item-${t}`}};return a.map((t,o)=>i.renderProcessedNode({processedElement:i.processRawNode(t,n),passedKey:e(t,o),disableEmotion:n}))}if(a instanceof e.Component)return i.renderProcessedNode({processedElement:i.processRawNode(a.render(),n),disableEmotion:n});if("string"==typeof a||"number"==typeof a||"boolean"==typeof a)return a;const c=i.processRawNode(a,n);return c?i.renderProcessedNode({processedElement:c,disableEmotion:n}):a}static renderProcessedNode({processedElement:o,passedKey:r,disableEmotion:n}){const a={};if(void 0!==r&&(a.key=r),i.isNodeInstance(o)){const e=o.rawProps?.key;return o.rawProps.disableEmotion=n,e===r?o.render():new s.BaseNode(o.element,{...o.rawProps,...a}).render()}return t.isReactClassComponent(o)?new s.BaseNode(o,{...a,disableEmotion:n}).render():o instanceof e.Component?o.render():"function"==typeof o?e.createElement(o,{key:r}):o}static ensurePortalInfrastructure(e){if(i.isServer)return!1;let t=i.portalInfrastructure.get(e);if(t?.domElement?.isConnected&&t?.reactRoot)return!0;if(t&&(!t.domElement?.isConnected||!t.reactRoot)){try{t.reactRoot?.unmount?.()}catch(e){r.__DEBUG__&&console.error("MeoNode: Error unmounting stale portal root.",e)}i.cleanupPortalInfra(t),i.portalInfrastructure.delete(e),t=void 0}const o=document.createElement("div");document.body.appendChild(o);const a=n.createRoot(o),c={render:a.render.bind(a),unmount:a.unmount.bind(a),update:()=>{}};return t={domElement:o,reactRoot:c},i.portalInfrastructure.set(e,t),s.BaseNode.portalCleanupRegistry.register(e,{domElement:o,reactRoot:c},e),!0}static cleanupPortalInfra(e){try{e.reactRoot?.unmount&&e.reactRoot.unmount()}catch(e){r.__DEBUG__&&console.error("Portal cleanup error:",e)}try{e.domElement?.isConnected&&e.domElement.remove()}catch(e){r.__DEBUG__&&console.error("DOM removal error:",e)}}}class a{heap=[];comparator;constructor(e){this.comparator=e}size(){return this.heap.length}isEmpty(){return 0===this.size()}push(e){this.heap.push(e),this.bubbleUp()}pop(){if(this.isEmpty())return;this.swap(0,this.size()-1);const e=this.heap.pop();return this.bubbleDown(),e}bubbleUp(e=this.size()-1){for(;e>0;){const t=Math.floor((e-1)/2);if(this.comparator(this.heap[t],this.heap[e])<=0)break;this.swap(t,e),e=t}}bubbleDown(e=0){const t=this.size()-1;for(;;){const o=2*e+1,r=2*e+2;let s=e;if(o<=t&&this.comparator(this.heap[o],this.heap[s])<0&&(s=o),r<=t&&this.comparator(this.heap[r],this.heap[s])<0&&(s=r),s===e)break;this.swap(e,s),e=s}}swap(e,t){[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]]}}exports.NodeUtil=i;
|
|
1
|
+
"use strict";var e=require("react"),t=require("../helper/react-is.helper.js"),o=require("../helper/common.helper.js"),r=require("../constant/common.const.js"),s=require("../core.node.js"),n=require("react-dom/client");class i{constructor(){}static isServer="undefined"==typeof window;static _functionSignatureCache=new WeakMap;static CACHE_SIZE_LIMIT=500;static CACHE_CLEANUP_BATCH=50;static _cssCache=new WeakMap;static CRITICAL_PROPS=new Set(["css","className","disableEmotion","props"]);static portalInfrastructure=new WeakMap;static isNodeInstance=e=>"object"==typeof e&&null!==e&&"element"in e&&"function"==typeof e.render&&"function"==typeof e.toPortal&&"isBaseNode"in e;static isStyleProp=i.isServer||"undefined"==typeof document?()=>!1:e=>e in document.body.style;static hashString(e){let t=2166136261,o=5381;for(let r=0;r<e.length;r++){const s=e.charCodeAt(r);t^=s,t=Math.imul(t,16777619),o=33*o^s}return`${(t>>>0).toString(36)}_${(o>>>0).toString(36)}`}static hashCSS(e){const t=this._cssCache.get(e);if(t)return t;const o=Object.keys(e);let r=o.length;for(let t=0;t<Math.min(o.length,10);t++){const s=o[t],n=e[s];r=(r<<5)-r+s.charCodeAt(0),r&=r,"string"==typeof n&&(r=(r<<5)-r+n.length)}const s=r.toString(36);return this._cssCache.set(e,s),s}static createPropSignature(e,t){if(i.isServer)return;const r=o.getElementTypeName(e),s=Object.keys(t);s.length>1&&s.sort();const n=[`${r}:`];if("function"==typeof e){let t=i._functionSignatureCache.get(e);t||(t=i.hashString(e.toString()),i._functionSignatureCache.set(e,t)),n.push(t)}for(const e of s){const o=t[e];let r;const s=typeof o;if("string"===s||"number"===s||"boolean"===s)r=`${e}:${o};`;else if(null===o)r=`${e}:null;`;else if(void 0===o)r=`${e}:undefined;`;else if("css"===e&&"object"==typeof o)r=`css:${this.hashCSS(o)};`;else if(Array.isArray(o)){const t=o.filter(e=>{const t=typeof e;return"string"===t||"number"===t||"boolean"===t||null===e});r=t.length===o.length?`${e}:[${t.join(",")}];`:`${e}:[${o.length}];`}else if(o&&o.isBaseNode)r=`${e}:${o.stableKey};`;else{r=`${e}:{${Object.keys(o).sort().join(",")}};`}n.push(r)}return i.hashString(n.join(","))}static extractCriticalProps(e,t){const o={_keyCount:t.length};let r=0;for(const s of t){if(r>=50)break;if(i.CRITICAL_PROPS.has(s)){o[s]=e[s],r++;continue}const n=s.charCodeAt(0);111!==n||110!==s.charCodeAt(1)?!(97===n&&114===s.charCodeAt(1)&&105===s.charCodeAt(2)&&97===s.charCodeAt(3)||100===n&&97===s.charCodeAt(1)&&116===s.charCodeAt(2)&&97===s.charCodeAt(3))?t.length<=100&&i.isStyleProp(s)&&(o[s]=e[s],r++):(o[s]=e[s],r++):(o[s]=e[s],r++)}return o}static getCachedCssProps(e,t){if(i.isServer||!t)return{cssProps:o.getCSSProps(e)};const r=s.BaseNode.propProcessingCache.get(t);if(r)return r.lastAccess=Date.now(),r.hitCount++,{cssProps:r.cssProps};const n=o.getCSSProps(e);return s.BaseNode.propProcessingCache.set(t,{cssProps:n,signature:t,lastAccess:Date.now(),hitCount:1}),s.BaseNode.propProcessingCache.size>i.CACHE_SIZE_LIMIT&&!s.BaseNode.scheduledCleanup&&(s.BaseNode.scheduledCleanup=!0,"undefined"!=typeof requestIdleCallback?requestIdleCallback(()=>{i._evictLRUEntries(),s.BaseNode.scheduledCleanup=!1},{timeout:2e3}):setTimeout(()=>{i._evictLRUEntries(),s.BaseNode.scheduledCleanup=!1},100)),{cssProps:n}}static _evictLRUEntries(){const e=Date.now(),t=new a((e,t)=>t.score-e.score);for(const[o,r]of s.BaseNode.propProcessingCache.entries()){const s=this._calculateEvictionScore(r,e);t.push({key:o,score:s})}for(let e=0;e<i.CACHE_CLEANUP_BATCH;e++){const e=t.pop();if(!e)break;s.BaseNode.propProcessingCache.delete(e.key)}}static _calculateEvictionScore(e,t){return(t-e.lastAccess)/1e3*.3+1e3/(e.hitCount+1)*.7}static processProps(e,t={},r){const{ref:s,key:n,children:a,css:c,props:d={},disableEmotion:l,...p}=t;if(0===Object.keys(p).length&&!c)return o.omitUndefined({ref:s,key:n,disableEmotion:l,nativeProps:o.omitUndefined(d),children:i._processChildren(a,l)});const u={},h={},f=Object.keys(p);for(let e=0;e<f.length;e++){const t=f[e],o=p[t],r=typeof o;"string"===r||"number"===r||"boolean"===r?u[t]=o:h[t]=o}const m=i.createPropSignature(e,u),{cssProps:C}=i.getCachedCssProps(u,m),y=o.getCSSProps(h),b=o.getDOMProps(p),E={...C,...y,...c},g=i._processChildren(a,l,r);return o.omitUndefined({ref:s,key:n,css:E,...b,disableEmotion:l,nativeProps:o.omitUndefined(d),children:g})}static _processChildren(e,t,o){if(e)return"function"==typeof e?e:Array.isArray(e)?1===e.length?i.processRawNode(e[0],t,`${o}_0`):e.map((e,r)=>i.processRawNode(e,t,`${o}_${r}`)):i.processRawNode(e,t,o)}static shouldCacheElement(e){return!i.isServer&&!!e.stableKey&&!!e.dependencies}static shouldNodeUpdate(e,t,o){return!!i.isServer||!o&&(void 0===t||(void 0===e||(t.length!==e.length||!!t.some((t,o)=>!Object.is(t,e[o])))))}static processRawNode(o,r,n){if(null==o||"string"==typeof o||"number"==typeof o||"boolean"==typeof o)return o;if(i.isNodeInstance(o)){if(n||r&&!o.rawProps.disableEmotion){const e=new s.BaseNode(o.element,o.rawProps,o.dependencies);return e.stableKey=`${n}:${e.stableKey}`,r&&!e.rawProps.disableEmotion&&(e.rawProps.disableEmotion=!0),e}return o}if(i.isFunctionChild(o))return new s.BaseNode(i.functionRenderer,{props:{render:o,disableEmotion:r}},void 0);if(e.isValidElement(o)){const{style:e,...t}=o.props,n={...t,...e||{}};return new s.BaseNode(o.type,{...n,...null!==o.key&&void 0!==o.key?{key:o.key}:{},disableEmotion:r},void 0)}return t.isReactClassComponent(o)||t.isMemo(o)||t.isForwardRef(o)?new s.BaseNode(o,{disableEmotion:r},void 0):o instanceof e.Component?i.processRawNode(o.render(),r,n):o}static isFunctionChild(e){if("function"!=typeof e||t.isReactClassComponent(e)||t.isMemo(e)||t.isForwardRef(e))return!1;try{return!(e.prototype&&"function"==typeof e.prototype.render)}catch(e){return r.__DEBUG__&&console.error("MeoNode: Error checking if a node is a function child.",e),!0}}static functionRenderer({render:t,disableEmotion:n}){let a;try{a=t()}catch(e){r.__DEBUG__&&console.error("MeoNode: Error executing function-as-a-child.",e),a=null}if(null==a)return a;if(i.isNodeInstance(a))return n&&!a.rawProps.disableEmotion?new s.BaseNode(a.element,{...a.rawProps,disableEmotion:!0}).render():a.render();if(Array.isArray(a)){const e=(e,t)=>{try{return`${o.getElementTypeName(e)}-${t}`}catch(e){return r.__DEBUG__&&console.error("MeoNode: Could not determine element type name for key in function-as-a-child.",e),`item-${t}`}};return a.map((t,o)=>i.renderProcessedNode({processedElement:i.processRawNode(t,n),passedKey:e(t,o),disableEmotion:n}))}if(a instanceof e.Component)return i.renderProcessedNode({processedElement:i.processRawNode(a.render(),n),disableEmotion:n});if("string"==typeof a||"number"==typeof a||"boolean"==typeof a)return a;const c=i.processRawNode(a,n);return c?i.renderProcessedNode({processedElement:c,disableEmotion:n}):a}static renderProcessedNode({processedElement:o,passedKey:r,disableEmotion:n}){const a={};if(void 0!==r&&(a.key=r),i.isNodeInstance(o)){const e=o.rawProps?.key;return o.rawProps.disableEmotion=n,e===r?o.render():new s.BaseNode(o.element,{...o.rawProps,...a}).render()}return t.isReactClassComponent(o)?new s.BaseNode(o,{...a,disableEmotion:n}).render():o instanceof e.Component?o.render():"function"==typeof o?e.createElement(o,{key:r}):o}static ensurePortalInfrastructure(e){if(i.isServer)return!1;let t=i.portalInfrastructure.get(e);if(t?.domElement?.isConnected&&t?.reactRoot)return!0;if(t&&(!t.domElement?.isConnected||!t.reactRoot)){try{t.reactRoot?.unmount?.()}catch(e){r.__DEBUG__&&console.error("MeoNode: Error unmounting stale portal root.",e)}i.cleanupPortalInfra(t),i.portalInfrastructure.delete(e),t=void 0}const o=document.createElement("div");document.body.appendChild(o);const a=n.createRoot(o),c={render:a.render.bind(a),unmount:a.unmount.bind(a),update:()=>{}};return t={domElement:o,reactRoot:c},i.portalInfrastructure.set(e,t),s.BaseNode.portalCleanupRegistry.register(e,{domElement:o,reactRoot:c},e),!0}static cleanupPortalInfra(e){try{e.reactRoot?.unmount&&e.reactRoot.unmount()}catch(e){r.__DEBUG__&&console.error("Portal cleanup error:",e)}try{e.domElement?.isConnected&&e.domElement.remove()}catch(e){r.__DEBUG__&&console.error("DOM removal error:",e)}}}class a{heap=[];comparator;constructor(e){this.comparator=e}size(){return this.heap.length}isEmpty(){return 0===this.size()}push(e){this.heap.push(e),this.bubbleUp()}pop(){if(this.isEmpty())return;this.swap(0,this.size()-1);const e=this.heap.pop();return this.bubbleDown(),e}bubbleUp(e=this.size()-1){for(;e>0;){const t=Math.floor((e-1)/2);if(this.comparator(this.heap[t],this.heap[e])<=0)break;this.swap(t,e),e=t}}bubbleDown(e=0){const t=this.size()-1;for(;;){const o=2*e+1,r=2*e+2;let s=e;if(o<=t&&this.comparator(this.heap[o],this.heap[s])<0&&(s=o),r<=t&&this.comparator(this.heap[r],this.heap[s])<0&&(s=r),s===e)break;this.swap(e,s),e=s}}swap(e,t){[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]]}}exports.NodeUtil=i;
|
|
@@ -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;IAGzC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAiF;IAEjH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAWnC,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAcnC,YAAY,OAAO,EAAE,CAAC,EAAE,QAAQ,GAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAM,EAAE,IAAI,CAAC,EAAE,cAAc,EAoBlF;IAED;;;;OAIG;IACH,IAAW,KAAK,IAAI,cAAc,CAKjC;IAED;;;;;;;OAOG;IACH,IAAW,YAAY,IAAI,cAAc,GAAG,SAAS,CAEpD;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IA2BrB;;;;;;;;OAQG;IACH,OAAc,oBAAoB;;;OAchC;IAEF;;;;;;;;;;;;;;OAcG;IACH,OAAc,qBAAqB;;;;;OAiCjC;IAEF;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,aAAa,GAAE,OAAe,GAAG,YAAY,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"core.node.d.ts","sourceRoot":"","sources":["../../src/core.node.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/core.node.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isValidElement as e,Fragment as t,createElement as r}from"react";import{isValidElementType as n,isFragment as o}from"./helper/react-is.helper.js";import{getComponentType as s,hasNoStyleTag as a,getElementTypeName as l}from"./helper/common.helper.js";import
|
|
1
|
+
import{isValidElement as e,Fragment as t,createElement as r}from"react";import{isValidElementType as n,isFragment as o}from"./helper/react-is.helper.js";import{getComponentType as s,hasNoStyleTag as a,getElementTypeName as l}from"./helper/common.helper.js";import c from"./components/styled-renderer.client.js";import{__DEBUG__ as i}from"./constant/common.const.js";import{MountTrackerUtil as d}from"./util/mount-tracker.util.js";import h from"./components/meonode-unmounter.client.js";import{NavigationCacheManagerUtil as u}from"./util/navigation-cache-manager.util.js";import{NodeUtil as p}from"./util/node.util.js";import{ThemeUtil as m}from"./util/theme.util.js";class g{instanceId=Math.random().toString(36).slice(2)+Date.now().toString(36);element;rawProps={};isBaseNode=!0;_props;_deps;stableKey;lastPropsObj;lastSignature;static elementCache=new Map;static propProcessingCache=new Map;static scheduledCleanup=!1;static _navigationStarted=!1;static renderContextPool=[];static acquireRenderContext(){const e=g.renderContextPool;return e.length>0?e.pop():{workStack:new Array(512),renderedElements:new Map}}static releaseRenderContext(e){g.renderContextPool.length<50&&e.workStack.length<2048&&(e.workStack.length=0,e.renderedElements.clear(),g.renderContextPool.push(e))}constructor(e,t={},r){if(!n(e)){const t=s(e);throw new Error(`Invalid element type: ${t} provided!`)}this.element=e,this.rawProps=t,this._deps=r;const{ref:o,children:a,...l}=t;this.stableKey=this._getStableKey(l),p.isServer||g._navigationStarted||(u.getInstance().start(),g._navigationStarted=!0)}get props(){return this._props||(this._props=p.processProps(this.element,this.rawProps,this.stableKey)),this._props}get dependencies(){return this._deps}_getStableKey({key:e,...t}){if(p.isServer)return;if(this.lastPropsObj===t)return this.lastSignature;this.lastPropsObj=t;const r=Object.keys(t),n=r.length;if(n>100){const e=p.extractCriticalProps(t,r);this.lastSignature=p.createPropSignature(this.element,e),i&&n>200&&console.warn(`MeoNode: Large props (${n} keys) on "${l(this.element)}". Consider splitting.`)}else this.lastSignature=p.createPropSignature(this.element,t);return null!=e?`${String(e)}:${this.lastSignature}`:this.lastSignature}static cacheCleanupRegistry=new FinalizationRegistry(e=>{const{cacheKey:t,instanceId:r}=e,n=g.elementCache.get(t);n?.instanceId===r&&g.elementCache.delete(t),d.mountedNodes.has(t)&&d.untrackMount(t)});static portalCleanupRegistry=new FinalizationRegistry(e=>{const{domElement:t,reactRoot:r}=e;i&&console.log("[MeoNode] FinalizationRegistry auto-cleaning portal");try{r&&"function"==typeof r.unmount&&r.unmount()}catch(e){i&&console.error("[MeoNode] Portal auto-cleanup unmount error:",e)}try{t?.isConnected&&t.remove()}catch(e){i&&console.error("[MeoNode] Portal auto-cleanup DOM removal error:",e)}});render(n=!1){!p.isServer&&this.stableKey&&d.trackMount(this.stableKey);const s=p.shouldCacheElement(this)?g.elementCache.get(this.stableKey):void 0,l=p.shouldNodeUpdate(s?.prevDeps,this._deps,n);if(!l&&s?.renderedElement)return s.accessCount+=1,s.renderedElement;const i=!l,u=g.acquireRenderContext();let{workStack:m}=u;const{renderedElements:f}=u;let y=0;try{const n=e=>{if(e>m.length){const t=Math.max(e,m.length<<1),r=new Array(t);for(let e=0;e<y;e++)r[e]=m[e];m=r}};for(m[y++]={node:this,isProcessed:!1,blocked:i};y>0;){const s=m[y-1];if(!s){y--;continue}const{node:l,isProcessed:i,blocked:d}=s;if(i){y--;const{children:n,key:s,css:i,nativeProps:d,disableEmotion:h,...u}=l.props;let m=[];if(n){const t=Array.isArray(n)?n:[n],r=t.length;m=new Array(r);for(let n=0;n<r;n++){const r=t[n];if(p.isNodeInstance(r)){const e=f.get(r);if(!e)throw new Error(`[MeoNode] Missing rendered element for child node: ${r.stableKey}`);m[n]=e}else e(r),m[n]=r}}const C={...u,key:s,...d};let P;if(l.element===t||o(l.element))P=r(l.element,{key:s},...m);else{P=!h&&(i||!a(l.element))?r(c,{element:l.element,...C,css:i,suppressHydrationWarning:!0},...m):r(l.element,C,...m)}if(p.shouldCacheElement(l)){const e=g.elementCache.get(l.stableKey);if(e)e.prevDeps=l._deps,e.renderedElement=P,e.accessCount+=1;else{const e={prevDeps:l._deps,renderedElement:P,nodeRef:new WeakRef(l),createdAt:Date.now(),accessCount:1,instanceId:l.instanceId};g.elementCache.set(l.stableKey,e),g.cacheCleanupRegistry.register(l,{cacheKey:l.stableKey,instanceId:l.instanceId},l)}}f.set(l,P)}else{s.isProcessed=!0;const e=l.props.children;if(e){const t=(Array.isArray(e)?e:[e]).filter(p.isNodeInstance);n(y+t.length);for(let e=t.length-1;e>=0;e--){const r=t[e],n=p.shouldCacheElement(r)?g.elementCache.get(r.stableKey):void 0,o=p.shouldNodeUpdate(n?.prevDeps,r._deps,d);if(!o&&n?.renderedElement){f.set(r,n.renderedElement);continue}const s=d||!o;m[y++]={node:r,isProcessed:!1,blocked:s}}}}}const s=f.get(this);return p.shouldCacheElement(this)?r(h,{node:this},s):s}finally{for(let e=0;e<y;e++)m[e]=null;g.releaseRenderContext({workStack:m,renderedElements:f})}}toPortal(){if(!p.ensurePortalInfrastructure(this))throw new Error("toPortal() can only be called in a client-side environment");const e=p.portalInfrastructure.get(this),{domElement:t,reactRoot:r}=e;(()=>{try{r.render(this.render())}catch(e){i&&console.error("[MeoNode] Portal render error:",e)}})();let n=!1;const o=r.unmount.bind(r);return r.update=e=>{if(n)i&&console.warn("[MeoNode] Attempt to update already-unmounted portal");else try{const t=p.isNodeInstance(e)?e.render():e;r.render(t)}catch(e){i&&console.error("[MeoNode] Portal update error:",e)}},r.unmount=()=>{if(n)i&&console.warn("[MeoNode] Portal already unmounted");else{n=!0;try{g.portalCleanupRegistry.unregister(this)}catch(e){i&&console.warn("[MeoNode] Portal unregister warning:",e)}p.portalInfrastructure.delete(this);try{t?.isConnected&&o()}catch(e){i&&console.error("[MeoNode] Portal unmount error:",e)}try{t?.isConnected&&t.remove()}catch(e){i&&console.error("[MeoNode] Portal DOM cleanup error:",e)}}},r}static clearCaches(){const e=Array.from(g.elementCache.keys());i&&console.log(`[MeoNode] clearCaches: Clearing ${e.length} entries`);for(const t of e){const e=g.elementCache.get(t);if(e){const r=e.nodeRef?.deref();if(r)try{g.cacheCleanupRegistry.unregister(r),r.lastSignature=void 0,r.lastPropsObj=void 0}catch{i&&console.warn(`[MeoNode] Could not unregister ${t} from FinalizationRegistry`)}}}g.propProcessingCache.clear(),g.elementCache.clear(),m.clearThemeCache(),d.cleanup(),i&&console.log("[MeoNode] All caches cleared")}}function f(e,t={},r){return new g(e,t,r)}function y(e,t){const r=(r,n)=>f(e,{...t,...r},n);return r.element=e,r}function C(e,t){const r=(r,n,o)=>f(e,{...t,...n,children:r},o);return r.element=e,r}f.clearCaches=g.clearCaches;export{g as BaseNode,f as Node,C as createChildrenFirstNode,y as createNode};
|
|
@@ -122,6 +122,18 @@ export declare class NodeUtil {
|
|
|
122
122
|
* @returns The processed children in normalized format.
|
|
123
123
|
*/
|
|
124
124
|
private static _processChildren;
|
|
125
|
+
/**
|
|
126
|
+
* Determines if a given `NodeInstance` should be cached.
|
|
127
|
+
* Caching is enabled only on the client-side and if the node has both a `stableKey`
|
|
128
|
+
* (indicating it's a stable, identifiable element) and `dependencies` (suggesting its render
|
|
129
|
+
* output might be stable across re-renders if dependencies don't change).
|
|
130
|
+
* @param node The `NodeInstance` to check for cacheability.
|
|
131
|
+
* @returns `true` if the node should be cached, `false` otherwise.
|
|
132
|
+
*/
|
|
133
|
+
static shouldCacheElement<E extends NodeInstance>(node: E): node is E & {
|
|
134
|
+
stableKey: string;
|
|
135
|
+
dependencies: DependencyList;
|
|
136
|
+
};
|
|
125
137
|
/**
|
|
126
138
|
* Determines if a node should update based on its dependency array.
|
|
127
139
|
* Uses a shallow comparison, similar to React's `useMemo` and `useCallback`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.util.d.ts","sourceRoot":"","sources":["../../../src/util/node.util.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node.util.d.ts","sourceRoot":"","sources":["../../../src/util/node.util.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e,{isValidElement as t,createElement as r}from"react";import{isReactClassComponent as o,isMemo as s,isForwardRef as n}from"../helper/react-is.helper.js";import{getElementTypeName as i,getCSSProps as c,omitUndefined as a,getDOMProps as l}from"../helper/common.helper.js";import{__DEBUG__ as p}from"../constant/common.const.js";import{BaseNode as d}from"../core.node.js";import{createRoot as u}from"react-dom/client";class h{constructor(){}static isServer="undefined"==typeof window;static _functionSignatureCache=new WeakMap;static CACHE_SIZE_LIMIT=500;static CACHE_CLEANUP_BATCH=50;static _cssCache=new WeakMap;static CRITICAL_PROPS=new Set(["css","className","disableEmotion","props"]);static portalInfrastructure=new WeakMap;static isNodeInstance=e=>"object"==typeof e&&null!==e&&"element"in e&&"function"==typeof e.render&&"function"==typeof e.toPortal&&"isBaseNode"in e;static isStyleProp=h.isServer||"undefined"==typeof document?()=>!1:e=>e in document.body.style;static hashString(e){let t=2166136261,r=5381;for(let o=0;o<e.length;o++){const s=e.charCodeAt(o);t^=s,t=Math.imul(t,16777619),r=33*r^s}return`${(t>>>0).toString(36)}_${(r>>>0).toString(36)}`}static hashCSS(e){const t=this._cssCache.get(e);if(t)return t;const r=Object.keys(e);let o=r.length;for(let t=0;t<Math.min(r.length,10);t++){const s=r[t],n=e[s];o=(o<<5)-o+s.charCodeAt(0),o&=o,"string"==typeof n&&(o=(o<<5)-o+n.length)}const s=o.toString(36);return this._cssCache.set(e,s),s}static createPropSignature(e,t){if(h.isServer)return;const r=i(e),o=Object.keys(t);o.length>1&&o.sort();const s=[`${r}:`];if("function"==typeof e){let t=h._functionSignatureCache.get(e);t||(t=h.hashString(e.toString()),h._functionSignatureCache.set(e,t)),s.push(t)}for(const e of o){const r=t[e];let o;const n=typeof r;if("string"===n||"number"===n||"boolean"===n)o=`${e}:${r};`;else if(null===r)o=`${e}:null;`;else if(void 0===r)o=`${e}:undefined;`;else if("css"===e&&"object"==typeof r)o=`css:${this.hashCSS(r)};`;else if(Array.isArray(r)){const t=r.filter(e=>{const t=typeof e;return"string"===t||"number"===t||"boolean"===t||null===e});o=t.length===r.length?`${e}:[${t.join(",")}];`:`${e}:[${r.length}];`}else if(r&&r.isBaseNode)o=`${e}:${r.stableKey};`;else{o=`${e}:{${Object.keys(r).sort().join(",")}};`}s.push(o)}return h.hashString(s.join(","))}static extractCriticalProps(e,t){const r={_keyCount:t.length};let o=0;for(const s of t){if(o>=50)break;if(h.CRITICAL_PROPS.has(s)){r[s]=e[s],o++;continue}const n=s.charCodeAt(0);111!==n||110!==s.charCodeAt(1)?!(97===n&&114===s.charCodeAt(1)&&105===s.charCodeAt(2)&&97===s.charCodeAt(3)||100===n&&97===s.charCodeAt(1)&&116===s.charCodeAt(2)&&97===s.charCodeAt(3))?t.length<=100&&h.isStyleProp(s)&&(r[s]=e[s],o++):(r[s]=e[s],o++):(r[s]=e[s],o++)}return r}static getCachedCssProps(e,t){if(h.isServer||!t)return{cssProps:c(e)};const r=d.propProcessingCache.get(t);if(r)return r.lastAccess=Date.now(),r.hitCount++,{cssProps:r.cssProps};const o=c(e);return d.propProcessingCache.set(t,{cssProps:o,signature:t,lastAccess:Date.now(),hitCount:1}),d.propProcessingCache.size>h.CACHE_SIZE_LIMIT&&!d.scheduledCleanup&&(d.scheduledCleanup=!0,"undefined"!=typeof requestIdleCallback?requestIdleCallback(()=>{h._evictLRUEntries(),d.scheduledCleanup=!1},{timeout:2e3}):setTimeout(()=>{h._evictLRUEntries(),d.scheduledCleanup=!1},100)),{cssProps:o}}static _evictLRUEntries(){const e=Date.now(),t=new f((e,t)=>t.score-e.score);for(const[r,o]of d.propProcessingCache.entries()){const s=this._calculateEvictionScore(o,e);t.push({key:r,score:s})}for(let e=0;e<h.CACHE_CLEANUP_BATCH;e++){const e=t.pop();if(!e)break;d.propProcessingCache.delete(e.key)}}static _calculateEvictionScore(e,t){return(t-e.lastAccess)/1e3*.3+1e3/(e.hitCount+1)*.7}static processProps(e,t={},r){const{ref:o,key:s,children:n,css:i,props:p={},disableEmotion:d,...u}=t;if(0===Object.keys(u).length&&!i)return a({ref:o,key:s,disableEmotion:d,nativeProps:a(p),children:h._processChildren(n,d)});const f={},m={},y=Object.keys(u);for(let e=0;e<y.length;e++){const t=y[e],r=u[t],o=typeof r;"string"===o||"number"===o||"boolean"===o?f[t]=r:m[t]=r}const C=h.createPropSignature(e,f),{cssProps:b}=h.getCachedCssProps(f,C),g=c(m),E=l(u),P={...b,...g,...i},w=h._processChildren(n,d,r);return a({ref:o,key:s,css:P,...E,disableEmotion:d,nativeProps:a(p),children:w})}static _processChildren(e,t,r){if(e)return"function"==typeof e?e:Array.isArray(e)?1===e.length?h.processRawNode(e[0],t,`${r}_0`):e.map((e,o)=>h.processRawNode(e,t,`${r}_${o}`)):h.processRawNode(e,t,r)}static shouldNodeUpdate(e,t,r){return!!h.isServer||!r&&(void 0===t||(void 0===e||(t.length!==e.length||!!t.some((t,r)=>!Object.is(t,e[r])))))}static processRawNode(r,i,c){if(null==r||"string"==typeof r||"number"==typeof r||"boolean"==typeof r)return r;if(h.isNodeInstance(r)){if(c||i&&!r.rawProps.disableEmotion){const e=new d(r.element,r.rawProps,r.dependencies);return e.stableKey=`${c}:${e.stableKey}`,i&&!e.rawProps.disableEmotion&&(e.rawProps.disableEmotion=!0),e}return r}if(h.isFunctionChild(r))return new d(h.functionRenderer,{props:{render:r,disableEmotion:i}},void 0);if(t(r)){const{style:e,...t}=r.props,o={...t,...e||{}};return new d(r.type,{...o,...null!==r.key&&void 0!==r.key?{key:r.key}:{},disableEmotion:i},void 0)}return o(r)||s(r)||n(r)?new d(r,{disableEmotion:i},void 0):r instanceof e.Component?h.processRawNode(r.render(),i,c):r}static isFunctionChild(e){if("function"!=typeof e||o(e)||s(e)||n(e))return!1;try{return!(e.prototype&&"function"==typeof e.prototype.render)}catch(e){return p&&console.error("MeoNode: Error checking if a node is a function child.",e),!0}}static functionRenderer({render:t,disableEmotion:r}){let o;try{o=t()}catch(e){p&&console.error("MeoNode: Error executing function-as-a-child.",e),o=null}if(null==o)return o;if(h.isNodeInstance(o))return r&&!o.rawProps.disableEmotion?new d(o.element,{...o.rawProps,disableEmotion:!0}).render():o.render();if(Array.isArray(o)){const e=(e,t)=>{try{return`${i(e)}-${t}`}catch(e){return p&&console.error("MeoNode: Could not determine element type name for key in function-as-a-child.",e),`item-${t}`}};return o.map((t,o)=>h.renderProcessedNode({processedElement:h.processRawNode(t,r),passedKey:e(t,o),disableEmotion:r}))}if(o instanceof e.Component)return h.renderProcessedNode({processedElement:h.processRawNode(o.render(),r),disableEmotion:r});if("string"==typeof o||"number"==typeof o||"boolean"==typeof o)return o;const s=h.processRawNode(o,r);return s?h.renderProcessedNode({processedElement:s,disableEmotion:r}):o}static renderProcessedNode({processedElement:t,passedKey:s,disableEmotion:n}){const i={};if(void 0!==s&&(i.key=s),h.isNodeInstance(t)){const e=t.rawProps?.key;return t.rawProps.disableEmotion=n,e===s?t.render():new d(t.element,{...t.rawProps,...i}).render()}return o(t)?new d(t,{...i,disableEmotion:n}).render():t instanceof e.Component?t.render():"function"==typeof t?r(t,{key:s}):t}static ensurePortalInfrastructure(e){if(h.isServer)return!1;let t=h.portalInfrastructure.get(e);if(t?.domElement?.isConnected&&t?.reactRoot)return!0;if(t&&(!t.domElement?.isConnected||!t.reactRoot)){try{t.reactRoot?.unmount?.()}catch(e){p&&console.error("MeoNode: Error unmounting stale portal root.",e)}h.cleanupPortalInfra(t),h.portalInfrastructure.delete(e),t=void 0}const r=document.createElement("div");document.body.appendChild(r);const o=u(r),s={render:o.render.bind(o),unmount:o.unmount.bind(o),update:()=>{}};return t={domElement:r,reactRoot:s},h.portalInfrastructure.set(e,t),d.portalCleanupRegistry.register(e,{domElement:r,reactRoot:s},e),!0}static cleanupPortalInfra(e){try{e.reactRoot?.unmount&&e.reactRoot.unmount()}catch(e){p&&console.error("Portal cleanup error:",e)}try{e.domElement?.isConnected&&e.domElement.remove()}catch(e){p&&console.error("DOM removal error:",e)}}}class f{heap=[];comparator;constructor(e){this.comparator=e}size(){return this.heap.length}isEmpty(){return 0===this.size()}push(e){this.heap.push(e),this.bubbleUp()}pop(){if(this.isEmpty())return;this.swap(0,this.size()-1);const e=this.heap.pop();return this.bubbleDown(),e}bubbleUp(e=this.size()-1){for(;e>0;){const t=Math.floor((e-1)/2);if(this.comparator(this.heap[t],this.heap[e])<=0)break;this.swap(t,e),e=t}}bubbleDown(e=0){const t=this.size()-1;for(;;){const r=2*e+1,o=2*e+2;let s=e;if(r<=t&&this.comparator(this.heap[r],this.heap[s])<0&&(s=r),o<=t&&this.comparator(this.heap[o],this.heap[s])<0&&(s=o),s===e)break;this.swap(e,s),e=s}}swap(e,t){[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]]}}export{h as NodeUtil};
|
|
1
|
+
import e,{isValidElement as t,createElement as r}from"react";import{isReactClassComponent as o,isMemo as s,isForwardRef as n}from"../helper/react-is.helper.js";import{getElementTypeName as i,getCSSProps as c,omitUndefined as a,getDOMProps as l}from"../helper/common.helper.js";import{__DEBUG__ as p}from"../constant/common.const.js";import{BaseNode as d}from"../core.node.js";import{createRoot as u}from"react-dom/client";class h{constructor(){}static isServer="undefined"==typeof window;static _functionSignatureCache=new WeakMap;static CACHE_SIZE_LIMIT=500;static CACHE_CLEANUP_BATCH=50;static _cssCache=new WeakMap;static CRITICAL_PROPS=new Set(["css","className","disableEmotion","props"]);static portalInfrastructure=new WeakMap;static isNodeInstance=e=>"object"==typeof e&&null!==e&&"element"in e&&"function"==typeof e.render&&"function"==typeof e.toPortal&&"isBaseNode"in e;static isStyleProp=h.isServer||"undefined"==typeof document?()=>!1:e=>e in document.body.style;static hashString(e){let t=2166136261,r=5381;for(let o=0;o<e.length;o++){const s=e.charCodeAt(o);t^=s,t=Math.imul(t,16777619),r=33*r^s}return`${(t>>>0).toString(36)}_${(r>>>0).toString(36)}`}static hashCSS(e){const t=this._cssCache.get(e);if(t)return t;const r=Object.keys(e);let o=r.length;for(let t=0;t<Math.min(r.length,10);t++){const s=r[t],n=e[s];o=(o<<5)-o+s.charCodeAt(0),o&=o,"string"==typeof n&&(o=(o<<5)-o+n.length)}const s=o.toString(36);return this._cssCache.set(e,s),s}static createPropSignature(e,t){if(h.isServer)return;const r=i(e),o=Object.keys(t);o.length>1&&o.sort();const s=[`${r}:`];if("function"==typeof e){let t=h._functionSignatureCache.get(e);t||(t=h.hashString(e.toString()),h._functionSignatureCache.set(e,t)),s.push(t)}for(const e of o){const r=t[e];let o;const n=typeof r;if("string"===n||"number"===n||"boolean"===n)o=`${e}:${r};`;else if(null===r)o=`${e}:null;`;else if(void 0===r)o=`${e}:undefined;`;else if("css"===e&&"object"==typeof r)o=`css:${this.hashCSS(r)};`;else if(Array.isArray(r)){const t=r.filter(e=>{const t=typeof e;return"string"===t||"number"===t||"boolean"===t||null===e});o=t.length===r.length?`${e}:[${t.join(",")}];`:`${e}:[${r.length}];`}else if(r&&r.isBaseNode)o=`${e}:${r.stableKey};`;else{o=`${e}:{${Object.keys(r).sort().join(",")}};`}s.push(o)}return h.hashString(s.join(","))}static extractCriticalProps(e,t){const r={_keyCount:t.length};let o=0;for(const s of t){if(o>=50)break;if(h.CRITICAL_PROPS.has(s)){r[s]=e[s],o++;continue}const n=s.charCodeAt(0);111!==n||110!==s.charCodeAt(1)?!(97===n&&114===s.charCodeAt(1)&&105===s.charCodeAt(2)&&97===s.charCodeAt(3)||100===n&&97===s.charCodeAt(1)&&116===s.charCodeAt(2)&&97===s.charCodeAt(3))?t.length<=100&&h.isStyleProp(s)&&(r[s]=e[s],o++):(r[s]=e[s],o++):(r[s]=e[s],o++)}return r}static getCachedCssProps(e,t){if(h.isServer||!t)return{cssProps:c(e)};const r=d.propProcessingCache.get(t);if(r)return r.lastAccess=Date.now(),r.hitCount++,{cssProps:r.cssProps};const o=c(e);return d.propProcessingCache.set(t,{cssProps:o,signature:t,lastAccess:Date.now(),hitCount:1}),d.propProcessingCache.size>h.CACHE_SIZE_LIMIT&&!d.scheduledCleanup&&(d.scheduledCleanup=!0,"undefined"!=typeof requestIdleCallback?requestIdleCallback(()=>{h._evictLRUEntries(),d.scheduledCleanup=!1},{timeout:2e3}):setTimeout(()=>{h._evictLRUEntries(),d.scheduledCleanup=!1},100)),{cssProps:o}}static _evictLRUEntries(){const e=Date.now(),t=new f((e,t)=>t.score-e.score);for(const[r,o]of d.propProcessingCache.entries()){const s=this._calculateEvictionScore(o,e);t.push({key:r,score:s})}for(let e=0;e<h.CACHE_CLEANUP_BATCH;e++){const e=t.pop();if(!e)break;d.propProcessingCache.delete(e.key)}}static _calculateEvictionScore(e,t){return(t-e.lastAccess)/1e3*.3+1e3/(e.hitCount+1)*.7}static processProps(e,t={},r){const{ref:o,key:s,children:n,css:i,props:p={},disableEmotion:d,...u}=t;if(0===Object.keys(u).length&&!i)return a({ref:o,key:s,disableEmotion:d,nativeProps:a(p),children:h._processChildren(n,d)});const f={},m={},y=Object.keys(u);for(let e=0;e<y.length;e++){const t=y[e],r=u[t],o=typeof r;"string"===o||"number"===o||"boolean"===o?f[t]=r:m[t]=r}const C=h.createPropSignature(e,f),{cssProps:b}=h.getCachedCssProps(f,C),g=c(m),E=l(u),P={...b,...g,...i},w=h._processChildren(n,d,r);return a({ref:o,key:s,css:P,...E,disableEmotion:d,nativeProps:a(p),children:w})}static _processChildren(e,t,r){if(e)return"function"==typeof e?e:Array.isArray(e)?1===e.length?h.processRawNode(e[0],t,`${r}_0`):e.map((e,o)=>h.processRawNode(e,t,`${r}_${o}`)):h.processRawNode(e,t,r)}static shouldCacheElement(e){return!h.isServer&&!!e.stableKey&&!!e.dependencies}static shouldNodeUpdate(e,t,r){return!!h.isServer||!r&&(void 0===t||(void 0===e||(t.length!==e.length||!!t.some((t,r)=>!Object.is(t,e[r])))))}static processRawNode(r,i,c){if(null==r||"string"==typeof r||"number"==typeof r||"boolean"==typeof r)return r;if(h.isNodeInstance(r)){if(c||i&&!r.rawProps.disableEmotion){const e=new d(r.element,r.rawProps,r.dependencies);return e.stableKey=`${c}:${e.stableKey}`,i&&!e.rawProps.disableEmotion&&(e.rawProps.disableEmotion=!0),e}return r}if(h.isFunctionChild(r))return new d(h.functionRenderer,{props:{render:r,disableEmotion:i}},void 0);if(t(r)){const{style:e,...t}=r.props,o={...t,...e||{}};return new d(r.type,{...o,...null!==r.key&&void 0!==r.key?{key:r.key}:{},disableEmotion:i},void 0)}return o(r)||s(r)||n(r)?new d(r,{disableEmotion:i},void 0):r instanceof e.Component?h.processRawNode(r.render(),i,c):r}static isFunctionChild(e){if("function"!=typeof e||o(e)||s(e)||n(e))return!1;try{return!(e.prototype&&"function"==typeof e.prototype.render)}catch(e){return p&&console.error("MeoNode: Error checking if a node is a function child.",e),!0}}static functionRenderer({render:t,disableEmotion:r}){let o;try{o=t()}catch(e){p&&console.error("MeoNode: Error executing function-as-a-child.",e),o=null}if(null==o)return o;if(h.isNodeInstance(o))return r&&!o.rawProps.disableEmotion?new d(o.element,{...o.rawProps,disableEmotion:!0}).render():o.render();if(Array.isArray(o)){const e=(e,t)=>{try{return`${i(e)}-${t}`}catch(e){return p&&console.error("MeoNode: Could not determine element type name for key in function-as-a-child.",e),`item-${t}`}};return o.map((t,o)=>h.renderProcessedNode({processedElement:h.processRawNode(t,r),passedKey:e(t,o),disableEmotion:r}))}if(o instanceof e.Component)return h.renderProcessedNode({processedElement:h.processRawNode(o.render(),r),disableEmotion:r});if("string"==typeof o||"number"==typeof o||"boolean"==typeof o)return o;const s=h.processRawNode(o,r);return s?h.renderProcessedNode({processedElement:s,disableEmotion:r}):o}static renderProcessedNode({processedElement:t,passedKey:s,disableEmotion:n}){const i={};if(void 0!==s&&(i.key=s),h.isNodeInstance(t)){const e=t.rawProps?.key;return t.rawProps.disableEmotion=n,e===s?t.render():new d(t.element,{...t.rawProps,...i}).render()}return o(t)?new d(t,{...i,disableEmotion:n}).render():t instanceof e.Component?t.render():"function"==typeof t?r(t,{key:s}):t}static ensurePortalInfrastructure(e){if(h.isServer)return!1;let t=h.portalInfrastructure.get(e);if(t?.domElement?.isConnected&&t?.reactRoot)return!0;if(t&&(!t.domElement?.isConnected||!t.reactRoot)){try{t.reactRoot?.unmount?.()}catch(e){p&&console.error("MeoNode: Error unmounting stale portal root.",e)}h.cleanupPortalInfra(t),h.portalInfrastructure.delete(e),t=void 0}const r=document.createElement("div");document.body.appendChild(r);const o=u(r),s={render:o.render.bind(o),unmount:o.unmount.bind(o),update:()=>{}};return t={domElement:r,reactRoot:s},h.portalInfrastructure.set(e,t),d.portalCleanupRegistry.register(e,{domElement:r,reactRoot:s},e),!0}static cleanupPortalInfra(e){try{e.reactRoot?.unmount&&e.reactRoot.unmount()}catch(e){p&&console.error("Portal cleanup error:",e)}try{e.domElement?.isConnected&&e.domElement.remove()}catch(e){p&&console.error("DOM removal error:",e)}}}class f{heap=[];comparator;constructor(e){this.comparator=e}size(){return this.heap.length}isEmpty(){return 0===this.size()}push(e){this.heap.push(e),this.bubbleUp()}pop(){if(this.isEmpty())return;this.swap(0,this.size()-1);const e=this.heap.pop();return this.bubbleDown(),e}bubbleUp(e=this.size()-1){for(;e>0;){const t=Math.floor((e-1)/2);if(this.comparator(this.heap[t],this.heap[e])<=0)break;this.swap(t,e),e=t}}bubbleDown(e=0){const t=this.size()-1;for(;;){const r=2*e+1,o=2*e+2;let s=e;if(r<=t&&this.comparator(this.heap[r],this.heap[s])<0&&(s=r),o<=t&&this.comparator(this.heap[o],this.heap[s])<0&&(s=o),s===e)break;this.swap(e,s),e=s}}swap(e,t){[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]]}}export{h as NodeUtil};
|
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.14",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main.js",
|
|
7
7
|
"types": "./dist/main.d.ts",
|