@webtides/element-js 1.0.0-alpha.5 → 1.0.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtides/element-js",
3
- "version": "1.0.0-alpha.5",
3
+ "version": "1.0.0-alpha.7",
4
4
  "module": "index.js",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -78,6 +78,9 @@ const processAttribute = (node, name) => {
78
78
  };
79
79
  };
80
80
 
81
+ /** @type {Map<Element, AttributePart>} */
82
+ const attributePartsCache = new WeakMap();
83
+
81
84
  /**
82
85
  * @param {Element} node
83
86
  * @param {String} name
@@ -114,24 +117,27 @@ export class AttributePart extends Part {
114
117
  initialValue = undefined;
115
118
 
116
119
  /**
117
- * @param {Node} node
120
+ * @param {Comment} node
118
121
  * @param {String} name
119
122
  * @param {String} initialValue
120
123
  */
121
124
  constructor(node, name, initialValue) {
125
+ // If we have multiple attribute parts with the same name, it means we have multiple
126
+ // interpolations inside that attribute. Instead of creating a new part, we will return the same
127
+ // as before and let it defer the update until the last interpolation gets updated
128
+ const attributePart = attributePartsCache.get(node.nextElementSibling);
129
+ if (attributePart && attributePart.name === name) {
130
+ attributePart.interpolations++;
131
+ node.__part = attributePart; // add Part to comment node for debugging in the browser
132
+ return attributePart;
133
+ }
134
+
122
135
  super();
123
- node.__part = this; // add Part to comment node for debugging in the browser
124
136
  this.name = name;
125
137
  this.initialValue = initialValue;
126
138
  this.processor = processAttributePart(node.nextElementSibling, this.name);
127
- }
128
-
129
- /**
130
- * @param {Node} node
131
- */
132
- addNode(node) {
133
- this.interpolations++;
134
- // TODO: what about the comment node? It will always be the first for now...
139
+ node.__part = this; // add Part to comment node for debugging in the browser
140
+ attributePartsCache.set(node.nextElementSibling, this);
135
141
  }
136
142
 
137
143
  /**
@@ -34,6 +34,8 @@ export class TemplatePart extends Part {
34
34
 
35
35
  let serverSideRendered = false;
36
36
  if (startNode) {
37
+ startNode.__part = this; // add Part to comment node for debugging in the browser
38
+
37
39
  const placeholder = startNode.data;
38
40
  const childNodes = [startNode];
39
41
  let childNode = startNode.nextSibling;
@@ -99,8 +101,6 @@ export class TemplatePart extends Part {
99
101
  partsCache.set(templateResult.strings, parts);
100
102
  }
101
103
 
102
- /** @type AttributePart */
103
- let previousAttributePart = undefined;
104
104
  this.parts = parts
