solarite 0.5.2 → 0.7.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Solarite-debug.js +4847 -3878
  3. package/dist/Solarite.js +4583 -3626
  4. package/dist/Solarite.min.js +2 -4
  5. package/package.json +19 -6
  6. package/readme.md +58 -11
  7. package/src/Globals.js +54 -72
  8. package/src/HtmlParser.js +90 -90
  9. package/src/MultiValueMap.js +57 -105
  10. package/src/NodeGroup.js +624 -470
  11. package/src/Path.js +224 -211
  12. package/src/PathToAttribValue.js +401 -261
  13. package/src/PathToAttribs.js +113 -80
  14. package/src/PathToComment.js +7 -7
  15. package/src/PathToComponent.js +183 -188
  16. package/src/PathToEvent.js +76 -64
  17. package/src/PathToKey.js +19 -0
  18. package/src/PathToNodes.js +1053 -566
  19. package/src/RootNodeGroup.js +120 -8
  20. package/src/Shell.js +570 -348
  21. package/src/Solarite.d.ts +134 -113
  22. package/src/Solarite.js +243 -286
  23. package/src/Template.js +195 -274
  24. package/src/Util.js +353 -351
  25. package/src/assert.js +10 -10
  26. package/src/assignAttributes.js +63 -0
  27. package/src/delve.js +55 -43
  28. package/src/h.js +220 -138
  29. package/src/jsx-dev-runtime.d.ts +1 -0
  30. package/src/jsx-dev-runtime.js +5 -0
  31. package/src/jsx-runtime.d.ts +21 -0
  32. package/src/jsx-runtime.js +84 -0
  33. package/src/jsx.js +194 -0
  34. package/src/toEl.js +77 -82
  35. package/dist/udomdiff-license.txt +0 -18
  36. package/src/getArg.js +0 -137
  37. package/src/hash.js +0 -89
  38. package/src/udomdiff.js +0 -176
  39. package/src/unused/FastLookupArray.js +0 -54
  40. package/src/unused/Hashes.js +0 -339
  41. package/src/unused/InUse.test.js +0 -92
  42. package/src/unused/InUseMap.js +0 -98
  43. package/src/unused/LinkedList.js +0 -117
  44. package/src/unused/LinkedList.test.js +0 -115
  45. package/src/unused/Misc.js +0 -13
  46. package/src/unused/Perf.js +0 -47
  47. package/src/unused/TrackedArray.js +0 -54
  48. package/src/unused/WeakArray.js +0 -33
  49. package/src/watch.js +0 -543
