solarite 0.2.3 → 0.3.0
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/dist/Solarite-debug.js +1708 -1211
- package/dist/Solarite.js +1642 -1047
- package/dist/Solarite.min.js +2 -2
- package/package.json +1 -1
- package/readme.md +1 -3
- package/src/solarite/ExprPath.js +479 -196
- package/src/solarite/Globals.js +77 -51
- package/src/solarite/HtmlParser.js +91 -0
- package/src/solarite/NodeGroup.js +288 -195
- package/src/solarite/Shell.js +117 -91
- package/src/solarite/Solarite.js +7 -3
- package/src/solarite/Template.js +23 -18
- package/src/solarite/Util.js +117 -179
- package/src/solarite/createSolarite.js +16 -138
- package/src/solarite/getArg.js +31 -16
- package/src/solarite/{r.js → h.js} +56 -18
- package/src/solarite/hash.js +12 -9
- package/src/solarite/watch.js +546 -0
- package/src/util/Errors.js +1 -0
- package/src/util/MultiValueMap.js +73 -13
- package/src/util/Util.js +15 -2
- package/src/util/delve.js +5 -4
- package/src/solarite/watch3.js +0 -98
- package/src/util/WeakArray.js +0 -33
package/src/solarite/Shell.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {assert} from "../util/Errors.js";
|
|
2
|
-
import ExprPath, {
|
|
3
|
-
import {
|
|
2
|
+
import ExprPath, {ExprPathType, getNodePath} from "./ExprPath.js";
|
|
3
|
+
import {isEvent} from "./Util.js";
|
|
4
4
|
import Globals from "./Globals.js";
|
|
5
|
+
import HtmlParser from "./HtmlParser.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* A Shell is created from a tagged template expression instantiated as Nodes,
|
|
@@ -9,105 +10,75 @@ import Globals from "./Globals.js";
|
|
|
9
10
|
* Only one Shell is created for all the items in a loop.
|
|
10
11
|
*
|
|
11
12
|
* When a NodeGroup is created from a Template's html strings,
|
|
12
|
-
* the NodeGroup then clones the Shell's
|
|
13
|
+
* the NodeGroup then clones the Shell's fragment to be its nodes. */
|
|
13
14
|
export default class Shell {
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
* @type {DocumentFragment} DOM parent of the shell nodes. */
|
|
17
|
+
* @type {DocumentFragment|Text} DOM parent of the shell's nodes. */
|
|
17
18
|
fragment;
|
|
18
19
|
|
|
19
20
|
/** @type {ExprPath[]} Paths to where expressions should go. */
|
|
20
21
|
paths = [];
|
|
21
22
|
|
|
22
|
-
//
|
|
23
|
-
events = [];
|
|
23
|
+
// Elements with events. Not yet used.
|
|
24
|
+
// events = [];
|
|
24
25
|
|
|
25
26
|
/** @type {int[][]} Array of paths */
|
|
26
27
|
ids = [];
|
|
28
|
+
|
|
29
|
+
/** @type {int[][]} Array of paths */
|
|
27
30
|
scripts = [];
|
|
31
|
+
|
|
32
|
+
/** @type {int[][]} Array of paths */
|
|
28
33
|
styles = [];
|
|
29
34
|
|
|
35
|
+
/** @type {int[][]} Array of paths. Used by activateEmbeds() to quickly find components. */
|
|
30
36
|
staticComponents = [];
|
|
31
37
|
|
|
38
|
+
/** @type {{path:int[], attribs:Object<string, string>}[]} */
|
|
39
|
+
//componentAttribs = [];
|
|
40
|
+
|
|
32
41
|
|
|
33
42
|
|
|
34
43
|
/**
|
|
35
44
|
* Create the nodes but without filling in the expressions.
|
|
36
45
|
* This is useful because the expression-less nodes created by a template can be cached.
|
|
37
|
-
* @param html {string[]} */
|
|
46
|
+
* @param html {string[]} Html strings, split on places where an expression exists. */
|
|
38
47
|
constructor(html=null) {
|
|
39
48
|
if (!html)
|
|
40
49
|
return;
|
|
41
50
|
|
|
42
51
|
//#IFDEV
|
|
43
|
-
this.
|
|
52
|
+
this._html = html.join('');
|
|
44
53
|
//#ENDIF
|
|
45
54
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
let buffer = [];
|
|
51
|
-
let commentPlaceholder = `<!--!✨!-->`;
|
|
52
|
-
let componentNames = {};
|
|
53
|
-
|
|
54
|
-
htmlContext(null); // Reset the context.
|
|
55
|
-
for (let i=0; i<html.length; i++) {
|
|
56
|
-
let lastHtml = html[i];
|
|
57
|
-
let context = htmlContext(lastHtml);
|
|
58
|
-
|
|
59
|
-
// Swap out Embedded Solarite Components with ${} attributes.
|
|
60
|
-
// Later, NodeGroup.render() will search for these and replace them with the real components.
|
|
61
|
-
// Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
|
|
62
|
-
if (context === htmlContext.Attribute) {
|
|
63
|
-
|
|
64
|
-
let lastIndex, lastMatch;
|
|
65
|
-
lastHtml.replace(/<[a-z][a-z0-9]*-[a-z0-9-]+/ig, (match, index) => {
|
|
66
|
-
lastIndex = index+1; // +1 for after opening <
|
|
67
|
-
lastMatch = match.slice(1);
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
if (lastMatch) {
|
|
71
|
-
let newTagName = lastMatch + '-solarite-placeholder';
|
|
72
|
-
lastHtml = lastHtml.slice(0, lastIndex) + newTagName + lastHtml.slice(lastIndex + lastMatch.length);
|
|
73
|
-
componentNames[lastMatch] = newTagName
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
buffer.push(lastHtml);
|
|
78
|
-
//console.log(lastHtml, context)
|
|
79
|
-
if (i < html.length-1)
|
|
80
|
-
if (context === htmlContext.Text)
|
|
81
|
-
buffer.push(commentPlaceholder) // Comment Placeholder. because we can't put text in between <tr> tags for example.
|
|
82
|
-
else
|
|
83
|
-
buffer.push(String.fromCharCode(placeholder+i));
|
|
55
|
+
if (html.length === 1 && !html[0].match(/[<&]/)) {
|
|
56
|
+
this.fragment = document.createTextNode(html[0]);
|
|
57
|
+
return;
|
|
84
58
|
}
|
|
85
59
|
|
|
86
|
-
|
|
60
|
+
|
|
61
|
+
// 1. Add placeholders
|
|
62
|
+
let joinedHtml = Shell.addPlaceholders(html);
|
|
63
|
+
|
|
87
64
|
let template = document.createElement('template'); // Using a single global template won't keep the nodes as children of the DocumentFragment.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
for (let name in componentNames)
|
|
93
|
-
joinedHtml = joinedHtml.replaceAll(`</${name}>`, `</${componentNames[name]}>`);
|
|
94
|
-
|
|
95
|
-
if (joinedHtml)
|
|
96
|
-
template.innerHTML = joinedHtml;
|
|
97
|
-
else // Create one text node, so shell isn't empty and NodeGroups created from it have something to point the startNode and endNode at.
|
|
98
|
-
template.content.append(document.createTextNode(''))
|
|
65
|
+
if (joinedHtml)
|
|
66
|
+
template.innerHTML = joinedHtml;
|
|
67
|
+
else // Create one text node, so shell isn't empty and NodeGroups created from it have something to point the startNode and endNode at.
|
|
68
|
+
template.content.append(document.createTextNode(''))
|
|
99
69
|
this.fragment = template.content;
|
|
100
70
|
|
|
101
|
-
//
|
|
71
|
+
// 2. Find placeholders
|
|
102
72
|
let node;
|
|
103
73
|
let toRemove = [];
|
|
104
|
-
|
|
74
|
+
let placeholdersUsed = 0;
|
|
75
|
+
const walker = document.createTreeWalker(this.fragment, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT);
|
|
105
76
|
while (node = walker.nextNode()) {
|
|
106
77
|
|
|
107
78
|
// Remove previous after each iteration, so paths will still be calculated correctly.
|
|
108
79
|
toRemove.map(el => el.remove());
|
|
109
80
|
toRemove = [];
|
|
110
|
-
|
|
81
|
+
|
|
111
82
|
// Replace attributes
|
|
112
83
|
if (node.nodeType === 1) {
|
|
113
84
|
for (let attr of [...node.attributes]) { // Copy the attributes array b/c we remove attributes as we go.
|
|
@@ -115,7 +86,8 @@ export default class Shell {
|
|
|
115
86
|
// Whole attribute
|
|
116
87
|
let matches = attr.name.match(/^[\ue000-\uf8ff]$/)
|
|
117
88
|
if (matches) {
|
|
118
|
-
this.paths.push(new ExprPath(null, node,
|
|
89
|
+
this.paths.push(new ExprPath(null, node, ExprPathType.AttribMultiple));
|
|
90
|
+
placeholdersUsed ++;
|
|
119
91
|
node.removeAttribute(matches[0]);
|
|
120
92
|
}
|
|
121
93
|
|
|
@@ -124,16 +96,17 @@ export default class Shell {
|
|
|
124
96
|
let parts = attr.value.split(/[\ue000-\uf8ff]/g);
|
|
125
97
|
if (parts.length > 1) {
|
|
126
98
|
let nonEmptyParts = (parts.length === 2 && !parts[0].length && !parts[1].length) ? null : parts;
|
|
127
|
-
let type = isEvent(attr.name) ?
|
|
99
|
+
let type = isEvent(attr.name) ? ExprPathType.Event : ExprPathType.AttribValue;
|
|
128
100
|
|
|
129
101
|
this.paths.push(new ExprPath(null, node, type, attr.name, nonEmptyParts));
|
|
102
|
+
placeholdersUsed += parts.length - 1;
|
|
130
103
|
node.setAttribute(attr.name, parts.join(''));
|
|
131
104
|
}
|
|
132
105
|
}
|
|
133
106
|
}
|
|
134
107
|
}
|
|
135
108
|
// Replace comment placeholders
|
|
136
|
-
else if (node.nodeType ===
|
|
109
|
+
else if (node.nodeType === 8 && node.nodeValue === '!✨!') {
|
|
137
110
|
|
|
138
111
|
// Get or create nodeBefore.
|
|
139
112
|
let nodeBefore = node.previousSibling; // Can be the same as another Path's nodeMarker.
|
|
@@ -158,12 +131,14 @@ export default class Shell {
|
|
|
158
131
|
}
|
|
159
132
|
/*#IFDEV*/assert(nodeMarker);/*#ENDIF*/
|
|
160
133
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
let path = new ExprPath(nodeBefore, nodeMarker, PathType.Content);
|
|
164
|
-
|
|
134
|
+
let path = new ExprPath(nodeBefore, nodeMarker, ExprPathType.Content);
|
|
165
135
|
this.paths.push(path);
|
|
136
|
+
placeholdersUsed ++;
|
|
166
137
|
}
|
|
138
|
+
|
|
139
|
+
else if (node.nodeType === 3 && node.parentNode?.tagName === 'TEXTAREA' && node.textContent.includes('<!--!✨!-->'))
|
|
140
|
+
throw new Error(`Textarea can't have expressions inside them. Use <textarea value="\${...}"> instead.`);
|
|
141
|
+
|
|
167
142
|
|
|
168
143
|
|
|
169
144
|
// Sometimes users will comment out a block of html code that has expressions.
|
|
@@ -174,8 +149,9 @@ export default class Shell {
|
|
|
174
149
|
let parts = node.textContent.split(/[\ue000-\uf8ff]/g);
|
|
175
150
|
for (let i=0; i<parts.length-1; i++) {
|
|
176
151
|
let path = new ExprPath(node.previousSibling, node)
|
|
177
|
-
path.type =
|
|
152
|
+
path.type = ExprPathType.Comment;
|
|
178
153
|
this.paths.push(path);
|
|
154
|
+
placeholdersUsed ++;
|
|
179
155
|
}
|
|
180
156
|
}
|
|
181
157
|
|
|
@@ -193,8 +169,9 @@ export default class Shell {
|
|
|
193
169
|
}
|
|
194
170
|
|
|
195
171
|
for (let i=0, node; node=placeholders[i]; i++) {
|
|
196
|
-
let path = new ExprPath(node.previousSibling, node,
|
|
172
|
+
let path = new ExprPath(node.previousSibling, node, ExprPathType.Content);
|
|
197
173
|
this.paths.push(path);
|
|
174
|
+
placeholdersUsed ++;
|
|
198
175
|
|
|
199
176
|
/*#IFDEV*/path.verify();/*#ENDIF*/
|
|
200
177
|
}
|
|
@@ -206,17 +183,17 @@ export default class Shell {
|
|
|
206
183
|
}
|
|
207
184
|
toRemove.map(el => el.remove());
|
|
208
185
|
|
|
186
|
+
// Less than or equal because there can be one path to multiple expressions
|
|
187
|
+
// if those expressions are in the same attribute value.
|
|
188
|
+
if (placeholdersUsed !== html.length-1)
|
|
189
|
+
throw new Error(`Could not parse expressions in template. Check for duplicate attributes or malformed html: ${html.join('${...}')}`);
|
|
190
|
+
|
|
209
191
|
// Handle solarite-placeholder's.
|
|
210
|
-
// Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
|
|
211
|
-
//if (componentNames.size)
|
|
212
|
-
// this.components = [...this.fragment.querySelectorAll([...componentNames].join(','))]
|
|
213
192
|
|
|
214
|
-
// Rename "is" attributes so the Web Components don't instantiate until we have the values of their PathExpr arguments.
|
|
193
|
+
// 3. Rename "is" attributes so the Web Components don't instantiate until we have the values of their PathExpr arguments.
|
|
215
194
|
// that happens in NodeGroup.applyComponentExprs()
|
|
216
|
-
for (let el of this.fragment.querySelectorAll('[is]'))
|
|
217
|
-
el.setAttribute('_is', el.getAttribute('is'))
|
|
218
|
-
// this.components.push(el);
|
|
219
|
-
}
|
|
195
|
+
for (let el of this.fragment.querySelectorAll('[is]'))
|
|
196
|
+
el.setAttribute('_is', el.getAttribute('is'));
|
|
220
197
|
|
|
221
198
|
for (let path of this.paths) {
|
|
222
199
|
if (path.nodeBefore)
|
|
@@ -224,16 +201,63 @@ export default class Shell {
|
|
|
224
201
|
path.nodeMarkerPath = getNodePath(path.nodeMarker)
|
|
225
202
|
|
|
226
203
|
// Cache so we don't have to calculate this later inside NodeGroup.applyExprs()
|
|
227
|
-
if (path.type ===
|
|
204
|
+
if (path.type === ExprPathType.AttribValue && path.nodeMarker.nodeType === 1 &&
|
|
228
205
|
(path.nodeMarker.tagName.includes('-') || path.nodeMarker.hasAttribute('is'))) {
|
|
229
|
-
path.type =
|
|
206
|
+
path.type = ExprPathType.ComponentAttribValue;
|
|
230
207
|
}
|
|
231
208
|
}
|
|
232
209
|
|
|
233
210
|
this.findEmbeds();
|
|
234
211
|
|
|
235
212
|
/*#IFDEV*/this.verify();/*#ENDIF*/
|
|
236
|
-
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 1. Add a Unicode placeholder char for where expressions go within attributes.
|
|
217
|
+
* 2. Add a comment placeholder for where expressions are children of other nodes.
|
|
218
|
+
* 3. Append -solarite-placeholder to the tag names of custom components so that we can wait to instantiate them later.
|
|
219
|
+
* @param htmlChunks {string[]}
|
|
220
|
+
* @returns {string} */
|
|
221
|
+
static addPlaceholders(htmlChunks) {
|
|
222
|
+
let tokens = [];
|
|
223
|
+
|
|
224
|
+
function addToken(token, context) {
|
|
225
|
+
|
|
226
|
+
if (context === HtmlParser.Tag) {
|
|
227
|
+
// Find Solarite Components tags and append -solarite-placeholder to their tag names.
|
|
228
|
+
// This way we can gather their constructor arguments and their children before we call their constructor.
|
|
229
|
+
// Later, NodeGroup.createNewComponent() will replace them with the real components.
|
|
230
|
+
// Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
|
|
231
|
+
token = token.replace(/^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i, match => match + '-solarite-placeholder');
|
|
232
|
+
}
|
|
233
|
+
tokens.push(token)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
let htmlParser = new HtmlParser(); // Reset the context.
|
|
237
|
+
for (let i = 0; i < htmlChunks.length; i++) {
|
|
238
|
+
let lastHtml = htmlChunks[i];
|
|
239
|
+
|
|
240
|
+
// Append -solarite-placholder to web component tags, so we can pass args to them when they're instantiated.
|
|
241
|
+
let lastIndex = 0;
|
|
242
|
+
let context = htmlParser.parse(lastHtml, (html, index, oldContext, newContext) => {
|
|
243
|
+
if (lastIndex !== index) {
|
|
244
|
+
let token = html.slice(lastIndex, index);
|
|
245
|
+
addToken(token, oldContext);
|
|
246
|
+
}
|
|
247
|
+
lastIndex = index;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Insert placeholders
|
|
251
|
+
if (i < htmlChunks.length - 1) {
|
|
252
|
+
if (context === HtmlParser.Text)
|
|
253
|
+
tokens.push(commentPlaceholder) // Comment Placeholder. because we can't put text in between <tr> tags for example.
|
|
254
|
+
else
|
|
255
|
+
tokens.push(String.fromCharCode(attribPlaceholder + i));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return tokens.join('');
|
|
260
|
+
}
|
|
237
261
|
|
|
238
262
|
/**
|
|
239
263
|
* We find the path to every embed here once in the Shell, instead of every time a NodeGroup is instantiated.
|
|
@@ -245,36 +269,30 @@ export default class Shell {
|
|
|
245
269
|
* this.staticComponents */
|
|
246
270
|
findEmbeds() {
|
|
247
271
|
this.scripts = Array.prototype.map.call(this.fragment.querySelectorAll('scripts'), el => getNodePath(el))
|
|
272
|
+
|
|
273
|
+
// TODO: only find styles that have ExprPaths in them?
|
|
248
274
|
this.styles = Array.prototype.map.call(this.fragment.querySelectorAll('style'), el => getNodePath(el))
|
|
249
275
|
|
|
250
276
|
let idEls = this.fragment.querySelectorAll('[id],[data-id]');
|
|
251
|
-
|
|
252
277
|
|
|
253
278
|
// Check for valid id names.
|
|
254
279
|
for (let el of idEls) {
|
|
255
280
|
let id = el.getAttribute('data-id') || el.getAttribute('id')
|
|
256
|
-
if (div.hasOwnProperty(id))
|
|
281
|
+
if (Globals.div.hasOwnProperty(id))
|
|
257
282
|
throw new Error(`<${el.tagName.toLowerCase()} id="${id}"> can't override existing HTMLElement id property.`)
|
|
258
283
|
}
|
|
259
284
|
|
|
260
|
-
|
|
261
285
|
this.ids = Array.prototype.map.call(idEls, el => getNodePath(el))
|
|
262
286
|
|
|
263
|
-
// Events (not yet used)
|
|
264
287
|
for (let el of this.fragment.querySelectorAll('*')) {
|
|
265
|
-
for (let attrib of el.attributes)
|
|
266
|
-
if (isEvent(attrib.name))
|
|
267
|
-
this.events.push([attrib.name, getNodePath(el)])
|
|
268
|
-
|
|
269
288
|
if (el.tagName.includes('-') || el.hasAttribute('_is'))
|
|
270
289
|
|
|
271
|
-
// Dynamic components have attributes with expression values.
|
|
290
|
+
// Dynamic components are components that have attributes with expression values.
|
|
272
291
|
// They are created from applyExprs()
|
|
273
292
|
// But static components are created in a separate path inside the NodeGroup constructor.
|
|
274
293
|
if (!this.paths.find(path => path.nodeMarker === el))
|
|
275
294
|
this.staticComponents.push(getNodePath(el));
|
|
276
295
|
}
|
|
277
|
-
|
|
278
296
|
}
|
|
279
297
|
|
|
280
298
|
/**
|
|
@@ -303,3 +321,11 @@ export default class Shell {
|
|
|
303
321
|
//#ENDIF
|
|
304
322
|
}
|
|
305
323
|
|
|
324
|
+
|
|
325
|
+
const commentPlaceholder = `<!--!✨!-->`;
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
// We increment the placeholder char as we go because nodes can't have the same attribute more than once.
|
|
329
|
+
const attribPlaceholder = 0xe000; // https://en.wikipedia.org/wiki/Private_Use_Areas 6400.
|
|
330
|
+
|
|
331
|
+
|
package/src/solarite/Solarite.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* MIT License
|
|
4
4
|
* https://vorticode.github.io/solarite/
|
|
5
5
|
*/
|
|
6
|
+
/** @jsx r */
|
|
7
|
+
/** @jsxFrag null */
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
import createSolarite from "./createSolarite.js";
|
|
@@ -16,10 +18,12 @@ let Solarite = new Proxy(createSolarite(), {
|
|
|
16
18
|
}
|
|
17
19
|
});
|
|
18
20
|
|
|
21
|
+
import h from './h.js';
|
|
22
|
+
export default h;
|
|
19
23
|
|
|
20
24
|
/** @type {HTMLElement|Class} */
|
|
21
25
|
export {Solarite}
|
|
22
|
-
export {default as r} from './
|
|
26
|
+
export {default as h, default as r} from './h.js';
|
|
23
27
|
export {getArg, ArgType} from './getArg.js';
|
|
24
28
|
export {default as Template} from './Template.js';
|
|
25
29
|
export {default as Globals} from './Globals.js';
|
|
@@ -28,7 +32,7 @@ import Util from './Util.js';
|
|
|
28
32
|
let getInputValue = Util.getInputValue;
|
|
29
33
|
export {getInputValue};
|
|
30
34
|
export {default as delve} from '../util/delve.js';
|
|
35
|
+
export {default as SolariteUtil} from './Util.js';
|
|
31
36
|
|
|
32
37
|
//Experimental:
|
|
33
|
-
//export {
|
|
34
|
-
//export {watch} from './watch2.js'; // unfinished
|
|
38
|
+
//export {default as watch, renderWatched} from './watch.js'; // unfinished
|
package/src/solarite/Template.js
CHANGED
|
@@ -18,19 +18,9 @@ export default class Template {
|
|
|
18
18
|
/** @type {Array} Used for toJSON() and getObjectHash(). Stores values used to quickly create a string hash of this template. */
|
|
19
19
|
hashedFields;
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
* @deprecated
|
|
23
|
-
* @type {ExprPath} Used with forEach() from watch.js
|
|
24
|
-
* Set in ExprPath.apply() */
|
|
25
|
-
parentPath;
|
|
26
|
-
|
|
27
21
|
/** @type {NodeGroup} */
|
|
28
22
|
nodeGroup;
|
|
29
23
|
|
|
30
|
-
/**
|
|
31
|
-
* @type {string[][]} */
|
|
32
|
-
paths = [];
|
|
33
|
-
|
|
34
24
|
/**
|
|
35
25
|
*
|
|
36
26
|
* @param htmlStrings {string[]}
|
|
@@ -81,39 +71,54 @@ export default class Template {
|
|
|
81
71
|
if (standalone) {
|
|
82
72
|
ng = new RootNodeGroup(this, null, options);
|
|
83
73
|
el = ng.getRootNode();
|
|
84
|
-
Globals.nodeGroups.set(el, ng);
|
|
74
|
+
Globals.nodeGroups.set(el, ng); // Why was this commented out?
|
|
85
75
|
firstTime = true;
|
|
86
76
|
}
|
|
87
77
|
else {
|
|
88
78
|
ng = Globals.nodeGroups.get(el);
|
|
89
79
|
if (!ng) {
|
|
90
80
|
ng = new RootNodeGroup(this, el, options);
|
|
91
|
-
Globals.nodeGroups.set(el, ng);
|
|
81
|
+
Globals.nodeGroups.set(el, ng); // Why was this commented out?
|
|
92
82
|
firstTime = true;
|
|
93
83
|
}
|
|
94
|
-
|
|
84
|
+
|
|
85
|
+
// This can happen if we try manually rendering one template to a NodeGroup that was created expecting a different template.
|
|
86
|
+
// These don't always have the same length, for example if one attribute has multiple expressions.
|
|
87
|
+
if (ng.paths.length === 0 && this.exprs.length || ng.paths.length > this.exprs.length)
|
|
88
|
+
throw new Error(`Solarite Error: Parent HTMLElement ${ng.template.html.join('${...}')} and ${ng.paths.length} \${value} placeholders can't accomodate a Template with ${this.exprs.length} values.`); }
|
|
95
89
|
|
|
96
90
|
// Creating the root nodegroup also renders it.
|
|
97
91
|
// If we didn't just create it, we need to render it.
|
|
98
92
|
if (!firstTime) {
|
|
99
93
|
if (this.html?.length === 1 && !this.html[0])
|
|
100
94
|
el.innerHTML = ''; // Fast path for empty component.
|
|
101
|
-
else
|
|
95
|
+
else {
|
|
102
96
|
ng.applyExprs(this.exprs);
|
|
97
|
+
}
|
|
103
98
|
}
|
|
104
99
|
|
|
100
|
+
ng.exprsToRender = new Map();
|
|
105
101
|
return el;
|
|
106
102
|
}
|
|
107
103
|
|
|
108
104
|
getExactKey() {
|
|
109
|
-
if (!this.exactKey)
|
|
110
|
-
|
|
105
|
+
if (!this.exactKey) {
|
|
106
|
+
if (this.exprs.length)
|
|
107
|
+
this.exactKey = getObjectHash(this);// calls this.toJSON().
|
|
108
|
+
else // Don't hash plain html.
|
|
109
|
+
this.exactKey = this.html[0];
|
|
110
|
+
}
|
|
111
111
|
return this.exactKey;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
getCloseKey() {
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
//console.log(this.exprs.length)
|
|
116
|
+
if (!this.closeKey) {
|
|
117
|
+
if (this.exprs.length)
|
|
118
|
+
this.closeKey = /*'@' + */this.toJSON()[0];
|
|
119
|
+
else
|
|
120
|
+
this.closeKey = this.html[0];
|
|
121
|
+
}
|
|
117
122
|
// Use the joined html when debugging? But it breaks some tests.
|
|
118
123
|
//return '@'+this.html.join('|')
|
|
119
124
|
|