lightview 2.3.6 → 2.3.7
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/.gemini/XPATH_IMPLEMENTATION.md +8 -12
- package/1769196538097-package.json +43 -0
- package/build_tmp/lightview-router.js +185 -0
- package/build_tmp/lightview-x.js +1608 -0
- package/build_tmp/lightview.js +932 -0
- package/docs/cdom-nav.html +37 -31
- package/docs/cdom-xpath.html +160 -0
- package/docs/cdom.html +71 -12
- package/jprx/index.js +2 -1
- package/jprx/package.json +1 -1
- package/jprx/parser.js +4 -2
- package/lightview-all.js +70 -58
- package/lightview-cdom.js +72 -55
- package/lightview.js +0 -4
- package/package.json +2 -2
- package/src/lightview-cdom.js +93 -62
- package/src/lightview.js +1 -4
- package/test-xpath-preprocess.js +0 -18
package/lightview-cdom.js
CHANGED
|
@@ -1018,6 +1018,7 @@ var LightviewCDOM = function(exports) {
|
|
|
1018
1018
|
return LV.computed(() => resolveExpression$1(expr, context));
|
|
1019
1019
|
};
|
|
1020
1020
|
const parseCDOMC = (input) => {
|
|
1021
|
+
if (typeof input !== "string") return input;
|
|
1021
1022
|
let i = 0;
|
|
1022
1023
|
const len2 = input.length;
|
|
1023
1024
|
const skipWhitespace = () => {
|
|
@@ -1245,6 +1246,7 @@ var LightviewCDOM = function(exports) {
|
|
|
1245
1246
|
};
|
|
1246
1247
|
const parseJPRX = (input) => {
|
|
1247
1248
|
var _a, _b;
|
|
1249
|
+
if (typeof input !== "string") return input;
|
|
1248
1250
|
let result = "";
|
|
1249
1251
|
let i = 0;
|
|
1250
1252
|
const len2 = input.length;
|
|
@@ -1344,9 +1346,9 @@ var LightviewCDOM = function(exports) {
|
|
|
1344
1346
|
result += JSON.stringify(expr);
|
|
1345
1347
|
continue;
|
|
1346
1348
|
}
|
|
1347
|
-
if (/[a-zA-Z_
|
|
1349
|
+
if (/[a-zA-Z_$\/.\/]/.test(char)) {
|
|
1348
1350
|
let word = "";
|
|
1349
|
-
while (i < len2 && /[a-zA-Z0-9_
|
|
1351
|
+
while (i < len2 && /[a-zA-Z0-9_$\/.-]/.test(input[i])) {
|
|
1350
1352
|
word += input[i];
|
|
1351
1353
|
i++;
|
|
1352
1354
|
}
|
|
@@ -3627,7 +3629,7 @@ var LightviewCDOM = function(exports) {
|
|
|
3627
3629
|
const isCDOM = contentType.includes("application/cdom") || contentType.includes("application/jprx") || contentType.includes("application/vdom") || contentType.includes("application/odom") || url.endsWith(".cdom") || url.endsWith(".jprx") || url.endsWith(".vdom") || url.endsWith(".odom");
|
|
3628
3630
|
if (isCDOM || contentType.includes("application/json") && text.trim().startsWith("{")) {
|
|
3629
3631
|
try {
|
|
3630
|
-
content = hydrate(
|
|
3632
|
+
content = hydrate(parseCDOMC(text));
|
|
3631
3633
|
} catch (e) {
|
|
3632
3634
|
}
|
|
3633
3635
|
}
|
|
@@ -3803,77 +3805,89 @@ var LightviewCDOM = function(exports) {
|
|
|
3803
3805
|
node[key] = hydrate(value, node);
|
|
3804
3806
|
}
|
|
3805
3807
|
}
|
|
3808
|
+
if (!parent && node.tag) {
|
|
3809
|
+
node.attributes = node.attributes || {};
|
|
3810
|
+
const originalOnMount = node.attributes.onmount;
|
|
3811
|
+
node.attributes.onmount = (el) => {
|
|
3812
|
+
if (typeof originalOnMount === "function") originalOnMount(el);
|
|
3813
|
+
resolveStaticXPath(el);
|
|
3814
|
+
};
|
|
3815
|
+
}
|
|
3806
3816
|
return node;
|
|
3807
3817
|
};
|
|
3808
3818
|
const validateXPath = (xpath) => {
|
|
3819
|
+
if (!xpath) return;
|
|
3809
3820
|
const forbiddenAxes = /\b(child|descendant|following|following-sibling)::/;
|
|
3810
3821
|
if (forbiddenAxes.test(xpath)) {
|
|
3811
3822
|
throw new Error(`XPath: Forward-looking axes not allowed during DOM construction: ${xpath}`);
|
|
3812
3823
|
}
|
|
3813
|
-
const hasShorthandChild = /\/[a-zA-Z]/.test(xpath) && !xpath.startsWith("/html");
|
|
3824
|
+
const hasShorthandChild = /\/(?![@.])(?![a-zA-Z0-9_-]+::)[a-zA-Z]/.test(xpath) && !xpath.startsWith("/html");
|
|
3814
3825
|
if (hasShorthandChild) {
|
|
3815
3826
|
throw new Error(`XPath: Shorthand child axis (/) not allowed during DOM construction: ${xpath}`);
|
|
3816
3827
|
}
|
|
3817
3828
|
};
|
|
3818
|
-
const
|
|
3819
|
-
var _a
|
|
3820
|
-
|
|
3821
|
-
const
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
);
|
|
3825
|
-
const nodesToProcess = [];
|
|
3826
|
-
let node = walker.nextNode();
|
|
3827
|
-
while (node) {
|
|
3828
|
-
nodesToProcess.push(node);
|
|
3829
|
-
node = walker.nextNode();
|
|
3830
|
-
}
|
|
3831
|
-
for (const node2 of nodesToProcess) {
|
|
3832
|
-
if (node2.nodeType === Node.ELEMENT_NODE) {
|
|
3833
|
-
const attributes = [...node2.attributes];
|
|
3834
|
-
for (const attr of attributes) {
|
|
3835
|
-
if (attr.name.startsWith("data-xpath-")) {
|
|
3836
|
-
const realAttr = attr.name.replace("data-xpath-", "");
|
|
3837
|
-
const xpath = attr.value;
|
|
3838
|
-
try {
|
|
3839
|
-
validateXPath(xpath);
|
|
3840
|
-
const result = document.evaluate(
|
|
3841
|
-
xpath,
|
|
3842
|
-
node2,
|
|
3843
|
-
null,
|
|
3844
|
-
XPathResult.STRING_TYPE,
|
|
3845
|
-
null
|
|
3846
|
-
);
|
|
3847
|
-
node2.setAttribute(realAttr, result.stringValue);
|
|
3848
|
-
node2.removeAttribute(attr.name);
|
|
3849
|
-
} catch (e) {
|
|
3850
|
-
(_a = globalThis.console) == null ? void 0 : _a.error(`[Lightview-CDOM] XPath resolution failed for attribute "${realAttr}":`, e.message);
|
|
3851
|
-
}
|
|
3852
|
-
}
|
|
3853
|
-
}
|
|
3854
|
-
}
|
|
3855
|
-
if (node2.__xpathExpr) {
|
|
3856
|
-
const xpath = node2.__xpathExpr;
|
|
3829
|
+
const resolveAttributeXPaths = (el) => {
|
|
3830
|
+
var _a;
|
|
3831
|
+
const attributes = [...el.attributes];
|
|
3832
|
+
for (const attr of attributes) {
|
|
3833
|
+
if (attr.name.startsWith("data-xpath-")) {
|
|
3834
|
+
const realAttr = attr.name.replace("data-xpath-", "");
|
|
3857
3835
|
try {
|
|
3858
|
-
validateXPath(
|
|
3859
|
-
const
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3836
|
+
validateXPath(attr.value);
|
|
3837
|
+
const doc = globalThis.document || el.ownerDocument;
|
|
3838
|
+
const result = doc.evaluate(
|
|
3839
|
+
attr.value,
|
|
3840
|
+
el,
|
|
3863
3841
|
null,
|
|
3864
3842
|
XPathResult.STRING_TYPE,
|
|
3865
3843
|
null
|
|
3866
3844
|
);
|
|
3867
|
-
|
|
3868
|
-
|
|
3845
|
+
el.setAttribute(realAttr, result.stringValue);
|
|
3846
|
+
el.removeAttribute(attr.name);
|
|
3869
3847
|
} catch (e) {
|
|
3870
|
-
(
|
|
3848
|
+
(_a = globalThis.console) == null ? void 0 : _a.error(`[Lightview-CDOM] XPath attribute error ("${realAttr}") at <${el.tagName.toLowerCase()} id="${el.id}">:`, e.message);
|
|
3871
3849
|
}
|
|
3872
3850
|
}
|
|
3873
3851
|
}
|
|
3874
3852
|
};
|
|
3853
|
+
const resolveTextNodeXPath = (node) => {
|
|
3854
|
+
var _a, _b, _c;
|
|
3855
|
+
if (!node.__xpathExpr) return;
|
|
3856
|
+
const xpath = node.__xpathExpr;
|
|
3857
|
+
try {
|
|
3858
|
+
validateXPath(xpath);
|
|
3859
|
+
const doc = globalThis.document || node.ownerDocument;
|
|
3860
|
+
const contextNode = node.parentNode || node;
|
|
3861
|
+
const result = doc.evaluate(
|
|
3862
|
+
xpath,
|
|
3863
|
+
contextNode,
|
|
3864
|
+
null,
|
|
3865
|
+
XPathResult.STRING_TYPE,
|
|
3866
|
+
null
|
|
3867
|
+
);
|
|
3868
|
+
node.textContent = result.stringValue;
|
|
3869
|
+
} catch (e) {
|
|
3870
|
+
(_c = globalThis.console) == null ? void 0 : _c.error(`[Lightview-CDOM] XPath text node error on <${(_a = node.parentNode) == null ? void 0 : _a.tagName.toLowerCase()} id="${(_b = node.parentNode) == null ? void 0 : _b.id}">:`, e.message);
|
|
3871
|
+
} finally {
|
|
3872
|
+
delete node.__xpathExpr;
|
|
3873
|
+
}
|
|
3874
|
+
};
|
|
3875
|
+
const resolveStaticXPath = (rootNode) => {
|
|
3876
|
+
const node = rootNode instanceof Node ? rootNode : (rootNode == null ? void 0 : rootNode.domEl) || rootNode;
|
|
3877
|
+
if (!node || !node.nodeType) return;
|
|
3878
|
+
if (node.nodeType === Node.ELEMENT_NODE) resolveAttributeXPaths(node);
|
|
3879
|
+
resolveTextNodeXPath(node);
|
|
3880
|
+
const doc = globalThis.document || node.ownerDocument;
|
|
3881
|
+
const walker = doc.createTreeWalker(node, NodeFilter.SHOW_ALL);
|
|
3882
|
+
let current = walker.nextNode();
|
|
3883
|
+
while (current) {
|
|
3884
|
+
if (current.nodeType === Node.ELEMENT_NODE) resolveAttributeXPaths(current);
|
|
3885
|
+
resolveTextNodeXPath(current);
|
|
3886
|
+
current = walker.nextNode();
|
|
3887
|
+
}
|
|
3888
|
+
};
|
|
3875
3889
|
if (typeof parseCDOMC !== "function") throw new Error("parseCDOMC not found");
|
|
3876
|
-
if (typeof parseJPRX !== "function") throw new Error("
|
|
3890
|
+
if (typeof parseJPRX !== "function") throw new Error("oldParseJPRX not found");
|
|
3877
3891
|
const LightviewCDOM2 = {
|
|
3878
3892
|
registerHelper,
|
|
3879
3893
|
registerOperator,
|
|
@@ -3882,7 +3896,9 @@ var LightviewCDOM = function(exports) {
|
|
|
3882
3896
|
resolvePathAsContext,
|
|
3883
3897
|
resolveExpression: resolveExpression$1,
|
|
3884
3898
|
parseCDOMC,
|
|
3885
|
-
parseJPRX,
|
|
3899
|
+
parseJPRX: parseCDOMC,
|
|
3900
|
+
// Alias parseJPRX to the more robust parseCDOMC
|
|
3901
|
+
oldParseJPRX: parseJPRX,
|
|
3886
3902
|
unwrapSignal,
|
|
3887
3903
|
getContext,
|
|
3888
3904
|
handleCDOMState: () => {
|
|
@@ -3892,7 +3908,7 @@ var LightviewCDOM = function(exports) {
|
|
|
3892
3908
|
activate,
|
|
3893
3909
|
hydrate,
|
|
3894
3910
|
resolveStaticXPath,
|
|
3895
|
-
version: "1.
|
|
3911
|
+
version: "1.1.0"
|
|
3896
3912
|
};
|
|
3897
3913
|
if (typeof window !== "undefined") {
|
|
3898
3914
|
globalThis.LightviewCDOM = {};
|
|
@@ -3903,9 +3919,10 @@ var LightviewCDOM = function(exports) {
|
|
|
3903
3919
|
exports.default = LightviewCDOM2;
|
|
3904
3920
|
exports.getContext = getContext;
|
|
3905
3921
|
exports.hydrate = hydrate;
|
|
3922
|
+
exports.oldParseJPRX = parseJPRX;
|
|
3906
3923
|
exports.parseCDOMC = parseCDOMC;
|
|
3907
3924
|
exports.parseExpression = parseExpression;
|
|
3908
|
-
exports.parseJPRX =
|
|
3925
|
+
exports.parseJPRX = parseCDOMC;
|
|
3909
3926
|
exports.registerHelper = registerHelper;
|
|
3910
3927
|
exports.registerOperator = registerOperator;
|
|
3911
3928
|
exports.resolveExpression = resolveExpression$1;
|
package/lightview.js
CHANGED
|
@@ -644,7 +644,6 @@
|
|
|
644
644
|
return reactiveAttrs;
|
|
645
645
|
};
|
|
646
646
|
const processChildren = (children, targetNode, clearExisting = true) => {
|
|
647
|
-
var _a;
|
|
648
647
|
if (clearExisting && targetNode.innerHTML !== void 0) {
|
|
649
648
|
targetNode.innerHTML = "";
|
|
650
649
|
}
|
|
@@ -717,9 +716,6 @@
|
|
|
717
716
|
childElements.push(childEl);
|
|
718
717
|
}
|
|
719
718
|
}
|
|
720
|
-
if (typeof ((_a = globalThis.LightviewCDOM) == null ? void 0 : _a.resolveStaticXPath) === "function") {
|
|
721
|
-
globalThis.LightviewCDOM.resolveStaticXPath(targetNode);
|
|
722
|
-
}
|
|
723
719
|
return childElements;
|
|
724
720
|
};
|
|
725
721
|
const setupChildrenInTarget = (children, targetNode) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lightview",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.7",
|
|
4
4
|
"description": "A lightweight reactive UI library with features of Bau, Juris, and HTMX",
|
|
5
5
|
"main": "lightview.js",
|
|
6
6
|
"workspaces": [
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"expr-eval": "^2.0.2",
|
|
39
|
-
"jprx": "^1.3.
|
|
39
|
+
"jprx": "^1.3.1",
|
|
40
40
|
"linkedom": "^0.18.12",
|
|
41
41
|
"marked": "^17.0.1"
|
|
42
42
|
}
|
package/src/lightview-cdom.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* The Reactive Path and Expression Engine for Lightview.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { registerHelper, registerOperator, parseExpression, resolvePath, resolvePathAsContext, resolveExpression, parseCDOMC, parseJPRX, unwrapSignal, BindingTarget } from '../jprx/parser.js';
|
|
6
|
+
import { registerHelper, registerOperator, parseExpression, resolvePath, resolvePathAsContext, resolveExpression, parseCDOMC, parseJPRX as oldParseJPRX, unwrapSignal, BindingTarget } from '../jprx/parser.js';
|
|
7
7
|
import { registerMathHelpers } from '../jprx/helpers/math.js';
|
|
8
8
|
import { registerLogicHelpers } from '../jprx/helpers/logic.js';
|
|
9
9
|
import { registerStringHelpers } from '../jprx/helpers/string.js';
|
|
@@ -111,7 +111,7 @@ registerHelper('mount', async (url, options = {}) => {
|
|
|
111
111
|
|
|
112
112
|
if (isCDOM || (contentType.includes('application/json') && text.trim().startsWith('{'))) {
|
|
113
113
|
try {
|
|
114
|
-
content = hydrate(
|
|
114
|
+
content = hydrate(parseCDOMC(text));
|
|
115
115
|
} catch (e) {
|
|
116
116
|
// Fail gracefully to text
|
|
117
117
|
}
|
|
@@ -352,6 +352,18 @@ const hydrate = (node, parent = null) => {
|
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
+
// 4. Automatic XPath Resolution
|
|
356
|
+
// If this is a top-level hydrated element, ensure it resolves its static XPaths on mount.
|
|
357
|
+
// We add it to node.attributes so Lightview's element() factory picks it up.
|
|
358
|
+
if (!parent && node.tag) {
|
|
359
|
+
node.attributes = node.attributes || {};
|
|
360
|
+
const originalOnMount = node.attributes.onmount;
|
|
361
|
+
node.attributes.onmount = (el) => {
|
|
362
|
+
if (typeof originalOnMount === 'function') originalOnMount(el);
|
|
363
|
+
resolveStaticXPath(el);
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
355
367
|
return node;
|
|
356
368
|
};
|
|
357
369
|
|
|
@@ -361,15 +373,18 @@ const hydrate = (node, parent = null) => {
|
|
|
361
373
|
* @param {string} xpath - The XPath expression to validate
|
|
362
374
|
*/
|
|
363
375
|
const validateXPath = (xpath) => {
|
|
376
|
+
if (!xpath) return;
|
|
377
|
+
|
|
364
378
|
// Check for forbidden forward-looking axes
|
|
365
379
|
const forbiddenAxes = /\b(child|descendant|following|following-sibling)::/;
|
|
366
380
|
if (forbiddenAxes.test(xpath)) {
|
|
367
381
|
throw new Error(`XPath: Forward-looking axes not allowed during DOM construction: ${xpath}`);
|
|
368
382
|
}
|
|
369
383
|
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
|
|
384
|
+
// Check for shorthand forward references like /div (implies child axis)
|
|
385
|
+
// We allow '/' if it's followed by '@' (attribute), '.' (parent/self shorthand),
|
|
386
|
+
// or is the start of the document root '/html'.
|
|
387
|
+
const hasShorthandChild = /\/(?![@.])(?![a-zA-Z0-9_-]+::)[a-zA-Z]/.test(xpath) && !xpath.startsWith('/html');
|
|
373
388
|
if (hasShorthandChild) {
|
|
374
389
|
throw new Error(`XPath: Shorthand child axis (/) not allowed during DOM construction: ${xpath}`);
|
|
375
390
|
}
|
|
@@ -381,76 +396,90 @@ const validateXPath = (xpath) => {
|
|
|
381
396
|
* Walks the tree and resolves all __xpath__ markers.
|
|
382
397
|
* @param {Node} rootNode - The root DOM node to start walking from
|
|
383
398
|
*/
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const nodesToProcess = [];
|
|
393
|
-
let node = walker.nextNode();
|
|
394
|
-
while (node) {
|
|
395
|
-
nodesToProcess.push(node);
|
|
396
|
-
node = walker.nextNode();
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Process all nodes
|
|
400
|
-
for (const node of nodesToProcess) {
|
|
401
|
-
// Check for XPath markers in attributes
|
|
402
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
403
|
-
const attributes = [...node.attributes];
|
|
404
|
-
for (const attr of attributes) {
|
|
405
|
-
if (attr.name.startsWith('data-xpath-')) {
|
|
406
|
-
const realAttr = attr.name.replace('data-xpath-', '');
|
|
407
|
-
const xpath = attr.value;
|
|
408
|
-
|
|
409
|
-
try {
|
|
410
|
-
validateXPath(xpath);
|
|
411
|
-
const result = document.evaluate(
|
|
412
|
-
xpath,
|
|
413
|
-
node,
|
|
414
|
-
null,
|
|
415
|
-
XPathResult.STRING_TYPE,
|
|
416
|
-
null
|
|
417
|
-
);
|
|
418
|
-
node.setAttribute(realAttr, result.stringValue);
|
|
419
|
-
node.removeAttribute(attr.name);
|
|
420
|
-
} catch (e) {
|
|
421
|
-
globalThis.console?.error(`[Lightview-CDOM] XPath resolution failed for attribute "${realAttr}":`, e.message);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Check for XPath markers in text nodes
|
|
428
|
-
if (node.__xpathExpr) {
|
|
429
|
-
const xpath = node.__xpathExpr;
|
|
399
|
+
/**
|
|
400
|
+
* Resolves static XPath markers for attributes on a specific element.
|
|
401
|
+
*/
|
|
402
|
+
const resolveAttributeXPaths = (el) => {
|
|
403
|
+
const attributes = [...el.attributes];
|
|
404
|
+
for (const attr of attributes) {
|
|
405
|
+
if (attr.name.startsWith('data-xpath-')) {
|
|
406
|
+
const realAttr = attr.name.replace('data-xpath-', '');
|
|
430
407
|
try {
|
|
431
|
-
validateXPath(
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
408
|
+
validateXPath(attr.value);
|
|
409
|
+
const doc = globalThis.document || el.ownerDocument;
|
|
410
|
+
const result = doc.evaluate(
|
|
411
|
+
attr.value,
|
|
412
|
+
el,
|
|
435
413
|
null,
|
|
436
414
|
XPathResult.STRING_TYPE,
|
|
437
415
|
null
|
|
438
416
|
);
|
|
439
|
-
|
|
440
|
-
|
|
417
|
+
el.setAttribute(realAttr, result.stringValue);
|
|
418
|
+
el.removeAttribute(attr.name);
|
|
441
419
|
} catch (e) {
|
|
442
|
-
globalThis.console?.error(`[Lightview-CDOM] XPath
|
|
420
|
+
globalThis.console?.error(`[Lightview-CDOM] XPath attribute error ("${realAttr}") at <${el.tagName.toLowerCase()} id="${el.id}">:`, e.message);
|
|
443
421
|
}
|
|
444
422
|
}
|
|
445
423
|
}
|
|
446
424
|
};
|
|
447
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Resolves static XPath markers for a text node.
|
|
428
|
+
*/
|
|
429
|
+
const resolveTextNodeXPath = (node) => {
|
|
430
|
+
if (!node.__xpathExpr) return;
|
|
431
|
+
const xpath = node.__xpathExpr;
|
|
432
|
+
try {
|
|
433
|
+
validateXPath(xpath);
|
|
434
|
+
const doc = globalThis.document || node.ownerDocument;
|
|
435
|
+
// Use the parent node (the element) as the context for evaluation
|
|
436
|
+
// This avoids errors in browsers that don't support Text nodes as context nodes
|
|
437
|
+
// and keeps evaluation consistent with attributes.
|
|
438
|
+
const contextNode = node.parentNode || node;
|
|
439
|
+
const result = doc.evaluate(
|
|
440
|
+
xpath,
|
|
441
|
+
contextNode,
|
|
442
|
+
null,
|
|
443
|
+
XPathResult.STRING_TYPE,
|
|
444
|
+
null
|
|
445
|
+
);
|
|
446
|
+
node.textContent = result.stringValue;
|
|
447
|
+
} catch (e) {
|
|
448
|
+
globalThis.console?.error(`[Lightview-CDOM] XPath text node error on <${node.parentNode?.tagName.toLowerCase()} id="${node.parentNode?.id}">:`, e.message);
|
|
449
|
+
} finally {
|
|
450
|
+
delete node.__xpathExpr;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Walks the tree and resolves all __xpath__ markers.
|
|
456
|
+
* @param {Node} rootNode - The root DOM node to start walking from
|
|
457
|
+
*/
|
|
458
|
+
const resolveStaticXPath = (rootNode) => {
|
|
459
|
+
const node = rootNode instanceof Node ? rootNode : (rootNode?.domEl || rootNode);
|
|
460
|
+
if (!node || !node.nodeType) return;
|
|
461
|
+
|
|
462
|
+
// Process the root node itself
|
|
463
|
+
if (node.nodeType === Node.ELEMENT_NODE) resolveAttributeXPaths(node);
|
|
464
|
+
resolveTextNodeXPath(node);
|
|
465
|
+
|
|
466
|
+
// Process all descendants
|
|
467
|
+
const doc = globalThis.document || node.ownerDocument;
|
|
468
|
+
const walker = doc.createTreeWalker(node, NodeFilter.SHOW_ALL);
|
|
469
|
+
let current = walker.nextNode();
|
|
470
|
+
while (current) {
|
|
471
|
+
if (current.nodeType === Node.ELEMENT_NODE) resolveAttributeXPaths(current);
|
|
472
|
+
resolveTextNodeXPath(current);
|
|
473
|
+
current = walker.nextNode();
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
448
477
|
|
|
449
478
|
// Prevent tree-shaking of parser functions by creating a side-effect
|
|
450
479
|
// These are used externally by lightview-x.js for .cdomc file loading
|
|
451
480
|
// The typeof check creates a runtime branch the bundler can't eliminate
|
|
452
481
|
if (typeof parseCDOMC !== 'function') throw new Error('parseCDOMC not found');
|
|
453
|
-
if (typeof
|
|
482
|
+
if (typeof oldParseJPRX !== 'function') throw new Error('oldParseJPRX not found');
|
|
454
483
|
|
|
455
484
|
const LightviewCDOM = {
|
|
456
485
|
registerHelper,
|
|
@@ -460,7 +489,8 @@ const LightviewCDOM = {
|
|
|
460
489
|
resolvePathAsContext,
|
|
461
490
|
resolveExpression,
|
|
462
491
|
parseCDOMC,
|
|
463
|
-
parseJPRX,
|
|
492
|
+
parseJPRX: parseCDOMC, // Alias parseJPRX to the more robust parseCDOMC
|
|
493
|
+
oldParseJPRX,
|
|
464
494
|
unwrapSignal,
|
|
465
495
|
getContext,
|
|
466
496
|
handleCDOMState: () => { },
|
|
@@ -468,7 +498,7 @@ const LightviewCDOM = {
|
|
|
468
498
|
activate,
|
|
469
499
|
hydrate,
|
|
470
500
|
resolveStaticXPath,
|
|
471
|
-
version: '1.
|
|
501
|
+
version: '1.1.0'
|
|
472
502
|
};
|
|
473
503
|
|
|
474
504
|
// Global export for non-module usage
|
|
@@ -486,7 +516,8 @@ export {
|
|
|
486
516
|
resolvePathAsContext,
|
|
487
517
|
resolveExpression,
|
|
488
518
|
parseCDOMC,
|
|
489
|
-
parseJPRX,
|
|
519
|
+
parseCDOMC as parseJPRX,
|
|
520
|
+
oldParseJPRX,
|
|
490
521
|
unwrapSignal,
|
|
491
522
|
BindingTarget,
|
|
492
523
|
getContext,
|
package/src/lightview.js
CHANGED
|
@@ -520,10 +520,7 @@ const processChildren = (children, targetNode, clearExisting = true) => {
|
|
|
520
520
|
}
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
-
|
|
524
|
-
if (typeof globalThis.LightviewCDOM?.resolveStaticXPath === 'function') {
|
|
525
|
-
globalThis.LightviewCDOM.resolveStaticXPath(targetNode);
|
|
526
|
-
}
|
|
523
|
+
|
|
527
524
|
|
|
528
525
|
return childElements;
|
|
529
526
|
};
|
package/test-xpath-preprocess.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// Test preprocessXPath function
|
|
2
|
-
import { parseCDOMC } from './jprx/parser.js';
|
|
3
|
-
|
|
4
|
-
const testInput = `{
|
|
5
|
-
button: {
|
|
6
|
-
id: "7",
|
|
7
|
-
children: [#../@id]
|
|
8
|
-
}
|
|
9
|
-
}`;
|
|
10
|
-
|
|
11
|
-
console.log('Test input:', testInput);
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const result = parseCDOMC(testInput);
|
|
15
|
-
console.log('Parsed successfully:', result);
|
|
16
|
-
} catch (e) {
|
|
17
|
-
console.error('Parse failed:', e.message);
|
|
18
|
-
}
|