@@ -1,81 +1,114 @@
1
- import Path from "./Path.js";
2
- import Globals from "./Globals.js";
3
- import assert from "./assert.js";
4
-
5
- export default class PathToAttribs extends Path {
6
-
7
- /**
8
- * @type {Set<string>} Used for type=AttribType.Multiple to remember the attributes that were added. */
9
- attrNames;
10
-
11
- /** @type {boolean} Provides one or more attributes on a component. */
12
- isComponent;
13
-
14
- constructor(nodeBefore, nodeMarker) {
15
- super(null, null);
16
- this.nodeMarker = nodeMarker;
17
- this.attrNames = new Set();
18
- }
19
-
20
- /**
21
- * @param exprs {Expr[][]} Only the first is used.
22
- * @param freeNodeGroups {boolean} Used only for watch. */
23
- apply(exprs, freeNodeGroups) {
24
- //#IFDEV
25
- assert(Array.isArray(exprs));
26
- //#ENDIF
27
-
28
- let expr = exprs[0];
29
- let node = this.nodeMarker;
30
-
31
- if (Array.isArray(expr))
32
- expr = expr.flat().join(' '); // flat and join so we can accept arrays of arrays of strings.
33
-
34
- // Add new attributes
35
- let oldNames = this.attrNames;
36
- this.attrNames = new Set();
37
- if (expr) {
38
- if (typeof expr === 'function') {
39
- Globals.currentPath = this; // Used by watch()
40
- this.watchFunction = expr; // used by renderWatched()
41
- expr = expr();
42
- Globals.currentPath = null;
43
- }
44
-
45
- // Attribute as name: value object.
46
- if (typeof expr === 'object') {
47
- for (let name in expr) {
48
- let value = expr[name];
49
- if (value === undefined || value === false || value === null)
50
- continue;
51
- node.setAttribute(name, value);
52
- this.attrNames.add(name)
53
- }
54
- }
55
-
56
- // Attributes as string
57
- else {
58
- let attrs = (expr + '') // Split string into multiple attributes.
59
- .split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|\S+))/g)
60
- .map(text => text.trim())
61
- .filter(text => text.length);
62
-
63
- for (let attr of attrs) {
64
- let [name, value] = attr.split(/\s*=\s*/); // split on first equals.
65
- value = (value || '').replace(/^(['"])(.*)\1$/, '$2'); // trim value quotes if they match.
66
- node.setAttribute(name, value);
67
- this.attrNames.add(name)
68
- }
69
- }
70
- }
71
-
72
- // Remove old attributes.
73
- for (let oldName of oldNames)
74
- if (!this.attrNames.has(oldName))
75
- node.removeAttribute(oldName);
76
- }
77
-
78
-
79
- getExpressionCount() { return 1 }
80
- getValue(exprs) { return exprs[0]; }
1
+ import Path from "./Path.js";
2
+ import Util from "./Util.js";
3
+ import assert from "./assert.js";
4
+ import PathToAttribValue from "./PathToAttribValue.js";
5
+ import PathToEvent from "./PathToEvent.js";
6
+ import {JsxAttr, styleToCss} from "./jsx.js";
7
+
8
+ export default class PathToAttribs extends Path {
9
+
10
+ /**
11
+ * @type {Set<string>} Used for type=AttribType.Multiple to remember the attributes that were added. */
12
+ attrNames;
13
+
14
+ /** @type {boolean} Provides one or more attributes on a component. */
15
+ isComponent;
16
+
17
+ constructor(nodeBefore, nodeMarker) {
18
+ super(null, null);
19
+ this.nodeMarker = nodeMarker;
20
+ this.attrNames = new Set();
21
+ }
22
+
23
+ /**
24
+ * @param exprs {Expr[][]} Only the first is used. */
25
+ apply(exprs) {
26
+ //#IFDEV
27
+ assert(Array.isArray(exprs));
28
+ //#ENDIF
29
+ this.applySingle(exprs[0]);
30
+ }
31
+
32
+ /**
33
+ * @param expr {Expr} */
34
+ applySingle(expr) {
35
+ let node = this.nodeMarker;
36
+
37
+ // JSX Tier 1 whole-attribute hole: `<a ` + jsxAttr("href", v) + `>`.
38
+ if (expr instanceof JsxAttr)
39
+ return this.applyJsxAttr(expr);
40
+
41
+ if (Array.isArray(expr))
42
+ expr = expr.flat().join(' '); // flat and join so we can accept arrays of arrays of strings.
43
+
44
+ // Add new attributes
45
+ let oldNames = this.attrNames;
46
+ this.attrNames = new Set();
47
+ if (expr) {
48
+ if (typeof expr === 'function')
49
+ expr = expr();
50
+
51
+ // Attribute as name: value object.
52
+ if (typeof expr === 'object') {
53
+ for (let name in expr) {
54
+ let value = expr[name];
55
+ if (value === undefined || value === false || value === null)
56
+ continue;
57
+ node.setAttribute(name, value);
58
+ this.attrNames.add(name)
59
+ }
60
+ }
61
+
62
+ // Attributes as string
63
+ else {
64
+ let attribs = Util.splitAttribs(expr);
65
+ for (let name in attribs) {
66
+ node.setAttribute(name, attribs[name]);
67
+ this.attrNames.add(name);
68
+ }
69
+ }
70
+ }
71
+
72
+ // Remove old attributes.
73
+ for (let oldName of oldNames)
74
+ if (!this.attrNames.has(oldName))
75
+ node.removeAttribute(oldName);
76
+ }
77
+
78
+
79
+ /**
80
+ * Apply a single JSX jsxAttr(name, value) pair. We delegate to a cached PathToEvent or
81
+ * PathToAttribValue sub-path so events, html properties, two-way binding, booleans, and
82
+ * contenteditable all behave exactly as `name=${value}` in a tagged template. The attr name
83
+ * at a given hole is a compile-time literal, so the sub-path is stable across renders.
84
+ * @param attr {JsxAttr} */
85
+ applyJsxAttr(attr) {
86
+ let name = attr.name;
87
+ if (name === 'key') // Lifted onto template.key by jsxTemplate(); never rendered as an attribute.
88
+ return;
89
+ let value = attr.value;
90
+
91
+ let sub = this.jsxSub;
92
+ if (sub === undefined || this.jsxSubName !== name) {
93
+ let node = this.nodeMarker;
94
+ if (Util.isEvent(name))
95
+ sub = new PathToEvent(null, node, name, null);
96
+ else {
97
+ sub = new PathToAttribValue(null, node, name, null);
98
+ sub.isHtmlProperty = Util.isHtmlProp(node, name);
99
+ }
100
+ sub.isComponentAttrib = this.isComponentAttrib;
101
+ this.jsxSub = sub;
102
+ this.jsxSubName = name;
103
+ }
104
+ sub.nodeMarker = this.nodeMarker;
105
+ sub.parentNg = this.parentNg;
106
+ if (name === 'style')
107
+ value = styleToCss(value);
108
+ sub.applySingle(value);
109
+ }
110
+
111
+
112
+ getExpressionCount() { return 1 }
113
+ getValue(exprs) { return exprs[0]; }
81
114
  }
@@ -1,8 +1,8 @@
1
- import Path from "./Path.js";
2
-
3
- // This Path renders nothing.
4
- export default class PathToComment extends Path {
5
- constructor(nodeBefore, nodeMarker) {
6
- super(nodeBefore, nodeMarker);
7
- }
1
+ import Path from "./Path.js";
2
+
3
+ // This Path renders nothing.
4
+ export default class PathToComment extends Path {
5
+ constructor(nodeBefore, nodeMarker) {
6
+ super(nodeBefore, nodeMarker);
7
+ }
8
8
  }
@@ -1,189 +1,184 @@
1
- import Path from "./Path.js";
2
- import Util from "./Util.js";
3
- import delve from "./delve.js";
4
- import assert from "./assert.js";
5
- import PathToAttribValue from "./PathToAttribValue.js";
6
- import Globals from "./Globals.js";
7
-
8
- export default class PathToComponent extends Path {
9
-
10
- /** @type {PathToAttribValue[]} Paths to dynamics attributes that will be set on the component.*/
11
- attribPaths;
12
-
13
- constructor(nodeBefore, nodeMarker) {
14
- super(null, nodeMarker);
15
- }
16
-
17
- /**
18
- * Call render() on the component pointed to by this Path.
19
- * And instantiate it (from a -solarite-placeholder element) if it hasn't been done yet.
20
- * @param exprs {Expr[][]} Expressions to evaluate for each attribute to pass to the constructor.
21
- * This is different than other Path.apply() functions which only receive Expr[] and not Expr[][].
22
- * Because here we're receiving an array of arrays of expressions, one for each dynamic attribute.
23
- * @param freeNodeGroups {boolean} Used only by watch.js.
24
- * @param changed {boolean} True if the exprs have changed since the last time render() was called.*/
25
- apply(exprs, freeNodeGroups=true, changed=true) {
26
- //#IFDEV
27
- assert(Array.isArray(exprs));
28
- assert(!exprs.length || Array.isArray(exprs[0]));
29
- //#ENDIF
30
-
31
- //#IFDEV
32
- assert(exprs.length === this.attribPaths.length);
33
- //#ENDIF
34
-
35
- let el = this.nodeMarker;
36
-
37
- // 1. Attributes
38
- let attribs = Util.attribsToObject(el, '_is');
39
- for (let i=0, attribPath; attribPath = this.attribPaths[i]; i++) {
40
- if (attribPath instanceof PathToAttribValue) {
41
- let name = Util.dashesToCamel(attribPath.attrName);
42
- attribs[name] = attribPath.getValue(exprs[i]);
43
- }
44
- else { // PathToAttribs
45
- let val = attribPath.getValue(exprs[i]);
46
- if (typeof val === 'object')
47
- for (let name in val)
48
- attribs[Util.dashesToCamel(name)] = val[name];
49
- else if (typeof val === 'string') {
50
- let attrs = val.split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|\S+))/g)
51
- .map(text => text.trim())
52
- .filter(text => text.length);
53
-
54
- for (let attr of attrs) {
55
- let [name, value] = attr.split(/\s*=\s*/); // split on first equals.
56
- value = (value || '').replace(/^(['"])(.*)\1$/, '$2'); // trim value quotes if they match.
57
- attribs[Util.dashesToCamel(name)] = value;
58
- }
59
- }
60
- }
61
- }
62
-
63
- // 2. Instantiate component on first time.
64
- let isAttrib = el.getAttribute('_is');
65
- if (el.tagName.endsWith('-SOLARITE-PLACEHOLDER') || isAttrib) {
66
-
67
-
68
- // 2a. Instantiate component
69
- let tagName = (isAttrib || el.tagName.slice(0, -21)).toLowerCase(); // Remove -SOLARITE-PLACEHOLDER
70
- let Constructor = customElements.get(tagName);
71
- if (!Constructor)
72
- throw new Error(`Must call customElements.define('${tagName}', Class) before using it.`);
73
-
74
- Globals.currentSlotChildren = [...el.childNodes]; // TODO: Does this need to be a stack?
75
- let newEl = new Constructor(attribs);
76
-
77
- // 2b. Copy attributes over.
78
- if (isAttrib) {
79
- newEl.setAttribute('is', isAttrib);
80
- // el.removeAttribute('_is');
81
- }
82
- for (let attrib of el.attributes)
83
- if (attrib.name !== '_is')
84
- newEl.setAttribute(attrib.name, attrib.value);
85
-
86
- // Set dynamic attributes if they are primitive types.
87
- for (let name in attribs) {
88
- let val = attribs[name];
89
- let valType = typeof val;
90
- if (valType === 'boolean') {
91
- if (val !== false && val !== undefined && val !== null) // Util.isFalsy() inlined
92
- newEl.setAttribute(name, '');
93
- }
94
-
95
- // If type is a non-boolean primitive, set the attribute value.
96
- else if (valType==='string' || valType === 'number' || valType==='bigint')
97
- newEl.setAttribute(name, val);
98
- }
99
-
100
-
101
- // 2c. If an id pointed at the placeholder, update it to point to the new element.
102
- let id = newEl.getAttribute('data-id') || newEl.getAttribute('id');
103
- if (id)
104
- delve(this.parentNg.getRootNode(), id.split(/\./g), newEl);
105
-
106
- // 2d. Update paths to use replaced element.
107
- let ng = this.parentNg;
108
- this.nodeMarker = newEl;
109
- for (let path of ng.paths) {
110
- if (path.nodeMarker === el)
111
- path.nodeMarker = newEl;
112
- if (path.nodeBefore === el)
113
- path.nodeBefore = newEl;
114
- }
115
- if (ng.startNode === el)
116
- ng.startNode = newEl;
117
- if (ng.endNode === el)
118
- ng.endNode = newEl;
119
-
120
- // 2f. Call render() if it wasn't called by the constructor.
121
- // This must happen before we add it to the DOM which can trigger connectedCallback() -> renderFirstTime()
122
- // Because that path renders it without the attribute expressions.
123
- if (typeof newEl.render === 'function' && !Globals.rendered.has(newEl))
124
- newEl.render(attribs, changed);
125
-
126
- // 2g. Update attribute paths to use the new element and re-apply them.
127
- for (let i=0, attribPath; attribPath = this.attribPaths[i]; i++) {
128
- attribPath.parentNg = this.parentNg;
129
- attribPath.nodeMarker = newEl;
130
- attribPath.apply(exprs[i]);
131
- }
132
-
133
- // 2e. Swap it to the DOM.
134
- el.replaceWith(newEl);
135
- }
136
-
137
- // 2f. Render
138
- else if (typeof el.render === 'function')
139
- el.render(attribs, changed);
140
-
141
- Globals.currentSlotChildren = null;
142
- }
143
-
144
- /**
145
- * @param newRoot {HTMLElement}
146
- * @param pathOffset {int}
147
- * @return {Path} */
148
- clone(newRoot, pathOffset=0) {
149
- /*#IFDEV*/this.verify();/*#ENDIF*/
150
- let nodeMarker = this.getNewNodeMarker(newRoot, pathOffset);
151
- let result = new PathToComponent(null, nodeMarker);
152
- result.attribPaths = this.attribPaths.map(path => path.clone(newRoot, pathOffset));
153
-
154
- //#IFDEV
155
- result.verify();
156
- //#ENDIF
157
-
158
- return result;
159
- }
160
-
161
- getExpressionCount() { return 0 }
162
-
163
- //#IFDEV
164
- verify() {
165
- super.verify();
166
- assert(this.nodeMarker.nodeType === Node.ELEMENT_NODE);
167
- assert(this.nodeMarker.tagName.includes('-') || this.nodeMarker.hasAttribute('is') || this.nodeMarker.hasAttribute('_is'));
168
- if (this.attribPaths)
169
- for (let path of this.attribPaths)
170
- path.verify();
171
- }
172
-
173
- //#ENDIF
174
- }
175
-
176
-
177
- // TODO: Conver this to an iterator, to make it faster?
178
- // Especially since it's only used on the first render?
179
- function getNodes(startNode, endNode) {
180
- let result = [];
181
- let current = startNode
182
- let afterLast = endNode?.nextSibling
183
- while (current && current !== afterLast) {
184
- result.push(current)
185
- current = current.nextSibling
186
- }
187
-
188
- return result;
1
+ import Path from "./Path.js";
2
+ import Util from "./Util.js";
3
+ import delve, {isDelvePath} from "./delve.js";
4
+ import assert from "./assert.js";
5
+ import PathToAttribValue from "./PathToAttribValue.js";
6
+ import PathToKey from "./PathToKey.js";
7
+ import Globals from "./Globals.js";
8
+ import {getObjectHash} from "./Template.js";
9
+
10
+ export default class PathToComponent extends Path {
11
+
12
+ /** @type {PathToAttribValue[]} Paths to dynamics attributes that will be set on the component.*/
13
+ attribPaths;
14
+
15
+ /** @type {string} Hash of the exprs from the previous apply(), used to detect changes. */
16
+ appliedExprsHash;
17
+
18
+ constructor(nodeBefore, nodeMarker) {
19
+ super(null, nodeMarker);
20
+ }
21
+
22
+ /**
23
+ * Call render() on the component pointed to by this Path.
24
+ * And instantiate it (from a -solarite-placeholder element) if it hasn't been done yet.
25
+ * @param exprs {Expr[][]} Expressions to evaluate for each attribute to pass to the constructor.
26
+ * This is different than other Path.apply() functions which only receive Expr[] and not Expr[][].
27
+ * Because here we're receiving an array of arrays of expressions, one for each dynamic attribute. */
28
+ apply(exprs) {
29
+ //#IFDEV
30
+ assert(Array.isArray(exprs));
31
+ assert(!exprs.length || Array.isArray(exprs[0]));
32
+ //#ENDIF
33
+
34
+ //#IFDEV
35
+ assert(exprs.length === this.attribPaths.length);
36
+ //#ENDIF
37
+
38
+ // Deep comparison via hashing, so mutating a field on the same object counts as changed.
39
+ let newHash = getObjectHash(exprs);
40
+ let changed = newHash !== this.appliedExprsHash;
41
+ this.appliedExprsHash = newHash;
42
+
43
+ let el = this.nodeMarker;
44
+
45
+ // 1. Attributes
46
+ let attribs = Util.attribsToObject(el, '_is');
47
+ for (let i=0, attribPath; attribPath = this.attribPaths[i]; i++) {
48
+ if (attribPath instanceof PathToKey) // The list key is never a component arg.
49
+ continue;
50
+ if (attribPath instanceof PathToAttribValue) {
51
+ let name = Util.dashesToCamel(attribPath.attrName);
52
+
53
+ // Resolve two way bindimg path before we pass it to the component.
54
+ let value = attribPath.getValue(exprs[i]);
55
+ if (!attribPath.attrValue && isDelvePath(value))
56
+ value = delve(value[0], value.slice(1));
57
+
58
+ attribs[name] = value;
59
+ }
60
+ else { // PathToAttribs
61
+ let val = attribPath.getValue(exprs[i]);
62
+ if (typeof val === 'object')
63
+ for (let name in val)
64
+ attribs[Util.dashesToCamel(name)] = val[name];
65
+ else if (typeof val === 'string') {
66
+ let attrs = Util.splitAttribs(val);
67
+ for (let name in attrs)
68
+ attribs[Util.dashesToCamel(name)] = attrs[name];
69
+ }
70
+ }
71
+ }
72
+
73
+ // 2. Instantiate component on first time.
74
+ let isAttrib = el.getAttribute('_is');
75
+ if (el.tagName.endsWith('-SOLARITE-PLACEHOLDER') || isAttrib) {
76
+
77
+
78
+ // 2a. Instantiate component
79
+ let tagName = (isAttrib || el.tagName.slice(0, -21)).toLowerCase(); // Remove -SOLARITE-PLACEHOLDER
80
+ let Constructor = customElements.get(tagName);
81
+ if (!Constructor)
82
+ throw new Error(`Must call customElements.define('${tagName}', Class) before using it.`);
83
+
84
+ Globals.currentSlotChildren = [...el.childNodes]; // TODO: Does this need to be a stack?
85
+ let newEl = new Constructor(attribs);
86
+
87
+ // 2b. Copy attributes over.
88
+ if (isAttrib) {
89
+ newEl.setAttribute('is', isAttrib);
90
+ // el.removeAttribute('_is');
91
+ }
92
+ for (let attrib of el.attributes)
93
+ if (attrib.name !== '_is')
94
+ newEl.setAttribute(attrib.name, attrib.value);
95
+
96
+ // Set dynamic attributes if they are primitive types.
97
+ for (let name in attribs) {
98
+ let val = attribs[name];
99
+ let valType = typeof val;
100
+ if (valType === 'boolean') {
101
+ if (val !== false && val !== undefined && val !== null) // Util.isFalsy() inlined
102
+ newEl.setAttribute(name, '');
103
+ }
104
+
105
+ // If type is a non-boolean primitive, set the attribute value.
106
+ else if (valType==='string' || valType === 'number' || valType==='bigint')
107
+ newEl.setAttribute(name, val);
108
+ }
109
+
110
+
111
+ // 2c. If an id pointed at the placeholder, update it to point to the new element.
112
+ let id = newEl.getAttribute('data-id') || newEl.getAttribute('id');
113
+ if (id)
114
+ delve(this.parentNg.getRootNode(), id.split(/\./g), newEl);
115
+
116
+ // 2d. Update paths to use replaced element.
117
+ let ng = this.parentNg;
118
+ this.nodeMarker = newEl;
119
+ for (let path of ng.paths) {
120
+ if (path.nodeMarker === el)
121
+ path.nodeMarker = newEl;
122
+ if (path.nodeBefore === el)
123
+ path.nodeBefore = newEl;
124
+ }
125
+ if (ng.startNode === el)
126
+ ng.startNode = newEl;
127
+ if (ng.endNode === el)
128
+ ng.endNode = newEl;
129
+
130
+ // 2f. Call render() if it wasn't called by the constructor.
131
+ // This must happen before we add it to the DOM which can trigger connectedCallback() -> renderFirstTime()
132
+ // Because that path renders it without the attribute expressions.
133
+ if (typeof newEl.render === 'function' && !Globals.rendered.has(newEl))
134
+ newEl.render(attribs, true);
135
+
136
+ // 2g. Update attribute paths to use the new element and re-apply them.
137
+ for (let i=0, attribPath; attribPath = this.attribPaths[i]; i++) {
138
+ attribPath.parentNg = this.parentNg;
139
+ attribPath.nodeMarker = newEl;
140
+ attribPath.apply(exprs[i]);
141
+ }
142
+
143
+ // 2e. Swap it to the DOM.
144
+ el.replaceWith(newEl);
145
+ }
146
+
147
+ // 2f. Render
148
+ else if (typeof el.render === 'function')
149
+ el.render(attribs, changed);
150
+
151
+ Globals.currentSlotChildren = null;
152
+ }
153
+
154
+ /**
155
+ * @param newRoot {HTMLElement}
156
+ * @param pathOffset {int}
157
+ * @return {Path} */
158
+ clone(newRoot, pathOffset=0) {
159
+ /*#IFDEV*/this.verify();/*#ENDIF*/
160
+ let nodeMarker = this.getNewNodeMarker(newRoot, pathOffset);
161
+ let result = new PathToComponent(null, nodeMarker);
162
+ result.attribPaths = this.attribPaths.map(path => path.clone(newRoot, pathOffset));
163
+
164
+ //#IFDEV
165
+ result.verify();
166
+ //#ENDIF
167
+
168
+ return result;
169
+ }
170
+
171
+ getExpressionCount() { return 0 }
172
+
173
+ //#IFDEV
174
+ verify() {
175
+ super.verify();
176
+ assert(this.nodeMarker.nodeType === Node.ELEMENT_NODE);
177
+ assert(this.nodeMarker.tagName.includes('-') || this.nodeMarker.hasAttribute('is') || this.nodeMarker.hasAttribute('_is'));
178
+ if (this.attribPaths)
179
+ for (let path of this.attribPaths)
180
+ path.verify();
181
+ }
182
+
183
+ //#ENDIF
189
184
  }