neo.mjs 10.2.1 → 10.3.1
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/.github/CONCEPT.md +2 -4
- package/.github/GETTING_STARTED.md +72 -51
- package/.github/RELEASE_NOTES/v10.3.0.md +71 -0
- package/.github/RELEASE_NOTES/v10.3.1.md +14 -0
- package/.github/epic-string-based-templates.md +690 -0
- package/ServiceWorker.mjs +2 -2
- package/apps/covid/view/MainContainer.mjs +1 -1
- package/apps/covid/view/country/Table.mjs +1 -1
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/apps/portal/view/learn/ContentComponent.mjs +1 -1
- package/apps/realworld/api/Base.mjs +2 -2
- package/apps/sharedcovid/view/MainContainer.mjs +1 -1
- package/apps/sharedcovid/view/MainContainerController.mjs +1 -1
- package/buildScripts/buildAll.mjs +4 -0
- package/buildScripts/buildESModules.mjs +23 -75
- package/buildScripts/bundleParse5.mjs +27 -0
- package/buildScripts/util/astTemplateProcessor.mjs +210 -0
- package/buildScripts/util/templateBuildProcessor.mjs +331 -0
- package/buildScripts/webpack/development/webpack.config.appworker.mjs +11 -0
- package/buildScripts/webpack/loader/template-loader.mjs +21 -0
- package/buildScripts/webpack/production/webpack.config.appworker.mjs +11 -0
- package/examples/README.md +1 -1
- package/examples/component/wrapper/googleMaps/MarkerDialog.mjs +2 -2
- package/examples/form/field/email/MainContainer.mjs +0 -1
- package/examples/form/field/number/MainContainer.mjs +0 -1
- package/examples/form/field/picker/MainContainer.mjs +0 -1
- package/examples/form/field/time/MainContainer.mjs +0 -1
- package/examples/form/field/trigger/copyToClipboard/MainContainer.mjs +0 -1
- package/examples/form/field/url/MainContainer.mjs +0 -1
- package/examples/functional/nestedTemplateComponent/Component.mjs +100 -0
- package/examples/functional/nestedTemplateComponent/MainContainer.mjs +48 -0
- package/examples/functional/nestedTemplateComponent/app.mjs +6 -0
- package/examples/functional/nestedTemplateComponent/index.html +11 -0
- package/examples/functional/nestedTemplateComponent/neo-config.json +6 -0
- package/examples/functional/templateComponent/Component.mjs +61 -0
- package/examples/functional/templateComponent/MainContainer.mjs +48 -0
- package/examples/functional/templateComponent/app.mjs +6 -0
- package/examples/functional/templateComponent/index.html +11 -0
- package/examples/functional/templateComponent/neo-config.json +6 -0
- package/learn/gettingstarted/Setup.md +29 -12
- package/learn/guides/fundamentals/ApplicationBootstrap.md +2 -2
- package/learn/guides/fundamentals/InstanceLifecycle.md +5 -5
- package/learn/guides/uibuildingblocks/HtmlTemplates.md +191 -0
- package/learn/guides/uibuildingblocks/HtmlTemplatesUnderTheHood.md +156 -0
- package/learn/guides/uibuildingblocks/WorkingWithVDom.md +1 -1
- package/learn/tree.json +2 -0
- package/package.json +61 -56
- package/src/DefaultConfig.mjs +3 -3
- package/src/calendar/view/calendars/List.mjs +1 -1
- package/src/calendar/view/month/Component.mjs +1 -1
- package/src/calendar/view/week/Component.mjs +1 -1
- package/src/component/Abstract.mjs +1 -1
- package/src/component/Base.mjs +33 -27
- package/src/container/Base.mjs +5 -5
- package/src/controller/Application.mjs +5 -5
- package/src/dialog/Base.mjs +6 -6
- package/src/draggable/DragProxyComponent.mjs +4 -4
- package/src/form/field/ComboBox.mjs +1 -1
- package/src/functional/_export.mjs +2 -1
- package/src/functional/component/Base.mjs +142 -93
- package/src/functional/util/HtmlTemplateProcessor.mjs +243 -0
- package/src/functional/util/html.mjs +24 -67
- package/src/list/Base.mjs +2 -2
- package/src/manager/Toast.mjs +1 -1
- package/src/menu/List.mjs +1 -1
- package/src/mixin/VdomLifecycle.mjs +87 -90
- package/src/tab/Container.mjs +2 -2
- package/src/tooltip/Base.mjs +1 -1
- package/src/tree/Accordion.mjs +2 -2
- package/src/worker/App.mjs +7 -7
- package/test/components/files/component/Base.mjs +1 -1
- package/test/siesta/siesta.js +2 -0
- package/test/siesta/tests/classic/Button.mjs +5 -5
- package/test/siesta/tests/functional/Button.mjs +6 -6
- package/test/siesta/tests/functional/HtmlTemplateComponent.mjs +193 -33
- package/test/siesta/tests/functional/Parse5Processor.mjs +82 -0
- package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +5 -5
- package/.github/epic-functional-components.md +0 -498
- package/.github/ticket-asymmetric-vdom-updates.md +0 -122
@@ -1,498 +0,0 @@
|
|
1
|
-
# Epic: Functional Components
|
2
|
-
|
3
|
-
## Motivation
|
4
|
-
|
5
|
-
This initiative will establish a new, lightweight base class for components. This class will bypass the traditional `items` array and `layout` system. Instead, it will be driven by a central `Effect` that calls a VDOM-generating method (e.g., `createVdom()`) to declaratively build the component's UI based on its current state. This aligns with the reactive patterns of other popular frameworks and provides a more intuitive and familiar entry point for those developers.
|
6
|
-
|
7
|
-
This epic will be broken down into several sub-tickets to implement this new component architecture in an iterative and isolated manner.
|
8
|
-
|
9
|
-
Functional Components are an addition to, and not a replacement for declarative component trees based on `container.Base`: `items`.
|
10
|
-
|
11
|
-
---
|
12
|
-
|
13
|
-
## Two Modes of Functional Component Definition
|
14
|
-
|
15
|
-
Neo.mjs will offer two distinct ways to define functional components, catering to different developer preferences and needs. Both modes leverage the underlying `Neo.functional.component.Base` class and the `Neo.core.Effect` system for reactive rendering, but they provide different levels of abstraction and access to the framework's full power.
|
16
|
-
|
17
|
-
### 1. Beginner Mode: Pure Function with Hooks (e.g., `Neo.functional.defineComponent`)
|
18
|
-
|
19
|
-
This mode is designed for developers seeking a highly concise and familiar syntax, especially those coming from frameworks like React. Components are defined as plain JavaScript functions that return VDOM. State management is handled via dedicated hooks (e.g., `useConfig`).
|
20
|
-
|
21
|
-
**Characteristics:**
|
22
|
-
- **Concise Syntax:** Focus on the VDOM rendering logic.
|
23
|
-
- **Hook-based State:** State and side effects are managed through `use` hooks.
|
24
|
-
- **Simplified API:** Abstracts away class boilerplate.
|
25
|
-
- **Tier 1 Reactivity:** Primarily leverages `Neo.core.Config` for reactive values and `Neo.core.Effect` for re-rendering.
|
26
|
-
- **No Lifecycle Hooks:** Does not expose `beforeGet`, `beforeSet`, or `afterSet` lifecycle hooks directly on the component definition, as these are tied to the class-based `static config` system.
|
27
|
-
|
28
|
-
### 2. Medium Mode: Class-based Functional Component (Extending `Neo.functional.component.Base`)
|
29
|
-
|
30
|
-
This mode provides direct access to the underlying `Neo.functional.component.Base` class, allowing developers to define components using `static config` properties. This offers a more explicit and powerful way to define reactive components within the Neo.mjs class system.
|
31
|
-
|
32
|
-
**Characteristics:**
|
33
|
-
- **Explicit Configs:** State is defined via `static config` properties.
|
34
|
-
- **Full Two-Tier Reactivity:** Access to both `Neo.core.Config` (Tier 1) and the `autoGenerateGetSet` mechanism (Tier 2), including `afterSet` lifecycle hooks for imperative side effects.
|
35
|
-
- **Class-based Structure:** Familiar to developers comfortable with class-based component patterns.
|
36
|
-
|
37
|
-
---
|
38
|
-
|
39
|
-
## Sub-Ticket: Create `Neo.component.mixin.VdomLifecycle`
|
40
|
-
|
41
|
-
**Status:** Done
|
42
|
-
|
43
|
-
### 1. Summary
|
44
|
-
|
45
|
-
Create a new mixin named `Neo.component.mixin.VdomLifecycle`. This mixin will encapsulate the core VDOM rendering engine logic currently located in `Neo.component.Base`. This is the foundational step for enabling the new functional component base class and for cleaning up the existing component architecture.
|
46
|
-
|
47
|
-
### 2. Rationale
|
48
|
-
|
49
|
-
The new reactivity layer introduced in v10, centered around `Neo.core.Config` and `Neo.core.Effect`, allows for a highly efficient and declarative component model. The `src/button/Effect.mjs` class serves as a proof-of-concept for this pattern, where a single `Effect` replaces dozens of imperative `afterSet` hooks.
|
50
|
-
|
51
|
-
This new, simpler component architecture relies on the ability to compose features using Mixins. With the recent enhancement enabling mixins to merge their `static config` into a consuming class, we can now create self-contained "feature mixins" (e.g., for VDOM management).
|
52
|
-
|
53
|
-
Extracting the VDOM logic from `component.Base` into `Neo.component.mixin.VdomLifecycle` is the foundational step. It will:
|
54
|
-
- Improve code modularity and separation of concerns.
|
55
|
-
- Slim down `component.Base`, making it easier to understand and maintain.
|
56
|
-
- Provide a reusable piece of core machinery that can be used by the new `FunctionalComponentBase` without inheriting all of `component.Base`.
|
57
|
-
|
58
|
-
### 3. Scope & Implementation Plan
|
59
|
-
|
60
|
-
1. **Create File:** Create a new file at `src/mixin/VdomLifecycle.mjs`.
|
61
|
-
2. **Identify & Move Logic:** Move the properties and methods related to the VDOM rendering engine from `src/component/Base.mjs` into `Neo.component.mixin.VdomLifecycle`. The list of candidates we previously identified will be used as the basis for this refactoring.
|
62
|
-
3. **Refactor `component.Base`:** Modify `src/component/Base.mjs` to use the new `VdomLifecycle` mixin, ensuring all existing functionality remains intact.
|
63
|
-
|
64
|
-
### 4. Definition of Done
|
65
|
-
|
66
|
-
- `Neo.component.mixin.VdomLifecycle` is created and contains the extracted VDOM logic.
|
67
|
-
- `Neo.component.Base` uses the new mixin.
|
68
|
-
- All existing component-related tests pass without regression, confirming the refactoring is successful.
|
69
|
-
|
70
|
-
---
|
71
|
-
|
72
|
-
## Sub-Ticket: Create `Neo.functional.component.Base`
|
73
|
-
|
74
|
-
**Status:** Done
|
75
|
-
|
76
|
-
### 1. Summary
|
77
|
-
|
78
|
-
Create a new base class, `Neo.functional.component.Base`, which will serve as the foundational class for all functional components. This class provides the core reactive rendering mechanism and acts as the underlying base for both class-based functional components and the simpler, function-based "beginner mode" components.
|
79
|
-
|
80
|
-
### 2. Rationale
|
81
|
-
|
82
|
-
The primary goal of the Functional Components epic is to provide a simpler entry point into the Neo.mjs ecosystem while also offering the full power of its reactivity. This base class achieves this by providing a minimal, modern API for creating components, directly appealing to developers familiar with frameworks like React and Vue, and serving as the common foundation for different component definition styles.
|
83
|
-
|
84
|
-
### 3. Scope & Implementation Plan
|
85
|
-
|
86
|
-
1. **Create File:** Create a new file at `src/functional/component/Base.mjs`.
|
87
|
-
2. **Class Definition:**
|
88
|
-
* The class will extend `Neo.core.Base`.
|
89
|
-
* It will use the `Neo.component.mixin.VdomLifecycle` (created in the prerequisite ticket).
|
90
|
-
* It will **not** extend `Neo.component.Base` or `Neo.container.Base`.
|
91
|
-
3. **Core API:**
|
92
|
-
* It will introduce a new method for developers to implement: `createVdom()`. This method is responsible for returning the component's VDOM structure based on its current configs (state).
|
93
|
-
* In its `construct()` method, it will create a `Neo.core.Effect`. This effect will wrap a call to `this.createVdom()` and assign the result to `this.vdom`. This ensures that any time a config used within `createVdom()` is changed, the component automatically re-renders.
|
94
|
-
|
95
|
-
### 4. Example Usage (Class-based Functional Component)
|
96
|
-
|
97
|
-
```javascript
|
98
|
-
import FunctionalBase from 'neo/functional/component/Base.mjs';
|
99
|
-
|
100
|
-
class MyFunctionalButton extends FunctionalBase {
|
101
|
-
static config = {
|
102
|
-
className: 'MyApp.MyFunctionalButton',
|
103
|
-
text_ : 'Click Me'
|
104
|
-
}
|
105
|
-
|
106
|
-
createVdom() {
|
107
|
-
return {
|
108
|
-
tag : 'button',
|
109
|
-
text: this.text
|
110
|
-
};
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
// An instance of this component would render a button and
|
115
|
-
// automatically update its text whenever `myButton.text = 'new text'` is called.
|
116
|
-
```
|
117
|
-
|
118
|
-
### 5. Definition of Done
|
119
|
-
|
120
|
-
- `src/functional/component/Base.mjs` is created.
|
121
|
-
- The class works as described, using `VdomLifecycle` and a central `Effect`.
|
122
|
-
- A basic test case is created to verify that a simple `FunctionalBase` component can be rendered and updates when its configs change.
|
123
|
-
|
124
|
-
---
|
125
|
-
|
126
|
-
## Sub-Ticket: Create `Neo.functional.useConfig` Hook
|
127
|
-
|
128
|
-
**Status:** In Progress
|
129
|
-
|
130
|
-
### 1. Summary
|
131
|
-
|
132
|
-
Implement a `useConfig` hook for functional components, allowing developers to manage reactive state within their "render" functions in a React-like fashion.
|
133
|
-
|
134
|
-
### 2. Rationale
|
135
|
-
|
136
|
-
This hook provides a simplified entry point for developers familiar with `useState` from other frameworks. It leverages Neo.mjs's Tier 1 reactivity (`Neo.core.Config`) for state management without requiring class-based config definitions, making it ideal for the "beginner mode" functional component experience.
|
137
|
-
|
138
|
-
### 3. Scope & Implementation Plan
|
139
|
-
|
140
|
-
1. **Create File:** Create `src/functional/useConfig.mjs`.
|
141
|
-
2. **Implement `useConfig`:** The hook will return a `[value, setter]` tuple. The `setter` will update an internal `Neo.core.Config` instance.
|
142
|
-
3. **Lifecycle Management:** Ensure the `Neo.core.Config` instance is properly managed (created, updated, destroyed) in relation to the functional component's lifecycle. This will likely involve associating the `core.Config` instance with the `Neo.functional.component.Base` instance that is executing the "render" function.
|
143
|
-
|
144
|
-
### 4. Example Usage
|
145
|
-
|
146
|
-
```javascript
|
147
|
-
import { useConfig } from 'neo/functional/useConfig.mjs';
|
148
|
-
import { defineComponent } from 'neo/functional/defineComponent.mjs'; // Assuming this exists
|
149
|
-
|
150
|
-
const MyCounter = defineComponent({
|
151
|
-
className: 'MyApp.MyCounter',
|
152
|
-
createVdom: () => {
|
153
|
-
const [count, setCount] = useConfig(0);
|
154
|
-
|
155
|
-
return {
|
156
|
-
tag: 'button',
|
157
|
-
text: `Count: ${count}`,
|
158
|
-
listeners: {
|
159
|
-
click: () => setCount(count + 1)
|
160
|
-
}
|
161
|
-
};
|
162
|
-
}
|
163
|
-
});
|
164
|
-
```
|
165
|
-
|
166
|
-
### 5. Definition of Done
|
167
|
-
|
168
|
-
- `Neo.functional.useConfig` hook is implemented and tested.
|
169
|
-
- It correctly creates and manages reactive state via `Neo.core.Config`.
|
170
|
-
- Changes to the state trigger re-execution of the component's render function (via `Effect`).
|
171
|
-
|
172
|
-
---
|
173
|
-
|
174
|
-
## Sub-Ticket: Create `Neo.functional.defineComponent` Factory
|
175
|
-
|
176
|
-
**Status:** Done
|
177
|
-
|
178
|
-
### 1. Summary
|
179
|
-
|
180
|
-
Implement a factory function that allows developers to define functional components using a plain JavaScript function, abstracting away the underlying class creation.
|
181
|
-
|
182
|
-
### 2. Rationale
|
183
|
-
|
184
|
-
This factory further simplifies the developer experience for "beginner mode" functional components, making the syntax more concise and familiar to developers accustomed to functional component patterns in other frameworks. It acts as the bridge between a pure function definition and the underlying `Neo.functional.component.Base` class.
|
185
|
-
|
186
|
-
### 3. Scope & Implementation Plan
|
187
|
-
|
188
|
-
1. **Create File:** Create `src/functional/defineComponent.mjs`.
|
189
|
-
2. **Implement `defineComponent`:** The factory will accept a configuration object (including `className`, optional `ntype`, and the `createVdom` function).
|
190
|
-
3. **Internal Class Generation:** Internally, `defineComponent` will create a new class that extends `Neo.functional.component.Base`. It will apply the provided `className` and `ntype` to this new class.
|
191
|
-
4. **`createVdom` Method Assignment:** The developer's `createVdom` function will be assigned as the `createVdom` method of the generated class's prototype.
|
192
|
-
5. **Integration with `useConfig`:** Ensure that the context (`this`) within the developer's `createVdom` function (when executed by the generated component instance) allows `useConfig` to correctly associate state with that instance.
|
193
|
-
|
194
|
-
### 4. Example Usage
|
195
|
-
|
196
|
-
```javascript
|
197
|
-
import { defineComponent } from 'neo/functional/defineComponent.mjs';
|
198
|
-
import { useConfig } from 'neo/functional/useConfig.mjs';
|
199
|
-
|
200
|
-
const MyGreeting = defineComponent({
|
201
|
-
className: 'MyApp.MyGreeting',
|
202
|
-
createVdom: (config) => {
|
203
|
-
const [name, setName] = useConfig('World');
|
204
|
-
|
205
|
-
return {
|
206
|
-
tag: 'div',
|
207
|
-
text: `Hello, ${name}!`,
|
208
|
-
listeners: {
|
209
|
-
click: () => setName(name === 'World' ? 'Neo.mjs' : 'World')
|
210
|
-
}
|
211
|
-
};
|
212
|
-
}
|
213
|
-
});
|
214
|
-
```
|
215
|
-
|
216
|
-
### 5. Definition of Done
|
217
|
-
|
218
|
-
- `Neo.functional.defineComponent` factory is implemented and tested.
|
219
|
-
- It successfully generates functional component classes from plain functions.
|
220
|
-
- Generated components correctly utilize `useConfig` for state management.
|
221
|
-
|
222
|
-
---
|
223
|
-
|
224
|
-
# Sub-Ticket: Enhance `Neo.functional.component.Base` for Hook Support
|
225
|
-
|
226
|
-
**Status:** Done
|
227
|
-
|
228
|
-
## 1. Summary
|
229
|
-
|
230
|
-
This ticket covers the foundational work on `Neo.functional.component.Base` to enable the hook system (`useConfig`, `useEvent`, etc.) for beginner-mode functional components. The key challenge was to allow external hook functions to manage state on a component instance without exposing that state through the public API.
|
231
|
-
|
232
|
-
## 2. Rationale
|
233
|
-
|
234
|
-
The initial implementation of `functional.component.Base` was a minimal class with a `createVdom` method driven by an `Effect`. To support hooks like React's `useState`, we needed a mechanism to:
|
235
|
-
1. Associate state (like `Config` instances) with a specific component instance.
|
236
|
-
2. Track the order of hook calls within a single `createVdom` execution.
|
237
|
-
3. Provide a way for external hook functions to access this internal state securely.
|
238
|
-
|
239
|
-
Using protected properties (e.g., `_hooks`) was considered but deemed insufficient for true encapsulation. The chosen solution provides a robust, framework-private way to manage hook state.
|
240
|
-
|
241
|
-
## 3. Scope & Implementation Plan
|
242
|
-
|
243
|
-
1. **Introduce Symbols for State:**
|
244
|
-
* Create two `Symbol.for()` symbols: `hookIndexSymbol` and `hooksSymbol`.
|
245
|
-
* These symbols act as unique keys for properties on the component instance, making them accessible to any module that knows the symbol, but keeping them off the public API.
|
246
|
-
|
247
|
-
2. **Initialize State in `FunctionalBase`:**
|
248
|
-
* In the `construct()` method of `functional.component.Base`, use `Object.defineProperties` to add the symbol-keyed properties (`[hookIndexSymbol]` and `[hooksSymbol]`) to the component instance.
|
249
|
-
* These properties are configured to be non-enumerable (`enumerable: false`) to further hide them from standard object iteration.
|
250
|
-
* The `hookIndex` is reset to `0` at the beginning of every `vdomEffect` execution, ensuring a clean slate for each render.
|
251
|
-
|
252
|
-
3. **Component Registration:**
|
253
|
-
* Implement the `afterSetId()` and `destroy()` methods to correctly register and unregister the functional component instance with `Neo.manager.Component`. This makes functional components discoverable via `Neo.getComponent()`, integrating them fully into the framework's component model.
|
254
|
-
|
255
|
-
4. **Link Effect to Component:**
|
256
|
-
* Modify the `vdomEffect` creation to pass the component's ID (`this.id`) to the `Effect` constructor. This allows the `useConfig` hook to retrieve the currently rendering component instance by getting the active effect from `EffectManager` and looking up the component by its ID.
|
257
|
-
|
258
|
-
## 4. Definition of Done
|
259
|
-
|
260
|
-
- `functional.component.Base` is updated to use `Symbol.for()` to manage internal hook state.
|
261
|
-
- The component correctly registers and unregisters itself with `ComponentManager`.
|
262
|
-
- The `vdomEffect` is correctly associated with the component's ID.
|
263
|
-
- The implementation provides the necessary foundation for the `useConfig` hook to function correctly.
|
264
|
-
|
265
|
-
# Sub-Ticket: Enhance `Neo.core.Effect` Constructor
|
266
|
-
|
267
|
-
**Status:** Done
|
268
|
-
|
269
|
-
## 1. Summary
|
270
|
-
|
271
|
-
This ticket covers a minor but important enhancement to the `Neo.core.Effect` class to support the functional component hook system.
|
272
|
-
|
273
|
-
## 2. Rationale
|
274
|
-
|
275
|
-
To allow hooks like `useConfig` to identify which component is currently rendering, we needed a way to link an active `Effect` back to its owner component. The cleanest, most decoupled way to achieve this was to add an optional `componentId` to the `Effect`'s constructor.
|
276
|
-
|
277
|
-
Since `core.Effect` is a new class introduced in the v10 beta series, adding an optional parameter is a safe, non-breaking change.
|
278
|
-
|
279
|
-
## 3. Scope & Implementation Plan
|
280
|
-
|
281
|
-
1. **Update `Effect` Constructor:**
|
282
|
-
* Modify the `constructor` of `Neo.core.Effect` to accept an optional second parameter, `componentId`.
|
283
|
-
* If `componentId` is provided, store it on a public `this.componentId` property on the effect instance.
|
284
|
-
|
285
|
-
2. **Update `FunctionalBase`:**
|
286
|
-
* In `Neo.functional.component.Base`, update the creation of the `vdomEffect` to pass `this.id` as the second argument to the `Effect` constructor.
|
287
|
-
|
288
|
-
## 4. Definition of Done
|
289
|
-
|
290
|
-
- The `Neo.core.Effect` constructor is updated to accept an optional `componentId`.
|
291
|
-
- `functional.component.Base` correctly passes its ID when creating its `vdomEffect`.
|
292
|
-
- This change enables the `useConfig` hook to reliably get the current component instance.
|
293
|
-
|
294
|
-
## Sub-Ticket: Create Interoperability Layer
|
295
|
-
|
296
|
-
**Status:** To Do
|
297
|
-
|
298
|
-
### 1. Summary
|
299
|
-
|
300
|
-
Design and implement the mechanism that allows functional components to seamlessly host classic components (e.g., `Neo.grid.Container`) and vice-versa. This is critical for ensuring that developers can adopt the new functional paradigm without losing access to the existing library of powerful, classic components.
|
301
|
-
|
302
|
-
### 2. Rationale
|
303
|
-
|
304
|
-
A developer will inevitably need to mix and match component paradigms. For example, they might build the main structure of their application using new functional components but need to include a complex, data-driven component like the grid. Without a robust interoperability layer, this would be impossible, creating a fractured ecosystem.
|
305
|
-
|
306
|
-
The core challenge is that functional components define children declaratively as part of a VDOM tree, while classic components are instantiated via `Neo.create()` and managed within an `items` array. We need to bridge this gap.
|
307
|
-
|
308
|
-
### 3. Technical Challenges & Example
|
309
|
-
|
310
|
-
Consider a `FunctionalBase` component trying to render a classic grid:
|
311
|
-
|
312
|
-
```javascript
|
313
|
-
// Inside a FunctionalBase component...
|
314
|
-
createVdom() {
|
315
|
-
return {
|
316
|
-
tag: 'div',
|
317
|
-
cn: [
|
318
|
-
{tag: 'h1', text: 'My Classic Grid'},
|
319
|
-
{
|
320
|
-
// This is just a VDOM node, not an instance yet
|
321
|
-
module: Neo.grid.Container,
|
322
|
-
store: this.myStore,
|
323
|
-
columns: [...]
|
324
|
-
}
|
325
|
-
]
|
326
|
-
};
|
327
|
-
}
|
328
|
-
```
|
329
|
-
|
330
|
-
To make this work, the system (likely the `VdomLifecycle` mixin) must solve these problems when it processes the VDOM from `createVdom()`:
|
331
|
-
|
332
|
-
1. **Instantiation:** It must detect VDOM nodes that represent component definitions (e.g., via a `module` or `ntype` key) and automatically call `Neo.create()` to turn them into component instances. The logic inside `container.Base#createItem` is a good reference for this.
|
333
|
-
2. **Parent/Child Linking:** Once the classic component instance (the grid) is created, its `parentId` must be set to the `id` of the functional component that is rendering it. The `parent` property should also be correctly linked.
|
334
|
-
3. **Context Propagation:** This parent/child link is essential for context-aware features. The grid instance must be able to find its parent's controller or state provider via `getController()` and `getStateProvider()`.
|
335
|
-
|
336
|
-
### 4. Scope & Implementation Plan
|
337
|
-
|
338
|
-
- Enhance the `VdomLifecycle` mixin (or create a new helper) to traverse VDOM trees before they are sent to the renderer.
|
339
|
-
- This traversal logic will identify and instantiate component definitions.
|
340
|
-
- It will be responsible for setting `parentId` on the new child instances.
|
341
|
-
- Create test cases that verify a functional component can successfully render a classic `container.Base` and that the classic container can find its functional parent's controller.
|
342
|
-
|
343
|
-
### 5. Definition of Done
|
344
|
-
|
345
|
-
- A functional component can successfully render a classic component defined within its `createVdom` method.
|
346
|
-
- The classic component is correctly mounted in the DOM.
|
347
|
-
- The classic component can access its functional parent via `this.parent`.
|
348
|
-
- Context-aware features work across the boundary.
|
349
|
-
|
350
|
-
---
|
351
|
-
|
352
|
-
## Sub-Ticket: Encourage Pure VDOM Effects
|
353
|
-
|
354
|
-
**Status:** To Do
|
355
|
-
|
356
|
-
### 1. Summary
|
357
|
-
|
358
|
-
Define and promote best practices for writing "pure" VDOM-generating methods (e.g., `createVdom()`) within functional components. This ensures that the output of these methods is solely determined by their inputs (component configs) and that they produce no side effects, which is crucial for predictability and enabling future optimizations like memoization.
|
359
|
-
|
360
|
-
### 2. Rationale
|
361
|
-
|
362
|
-
The `Neo.core.Effect` system automatically re-executes VDOM-generating methods when their dependencies changes. For this system to be truly robust and performant, these methods should ideally be pure functions. Purity makes components easier to reason about, test, and debug. It also unlocks significant performance gains through memoization, as the output can be safely cached if inputs remain unchanged.
|
363
|
-
|
364
|
-
### 3. Scope & Implementation Plan
|
365
|
-
|
366
|
-
1. **Define Purity Guidelines:** Clearly document what constitutes a "pure" VDOM-generating method in the context of Neo.mjs functional components. This includes avoiding direct DOM manipulation, external state modification, or reliance on non-reactive global state within `createVdom()`.
|
367
|
-
2. **Documentation:** Add a section to the functional component documentation explaining the concept of pure effects and why it's important.
|
368
|
-
3. **Linting/Static Analysis (Optional, Future):** Explore the possibility of adding linting rules or static analysis checks to identify potential impurities in `createVdom()` methods.
|
369
|
-
|
370
|
-
### 4. Definition of Done
|
371
|
-
|
372
|
-
- Clear guidelines for writing pure VDOM-generating methods are documented.
|
373
|
-
- The documentation explains the benefits of purity and provides examples.
|
374
|
-
|
375
|
-
---
|
376
|
-
|
377
|
-
## Sub-Ticket: Implement Effect Memoization
|
378
|
-
|
379
|
-
**Status:** To Do
|
380
|
-
|
381
|
-
### 1. Summary
|
382
|
-
|
383
|
-
Enhance the `Neo.core.Effect` system (or provide a utility around it) to support memoization for VDOM-generating methods. This will significantly improve rendering performance by caching the VDOM output and preventing unnecessary re-executions when component configs (inputs) have not changed.
|
384
|
-
|
385
|
-
### 2. Rationale
|
386
|
-
|
387
|
-
Functional components, driven by `Neo.core.Effect`, re-generate their VDOM whenever a tracked config changes. While efficient, re-generating complex VDOM trees can still be computationally intensive. By memoizing the output of pure VDOM-generating methods, we can avoid redundant work. If the inputs to `createVdom()` are the same as the last execution, the cached VDOM can be returned directly, bypassing the VDOM generation and worker communication steps.
|
388
|
-
|
389
|
-
### 3. Scope & Implementation Plan
|
390
|
-
|
391
|
-
1. **Memoization Mechanism:** Design and implement a caching layer for `Neo.core.Effect` instances (or a new `MemoizedEffect` class). This mechanism will:
|
392
|
-
* Store the last computed VDOM output.
|
393
|
-
* Efficiently compare current inputs (tracked configs) with previous inputs to determine if re-execution is necessary.
|
394
|
-
* Invalidate the cache when inputs change.
|
395
|
-
2. **Integration:** Determine how developers will opt-in to memoization (e.g., a config on `FunctionalBase`, a decorator, or a utility function).
|
396
|
-
3. **Performance Testing:** Create benchmarks to measure the performance gains achieved through memoization, especially for components with complex VDOM structures or frequently updated but unchanged inputs.
|
397
|
-
|
398
|
-
### 4. Definition of Done
|
399
|
-
|
400
|
-
- A memoization mechanism for `Neo.core.Effect` is implemented.
|
401
|
-
- Functional components can leverage memoization to improve rendering performance.
|
402
|
-
- Performance benchmarks demonstrate measurable gains.
|
403
|
-
|
404
|
-
## Sub-Ticket: Proof of Concept: Beginner Mode Functional Component
|
405
|
-
|
406
|
-
**Status:** To Do
|
407
|
-
|
408
|
-
### 1. Summary
|
409
|
-
|
410
|
-
Create a simple, working example of a "Beginner Mode" functional component using `Neo.functional.defineComponent` and `Neo.functional.useConfig`.
|
411
|
-
|
412
|
-
### 2. Rationale
|
413
|
-
|
414
|
-
This PoC is crucial to validate the end-to-end developer experience for the simplified functional component definition. It will demonstrate that a developer can define a reactive component as a plain function, leveraging hooks for state, and that it renders correctly and updates reactively.
|
415
|
-
|
416
|
-
### 3. Scope & Implementation Plan
|
417
|
-
|
418
|
-
1. **Create a Simple Component:** Define a basic functional component (e.g., a counter or a text display) using the `defineComponent` factory and `useConfig` hook.
|
419
|
-
2. **Render the Component:** Instantiate and render this component within a test environment or a minimal application.
|
420
|
-
3. **Verify Reactivity:** Ensure that changes to the state managed by `useConfig` correctly trigger re-renders of the component.
|
421
|
-
|
422
|
-
### 4. Example Usage
|
423
|
-
|
424
|
-
```javascript
|
425
|
-
// In a component file (e.g., MyCounter.mjs)
|
426
|
-
import { defineComponent } from 'neo/functional/defineComponent.mjs';
|
427
|
-
import { useConfig } from 'neo/functional/useConfig.mjs';
|
428
|
-
|
429
|
-
export default defineComponent(function MyCounter(config) { // The functional component is now the function itself
|
430
|
-
const [count, setCount] = useConfig(0);
|
431
|
-
|
432
|
-
return {
|
433
|
-
tag: 'button',
|
434
|
-
text: `Count: ${count}`,
|
435
|
-
// No listeners property directly in VDOM for beginner mode
|
436
|
-
};
|
437
|
-
});
|
438
|
-
|
439
|
-
// In your app's MainView or a test file
|
440
|
-
// Neo.create(MyCounter, { id: 'my-counter-instance' });
|
441
|
-
```
|
442
|
-
|
443
|
-
### 5. Definition of Done
|
444
|
-
|
445
|
-
- A functional component defined as a plain function using `defineComponent` and `useConfig` is successfully rendered.
|
446
|
-
- The component's state updates reactively, and these updates are reflected in the DOM.
|
447
|
-
|
448
|
-
---
|
449
|
-
|
450
|
-
## Sub-Ticket: DOM Event Handling for Beginner Mode Functional Components
|
451
|
-
|
452
|
-
**Status:** To Do
|
453
|
-
|
454
|
-
### 1. Summary
|
455
|
-
|
456
|
-
Explore and define the idiomatic way for developers to handle DOM events within "Beginner Mode" functional components, ensuring integration with Neo.mjs's powerful, separate DOM event engine.
|
457
|
-
|
458
|
-
### 2. Rationale
|
459
|
-
|
460
|
-
Unlike some other frameworks, Neo.mjs has a dedicated and highly optimized DOM event management system that operates separately from the VDOM. Directly embedding `listeners` within the VDOM (as is common in React) is not the Neo.mjs way and can lead to confusion and suboptimal performance. This ticket aims to define a clear, intuitive, and performant pattern for event handling in the simplified functional component mode.
|
461
|
-
|
462
|
-
### 3. Scope & Implementation Plan
|
463
|
-
|
464
|
-
1. **Research Existing Patterns:** Analyze how Neo.mjs's DOM event engine (`Neo.manager.DomEvent`) is currently used and how it can be exposed in a functional, hook-like manner.
|
465
|
-
2. **Propose API:** Brainstorm and propose a `useEvent` hook or similar API that allows developers to attach event listeners to VDOM nodes declaratively within their `createVdom` function, without directly embedding `listeners` in the VDOM object.
|
466
|
-
3. **Integration:** Ensure the proposed API seamlessly integrates with the underlying `Neo.functional.component.Base` instance and the `Neo.manager.DomEvent`.
|
467
|
-
4. **Documentation:** Provide clear examples and guidelines for using the new event handling mechanism.
|
468
|
-
|
469
|
-
### 4. Example Usage (Conceptual)
|
470
|
-
|
471
|
-
```javascript
|
472
|
-
import { defineComponent } from 'neo/functional/defineComponent.mjs';
|
473
|
-
import { useConfig } from 'neo/functional/useConfig.mjs';
|
474
|
-
import { useEvent } from 'neo/functional/useEvent.mjs'; // New hook
|
475
|
-
|
476
|
-
export default defineComponent(function MyClickableDiv(config) {
|
477
|
-
const [count, setCount] = useConfig(0);
|
478
|
-
|
479
|
-
// Attach a click listener using the new hook
|
480
|
-
useEvent('click', (event) => {
|
481
|
-
setCount(count + 1);
|
482
|
-
console.log('Div clicked!', event);
|
483
|
-
});
|
484
|
-
|
485
|
-
return {
|
486
|
-
tag: 'div',
|
487
|
-
html: `Clicked ${count} times`,
|
488
|
-
// No listeners property directly in VDOM
|
489
|
-
};
|
490
|
-
});
|
491
|
-
```
|
492
|
-
|
493
|
-
### 5. Definition of Done
|
494
|
-
|
495
|
-
- A clear and idiomatic pattern for DOM event handling in "Beginner Mode" functional components is defined.
|
496
|
-
- Necessary hooks/utilities (e.g., `useEvent`) are designed.
|
497
|
-
- Documentation and examples are provided.
|
498
|
-
|
@@ -1,122 +0,0 @@
|
|
1
|
-
# Epic: Optimized VDOM Update Strategies
|
2
|
-
|
3
|
-
This epic outlines a series of surgical enhancements to the framework's VDOM update lifecycle. The goal is to improve the robustness and maintainability of the update mechanism by centralizing complex state for all collision scenarios, without altering the existing high-performance, real-time messaging architecture.
|
4
|
-
|
5
|
-
## Core Problem: Distributed State
|
6
|
-
The current VDOM update logic is extremely fast, but the state management for aggregated updates is complex and distributed across component instances. This is true for two scenarios:
|
7
|
-
1. **Pre-Flight Merges:** Multiple updates are queued in the same event loop tick and merged before a worker message is sent.
|
8
|
-
2. **In-Flight Collisions:** A child requests an update while its parent's update is already in-flight to the worker.
|
9
|
-
|
10
|
-
In both cases, callbacks and post-update triggers are stored in scattered instance-level caches (`resolveUpdateCache`, `childUpdateCache`), making the lifecycle hard to debug and maintain.
|
11
|
-
|
12
|
-
## Solution: Centralized Orchestration Manager
|
13
|
-
|
14
|
-
We will introduce a new manager to act as a central orchestrator for all collision-related state. This approach preserves the existing high-performance update engine while drastically simplifying the `VdomLifecycle` mixin.
|
15
|
-
|
16
|
-
### 1. New Class: `Neo.manager.VDomUpdate` (Orchestrator)
|
17
|
-
- This new singleton manager will **not** schedule or delay updates.
|
18
|
-
- It will contain two maps to manage all collision scenarios:
|
19
|
-
- `mergedCallbackMap`: Stores callbacks and relevant update depth information for **pre-flight merges**.
|
20
|
-
- `postUpdateQueueMap`: Stores child components that need updating after an **in-flight collision**.
|
21
|
-
- It will expose methods to be called by the `VdomLifecycle` mixin:
|
22
|
-
- `registerMerged(ownerId, childId, callbacks, childUpdateDepth, distance)`: Stores the child's callbacks, its `updateDepth`, and its `distance` from the owner.
|
23
|
-
- `registerPostUpdate(ownerId, childId, resolve)`
|
24
|
-
- `executeCallbacks(ownerId)`: This method will also be responsible for calculating the maximum required `updateDepth` for the `ownerId` (parent) based on the `childUpdateDepth` and `distance` of all merged children. It will then set the `ownerId` component's `updateDepth` to this calculated maximum *before* the parent's `update()` method is called (if `needsVdomUpdate` is true).
|
25
|
-
- `triggerPostUpdates(ownerId)`
|
26
|
-
|
27
|
-
### 2. `VdomLifecycle.mjs` Refactoring
|
28
|
-
- The core, high-performance update logic (`updateVdom`, collision detection) will remain.
|
29
|
-
- **Pre-Flight Merge:** When an update is merged (e.g., in `mergeIntoParentUpdate()`), it will now call `Neo.manager.VDomUpdate.registerMerged(parent.id, me.id, me.resolveUpdateCache, me.updateDepth, distance)` instead of manipulating local caches.
|
30
|
-
- **In-Flight Collision:** When an update collides with an in-flight parent (in `isParentUpdating()`), it will call `Neo.manager.VDomUpdate.registerPostUpdate(...)`. The child component will still set its own `needsVdomUpdate = true` and hold its own callback in its `resolveUpdateCache` for the update it will eventually run.
|
31
|
-
- **On Update Completion:** When a root update cycle finishes, its `then()` block will call both `manager.executeCallbacks(this.id)` and `manager.triggerPostUpdates(this.id)`.
|
32
|
-
- This change allows for the complete removal of the complex `childUpdateCache` property and simplifies the logic around `resolveUpdateCache`.
|
33
|
-
|
34
|
-
### 3. Asymmetric VDOM Serialization (`ComponentManager.mjs`)
|
35
|
-
- This part of the plan remains crucial and unchanged.
|
36
|
-
- `getVdomTree()` and `getVnodeTree()` will be refactored to honor the `updateDepth` of each individual component.
|
37
|
-
- If a child component is excluded from an update, a lightweight placeholder object `{componentId: 'neo-ignore'}` will be inserted into the tree to preserve its structural integrity.
|
38
|
-
|
39
|
-
### 4. VDOM Worker Enhancement (`vdom/Helper.mjs`)
|
40
|
-
- The VDOM worker's `createDeltas()` method will be enhanced to recognize the `neo-ignore` placeholder. When it encounters this node, it will skip the diffing process, leaving the corresponding DOM element untouched.
|
41
|
-
|
42
|
-
### 5. Testing Strategy
|
43
|
-
Given the architectural significance of these changes, a comprehensive testing strategy is required. Since the test environment runs in a single thread (main), the strategy will be:
|
44
|
-
|
45
|
-
1. **Unit Test Environment Setup:**
|
46
|
-
- The test suite will import all necessary classes directly into the main thread: `vdom.Helper`, `manager.VDomUpdate`, and all relevant components and containers.
|
47
|
-
- Components will be instantiated in a non-rendering mode (`preventDOM: true` or a similar mechanism) to avoid any interaction with the actual DOM.
|
48
|
-
|
49
|
-
2. **Validation Points:**
|
50
|
-
- **Aggregation Logic:** Create complex scenarios with nested components triggering updates simultaneously to verify that the new `VDomUpdate` manager correctly handles both pre-flight and in-flight collisions.
|
51
|
-
- **Delta Correctness:** After a test update cycle, inspect the generated `deltas` array to ensure it is correct and that the `neo-ignore` placeholder is working as expected.
|
52
|
-
- **VNode Redistribution:** Verify that after an update, the new `vnode` is correctly passed down and synchronized to all relevant child components.
|
53
|
-
- **Callback Execution:** Ensure that all `resolve` callbacks from merged and queued updates are executed correctly and exactly once.
|
54
|
-
|
55
|
-
3. **Benchmarking:**
|
56
|
-
- A dedicated feature branch will be used for this epic.
|
57
|
-
- Create a suite of performance tests that simulate high-frequency updates on complex component trees.
|
58
|
-
- Run these benchmarks on both the `main` branch (old implementation) and the feature branch (new implementation) to rigorously compare performance and ensure there are no regressions.
|
59
|
-
|
60
|
-
This final, refined approach provides the best of all worlds: it fixes the code complexity and maintainability issues by centralizing all collision-related state, while fully preserving the framework's proven, real-time performance characteristics.
|
61
|
-
|
62
|
-
## Implementation Progress
|
63
|
-
|
64
|
-
**Status:** Foundational work complete. Documentation and a regression test suite are in place, and key framework APIs have been enhanced.
|
65
|
-
|
66
|
-
### 1. Documentation & Test Suite
|
67
|
-
|
68
|
-
Two crucial files have been created to guide and validate this epic:
|
69
|
-
|
70
|
-
- **Epic Ticket (`.github/ticket-asymmetric-vdom-updates.md`):** This central document outlines the problem, defines the solution, and tracks our progress.
|
71
|
-
- **`VdomAsymmetricUpdates.mjs`:** A low-level unit test that directly validates the core logic of the new `VDomUpdate` manager and `TreeBuilder`. It simulates asymmetric updates with mock components to ensure the delta generation is correct.
|
72
|
-
- **`VdomRealWorldUpdates.mjs`:** A high-level integration test using real components to create a regression suite. It verifies that the framework's *existing* ability to merge updates from multiple component levels into a single, efficient cycle remains intact.
|
73
|
-
|
74
|
-
Key achievements of these tests:
|
75
|
-
- **Validates Current Logic:** It successfully tests the *existing* logic where updates from multiple component levels (e.g., a parent and a grandchild) within the same event loop tick are correctly merged into a single, efficient VDOM update cycle.
|
76
|
-
- **Pure VDOM Testing:** The test runs without a real DOM. It creates component instances, generates their initial `vnode` structure, and then programmatically triggers updates.
|
77
|
-
- **Asserts on Deltas:** Instead of inspecting the DOM, the test awaits the `promiseUpdate()` method (which was enhanced to resolve with the update data) and directly inspects the raw `deltas` array. This provides a precise and reliable way to verify that the update-merging logic is working as expected.
|
78
|
-
|
79
|
-
This test now serves as a critical regression suite, ensuring that the planned refactoring of the update mechanism will not alter this core, high-performance behavior.
|
80
|
-
|
81
|
-
### 2. Framework API Enhancements
|
82
|
-
|
83
|
-
The process of building the test suite revealed opportunities to improve the core framework. The following enhancements have been implemented:
|
84
|
-
|
85
|
-
- **`Component.mountedPromise`:** A new, robust `mountedPromise` getter has been added to `component.Base`. This provides a clean, reusable, promise-based API for awaiting a component's mounted state. The implementation is resilient to the framework's full lifecycle, correctly handling unmounting and remounting, making it a valuable tool for both testing and application-level code.
|
86
|
-
- **`VdomLifecycle.promiseUpdate()` Enhancement:** The `promiseUpdate()` method was refactored to resolve with the full data object from `vdom.Helper.update()`, giving developers and tests access to the generated `deltas` and the new `vnode`.
|
87
|
-
- **Environment Robustness:** Several fixes were made to the `VdomLifecycle` mixin to ensure it works reliably in both multi-threaded (worker) and single-threaded (test) environments, particularly around `vdom.Helper` calls and `Neo.currentWorker` access.
|
88
|
-
|
89
|
-
- **`VdomLifecycle.promiseUpdate()` Enhancement:** The `promiseUpdate()` method was refactored to resolve with the full data object from `vdom.Helper.update()`, giving developers and tests access to the generated `deltas` and the new `vnode`.
|
90
|
-
- **Environment Robustness:** Several fixes were made to the `VdomLifecycle` mixin to ensure it works reliably in both multi-threaded (worker) and single-threaded (test) environments, particularly around `vdom.Helper` calls and `Neo.currentWorker` access.
|
91
|
-
|
92
|
-
## Comparison with `dev` Branch (based on PR #7077)
|
93
|
-
|
94
|
-
This feature branch represents a major architectural enhancement to the VDOM update lifecycle. Here is a summary of the key changes compared to the `dev` branch:
|
95
|
-
|
96
|
-
- **New Update Orchestration Core:**
|
97
|
-
- `manager.VDomUpdate`: A new singleton manager has been introduced to centralize the state for both pre-flight update merges and in-flight update collisions.
|
98
|
-
- `util.vdom.TreeBuilder`: A new utility class for recursively building VDOM and VNode trees. It is the engine behind creating the *asymmetric* trees needed for optimized updates, correctly expanding component references based on a calculated `updateDepth`.
|
99
|
-
|
100
|
-
- **Core Framework Refactoring:**
|
101
|
-
- `mixin/VdomLifecycle.mjs`: This critical mixin has been significantly refactored. The complex, distributed state management (`childUpdateCache`) has been removed, and it now delegates all collision and merge logic to the new `VDomUpdate` manager.
|
102
|
-
The `executeVdomUpdate()` method has been modernized to use `async/await`, making the control flow more robust and readable, and ensuring deltas are correctly applied in non-worker environments.
|
103
|
-
- `vdom/Helper.mjs`: The diffing engine has been enhanced to support the new asymmetric update strategy.
|
104
|
-
- `component/Base.mjs`: The base component has been improved with a robust `mountedPromise` for easier async handling and other lifecycle enhancements to support the new update model.
|
105
|
-
- `manager/Component.mjs`: Has undergone significant refactoring to align with the new VDOM strategies.
|
106
|
-
- `functional/component/Base.mjs`: The base for functional components has been updated to align with the lifecycle changes in `component.Base`.
|
107
|
-
|
108
|
-
- **New Testing Infrastructure:**
|
109
|
-
- `test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs`: A new low-level test suite that directly validates the logic of `VDomUpdate` and `TreeBuilder` using mock components.
|
110
|
-
- `test/siesta/tests/vdom/VdomRealWorldUpdates.mjs`: A new high-level integration test suite using real, nested components to serve as a regression test.
|
111
|
-
- `src/DefaultConfig.mjs`: The new `allowVdomUpdatesInTests: true` flag has been added to facilitate running VDOM-related tests in a unit test environment.
|
112
|
-
- `test/siesta/siesta.js`: The test runner has been updated to include these new test suites.
|
113
|
-
|
114
|
-
### Remaining Work to Complete the Epic (as of this PR)
|
115
|
-
|
116
|
-
While the core architectural shift is complete, the following tasks remain to finalize the epic:
|
117
|
-
|
118
|
-
- **Finalize Cleanup:**
|
119
|
-
- The `childUpdateCache` property inside `src/component/Base.mjs` is now obsolete. It can be safely removed, as `VDomUpdate` has taken over its responsibilities.
|
120
|
-
- The `updateVdom()` method in `VdomLifecycle.mjs` still uses a `timeout` to handle updates on unmounted components. This can be refactored to use the new `mountedPromise`, creating a cleaner and more robust implementation.
|
121
|
-
- **Complete Asymmetric Logic:** The `TreeBuilder` and `vdom.Helper` still need the final logic to handle the `neo-ignore` placeholder. This will enable truly asymmetric updates where non-participating component sub-trees are completely skipped during the diffing process.
|
122
|
-
- **Performance Benchmarking:** Conduct rigorous performance tests to compare this branch against `dev` and ensure no regressions.
|