lightview
Version:
A reactive UI library with features of Bau, Juris, and HTMX plus safe LLM UI generation
187 lines (147 loc) • 6.89 kB
Markdown
# XPath Integration for cDOM - Implementation Summary
## Overview
XPath support has been added to cDOM, allowing developers to navigate the DOM structure during element definition. This enables referencing parent/ancestor attributes without duplication.
## Features Implemented
### 1. Static XPath (`#` prefix)
- **Syntax:** `#xpath-expression`
- **Evaluation:** Once during DOM construction (two-pass approach)
- **Reactivity:** None - becomes a static string value
- **Escape Sequence:** `'#` produces literal `#` string
children: ["#@aria-label"]
}
```
**Note on Text Nodes:** Even though the XPath refers to a child of the element, it is evaluated relative to the element itself (`self::*`). This improves consistency between attributes and children, and avoids "Operation is not supported" errors in browsers when using a Text node as an XPath context.
### 2. Reactive XPath (`=xpath()` helper)
- **Syntax:** `=xpath('expression')`
- **Evaluation:** As JPRX helper, returns computed signal
- **Reactivity:** Re-evaluates when observed DOM changes (TODO: full MutationObserver)
- **Composable:** Can mix with JPRX expressions
**Example:**
```javascript
{
attributes: {
title: "=xpath('../@id')",
class: "=concat(xpath('../@disabled'), ' ', $/theme)"
}
}
```
## Allowed XPath Axes (Backward-Only)
✅ `self::` or `.` - current node
✅ `parent::` or `..` - parent node
✅ `ancestor::` - all ancestors
✅ `ancestor-or-self::`
✅ `preceding-sibling::` - earlier siblings
✅ `preceding::` - all preceding nodes
❌ `child::` - forward reference (not allowed)
❌ `descendant::` - forward reference (not allowed)
❌ `following::` - forward reference (not allowed)
❌ `following-sibling::` - forward reference (not allowed)
## Implementation Details
### Files Modified
1. **`jprx/parser.js`**
- Added `preprocessXPath()` function to quote unquoted `#xpath` expressions in cDOMC
- Handles escape sequences (`'#`)
- Skips quoted strings and comments
2. **`src/lightview-cdom.js`**
- Updated `hydrate()` to detect and mark `#xpath` expressions
- Added `validateXPath()` to ensure no forward-looking axes
- Added `resolveStaticXPath()` for two-pass XPath resolution
- Registered `registerDOMHelpers` for reactive xpath() helper
- Exported `resolveStaticXPath` for global access
3. **`src/lightview.js`**
- Updated `makeReactiveAttributes()` to handle `__xpath__` markers from hydration
- Updated `processChildren()` to handle XPath markers in children arrays
- Added call to `resolveStaticXPath()` after DOM tree construction
4. **`jprx/helpers/dom.js`** (NEW)
- Created reactive `xpath()` helper function
- Returns computed signal for XPath evaluation
- Validates against forward-looking axes
- TODO: Add Mutation Observer for full reactivity
5. **`jprx/index.js`**
- Exported `registerDOMHelpers`
### Two-Pass Resolution Flow
**Pass 1: Build DOM Tree**
1. `parseCDOMC()` preprocesses input, quoting unquoted `#xpath` expressions
2. `hydrate()` detects `#xpath` strings, marks with `{ __xpath__: 'expr', __static__: true }`
3. `element()` creates DOM nodes
4. `makeReactiveAttributes()` sets `data-xpath-{attrname}` markers on nodes
5. `processChildren()` creates text nodes with `__xpathExpr` property
6. Tree is fully constructed via `appendChild()`
**Pass 2: Resolve XPath**
7. `resolveStaticXPath()` called after tree construction
8. Walks tree with `TreeWalker`
9. Finds nodes with `data-xpath-*` attributes or `__xpathExpr` properties
10. Validates XPath expressions (no forward-looking)
11. Calls browser's `document.evaluate()` with node as context
12. Sets resolved values and cleans up markers
## Usage Examples
### cDOMC (Unquoted)
```javascript
{
tag: div,
attributes: {
id: parent-div,
data-theme: dark
},
children: [{
tag: button,
attributes: {
data-parent-id: #../@id,
class: "=concat(xpath('../@data-theme'), '-button')"
},
children: [#@aria-label]
}]
}
```
### JSON
```javascript
{
"tag": "div",
"attributes": {
"id": "parent-div",
"data-theme": "dark"
},
"children": [{
"tag": "button",
"attributes": {
"data-parent-id": "#../@id",
"class": "=concat(xpath('../@data-theme'), '-button')"
},
"children": ["#@aria-label"]
}]
}
```
## Testing
Test file created: `test-xpath.html`
Open in browser to verify:
- Static XPath in attributes
- Parent attribute access
- Escape sequences
## Key Features
1. **Unquoted XPath in cDOMC**: Fully supported through structural integration with the parser character loop.
- ✅ Working: `children: [#@id]`
- ✅ Working: `children: [#div[@id='1']]` (Complex paths with brackets now work!)
2. **Reactive xpath() helper**: Currently evaluates once via computed signal. Full reactivity with MutationObserver is TODO.
3. **XPath complexity**: Works with paths like `../@id`, `../../@attr`, and predicate-based paths.
4. **Consistency**: Both attributes and children evaluate XPath relative to the Element node. This avoids browser issues with Text node context and simplifies the developer's mental model.
## Implementation: Structural Parser Integration
Instead of a string preprocessor hack, XPath support is now integrated directly into the `parseCDOMC` word parser.
- When `parseWord` encounters `#`, it enters a permissive "expression mode" (similar to `=` JPRX).
- In this mode, it tracks nesting depth for `[]`, `{}`, and `()`.
- It handles internal quotes within the XPath safely.
- It only terminates the unquoted word when nesting depth is `0` and it encounters a structural cDOMC delimiter (e.g., `]`, `}`, or `,`).
## Bug Fixes Applied
1. **Structural Delimiters**: Fixed the issue where unquoted XPath would consume the closing bracket of its container.
2. **Complex Paths**: Enabled support for square brackets and other structural characters within the XPath itself.
3. **Text node context**: Fixed XPath evaluation to use the text node itself as context, not its parent element.
## Next Steps
1. Add MutationObserver to reactive xpath() helper for full reactivity
2. Add comprehensive unit tests for both static and reactive XPath
3. Document XPath support in cDOM documentation
4. Add more examples showing different XPath axes
5. Performance optimization for large DOMs
## Lint Issues
Note: The following lint warnings exist but don't affect functionality:
- **File length**: `/mnt/c/Users/Owner/AntigravityProjects/lightview/jprx/parser.js` has 1552 lines (limit 500)
- This is a large parser file; could be split/refactored in the future
- **Escape characters**: Some unnecessary escapes in regex (cosmetic issue)