105
105
  .map((part) => {
106
106
  // We currently need the path because the fragment will be cloned via importNode and therefore the node will be a different one
@@ -110,22 +110,13 @@ export class TemplatePart extends Part {
110
110
  .map((part, index) => {
111
111
  if (part.type === 'node') {
112
112
  return new ChildNodePart(part.node, templateResult.values[index]);
113
- // TODO: the nodes in the parts array also have nested parts information... could we start creating the nested parts here as well?
114
113
  }
115
114
  if (part.type === 'attribute') {
116
- // If we have multiple attribute parts with the same name, it means we have multiple
117
- // interpolations inside that attribute. Instead of creating a new part, we will return the same
118
- // as before and let it defer the update until the last interpolation gets updated
119
- if (previousAttributePart && previousAttributePart.name === part.name) {
120
- previousAttributePart.addNode(part.node);
121
- return previousAttributePart;
122
- }
123
- return (previousAttributePart = new AttributePart(part.node, part.name, part.initialValue));
115
+ return new AttributePart(part.node, part.name, part.initialValue);
124
116
  }
125
117
  if (part.type === 'directive') {
126
118
  return new NodePart(part.node, templateResult.values[index]);
127
119
  }
128
-
129
120
  throw `cannot map part: ${part}`;
130
121
  });
131
122
  this.strings = templateResult.strings;
@@ -12,7 +12,7 @@ const partPositions = /[\x01\x02]/g;
12
12
  // \x03 COMMENT.ATTRIBUTE_TOKEN
13
13
  // \x04 Node.ATTRIBUTE_TOKEN
14
14
 
15
- const interpolation = new RegExp(`(<!--dom-part-(\\d+)--><!--/dom-part-(\\d+)-->|(\\S\\w+)="\x04(")?|\x04)`, 'g');
15
+ const interpolation = new RegExp(`(<!--dom-part-(\\d+)--><!--/dom-part-(\\d+)-->|(\\S[-\\w]+)="\x04(")?|\x04)`, 'g');
16
16
 
17
17
  /**
18
18
  * Given a template, find part positions as both nodes and attributes and
@@ -258,40 +258,39 @@ export class TemplateResult {
258
258
  const template = range.cloneContents();
259
259
 
260
260
  const treeWalker = globalThis.document?.createTreeWalker(template, 128);
261
- let node = treeWalker.currentNode;
261
+ let node = treeWalker.nextNode(); //to skip the root template-part
262
+ const parts = [];
262
263
 
263
- // this is kinda freaky because the tree walker is shared throughout the recursion - but YOLO
264
- const getParts = () => {
265
- const parts = [];
266
- let currentPart = undefined;
267
- // search for parts through numbered comment nodes with placeholders
268
- while ((node = treeWalker.nextNode())) {
269
- if (/^template-part$/.test(node.data) && currentPart) {
270
- // start recursion -> add nested parts to previous part
271
- currentPart.parts = getParts();
272
- }
273
- if (/^\/template-part$/.test(node.data)) {
274
- // end recursion
275
- return parts;
276
- }
277
- if (/^dom-part-\d+$/.test(node.data)) {
278
- parts.push((currentPart = { type: 'node', path: getNodePath(node), parts: [] }));
279
- }
280
- if (/^dom-part-\d+:/.test(node.data)) {
281
- const [_, attribute] = node.data.split(':');
282
- const [name, initialValue] = attribute.split('=');
283
- currentPart = undefined;
284
- parts.push({ type: 'attribute', path: getNodePath(node), name: name, initialValue });
285
- }
286
- if (/^dom-part-\d+\$/.test(node.data)) {
287
- currentPart = undefined;
288
- parts.push({ type: 'directive', path: getNodePath(node) });
264
+ let nestedLevel = 0;
265
+ // search for parts through numbered comment nodes with placeholders
266
+ while ((node = treeWalker.nextNode())) {
267
+ if (/^template-part$/.test(node.data)) {
268
+ nestedLevel++;
269
+ continue;
270
+ }
271
+ if (/^\/template-part$/.test(node.data)) {
272
+ if (nestedLevel > 0) {
273
+ nestedLevel--;
289
274
  }
275
+ continue;
290
276
  }
291
- return parts;
292
- };
293
-
294
- const parts = getParts();
277
+ if (nestedLevel > 0) {
278
+ continue;
279
+ }
280
+ if (/^dom-part-\d+$/.test(node.data)) {
281
+ parts.push({ type: 'node', path: getNodePath(node) });
282
+ continue;
283
+ }
284
+ if (/^dom-part-\d+:/.test(node.data)) {
285
+ const [_, ...attribute] = node.data.split(':');
286
+ const [name, initialValue] = attribute.join(':').split('=');
287
+ parts.push({ type: 'attribute', path: getNodePath(node), name: name, initialValue });
288
+ continue;
289
+ }
290
+ if (/^dom-part-\d+\$/.test(node.data)) {
291
+ parts.push({ type: 'directive', path: getNodePath(node) });
292
+ }
293
+ }
295
294
 
296
295
  // We couldn't correctly parse parts from the template
297
296
  if (parts.length !== this.strings.length - 1) {