@webtides/element-js 1.0.0-alpha.9 → 1.1.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/CHANGELOG.md +369 -0
- package/README.md +3 -3
- package/docs/README.md +70 -89
- package/docs/concepts/classes.md +37 -66
- package/docs/concepts/hierarchy.md +29 -31
- package/docs/concepts/lifecycle.md +9 -23
- package/docs/concepts/reactivity.md +1 -3
- package/docs/features/computed-properties.md +13 -16
- package/docs/features/dependency-injection-context-protocol.md +75 -87
- package/docs/features/global-styles.md +1 -1
- package/docs/features/methods.md +14 -15
- package/docs/features/properties.md +21 -33
- package/docs/features/refs.md +23 -31
- package/docs/features/renderless.md +31 -37
- package/docs/features/store.md +63 -78
- package/docs/features/templates.md +4 -1
- package/docs/features/watch.md +28 -35
- package/docs/getting-started/upgrade-to-v1.md +16 -0
- package/docs/guide/guide.md +10 -22
- package/index.js +29 -2
- package/package.json +62 -62
- package/src/BaseElement.js +637 -632
- package/src/StyledElement.js +104 -104
- package/src/TemplateElement.js +53 -58
- package/src/dom-parts/AttributePart.js +116 -116
- package/src/dom-parts/ChildNodePart.js +288 -288
- package/src/dom-parts/NodePart.js +27 -27
- package/src/dom-parts/Part.js +6 -6
- package/src/dom-parts/TemplatePart.js +119 -119
- package/src/dom-parts/TemplateResult.js +274 -267
- package/src/dom-parts/directives.js +105 -48
- package/src/dom-parts/html.js +2 -2
- package/src/dom-parts/render.js +6 -1
- package/src/util/AttributeParser.js +47 -47
- package/src/util/DOMHelper.js +190 -191
- package/src/util/Directive.js +31 -24
- package/src/util/GlobalStylesStore.js +56 -49
- package/src/util/SerializeStateHelper.js +56 -56
- package/src/util/Store.js +174 -174
- package/src/util/defineElement.js +5 -5
- package/src/util/toString.js +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { COMMENT_NODE, getNodePath } from '../util/DOMHelper';
|
|
1
|
+
import { COMMENT_NODE, getNodePath } from '../util/DOMHelper.js';
|
|
2
2
|
import { encodeAttribute, isObjectLike } from '../util/AttributeParser.js';
|
|
3
3
|
import { TemplatePart } from './TemplatePart.js';
|
|
4
4
|
|
|
@@ -22,52 +22,50 @@ const interpolation = new RegExp(`(<!--dom-part-(\\d+)--><!--/dom-part-(\\d+)-->
|
|
|
22
22
|
* @returns {string} X/HTML with prefixed comments or attributes
|
|
23
23
|
*/
|
|
24
24
|
export const createTemplateString = (templateStrings, attributePlaceholders = '') => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
// TODO: make it easier to identify attribute and node parts for SSR and leave the comments at those positions to be replaced in toString()
|
|
26
|
+
let partIndex = 0;
|
|
27
|
+
// join all interpolations (for values) with a special placeholder and remove any whitespace
|
|
28
|
+
let template = templateStrings.join('\x01').trim();
|
|
29
|
+
// find (match) all elements to identify their attributes
|
|
30
|
+
template = template.replace(elements, (_, name, attributesString, trailingSlash) => {
|
|
31
|
+
let elementTagWithAttributes = name + attributesString.replaceAll('\x01', attributePlaceholders).trimEnd();
|
|
32
|
+
// TODO the closing case is weird.
|
|
33
|
+
if (trailingSlash.length) elementTagWithAttributes += voidElements.test(name) ? ' /' : '></' + name;
|
|
34
|
+
// collect all attribute parts so that we can place matching comment nodes
|
|
35
|
+
const attributeParts = attributesString.replace(attributes, (attribute, name, valueWithQuotes, directive) => {
|
|
36
|
+
if (directive && directive === '\x01') {
|
|
37
|
+
return `<!--\x02$-->`;
|
|
38
|
+
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<!--/template-part-->
|
|
69
|
-
`;
|
|
70
|
-
// TODO: ^ this is actually important because of the whitespace
|
|
40
|
+
// remove quotes from attribute value to normalize the value
|
|
41
|
+
const value =
|
|
42
|
+
valueWithQuotes?.startsWith('"') || valueWithQuotes?.startsWith("'")
|
|
43
|
+
? valueWithQuotes.slice(1, -1)
|
|
44
|
+
: valueWithQuotes;
|
|
45
|
+
const partsCount = (attribute.match(/\x01/g) || []).length;
|
|
46
|
+
const parts = [];
|
|
47
|
+
for (let index = 0; index < partsCount; index++) {
|
|
48
|
+
parts.push(`<!--\x02:${name}=${value.replaceAll('\x01', '\x03')}-->`);
|
|
49
|
+
}
|
|
50
|
+
return parts.join('');
|
|
51
|
+
});
|
|
52
|
+
// TODO create test to check if lf are in place
|
|
53
|
+
return `
|
|
54
|
+
${attributesString.includes('\x01') ? attributeParts : ''}\n
|
|
55
|
+
<${elementTagWithAttributes}>\n
|
|
56
|
+
`.trim();
|
|
57
|
+
});
|
|
58
|
+
// replace interpolation placeholders with our indexed markers
|
|
59
|
+
template = template.replace(partPositions, (partPosition) => {
|
|
60
|
+
if (partPosition === '\x01') {
|
|
61
|
+
return `<!--dom-part-${partIndex}--><!--/dom-part-${partIndex++}-->`;
|
|
62
|
+
} else if (partPosition === '\x02') {
|
|
63
|
+
return `dom-part-${partIndex++}`;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// TODO create test to check if lf are in place
|
|
67
|
+
// /n is important in the returns as we expect a certain order of text / comments an nodes
|
|
68
|
+
return `<!--template-part-->\n${template}\n<!--/template-part-->`.trim();
|
|
71
69
|
};
|
|
72
70
|
|
|
73
71
|
/**
|
|
@@ -77,9 +75,9 @@ export const createTemplateString = (templateStrings, attributePlaceholders = ''
|
|
|
77
75
|
* @return {String}
|
|
78
76
|
*/
|
|
79
77
|
const attribute = (name, value, isSingleValue = true) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
return ` ${name}="${encodeAttribute(isObjectLike(value) ? JSON.stringify(value) : value)}${
|
|
79
|
+
isSingleValue ? '"' : ''
|
|
80
|
+
}`;
|
|
83
81
|
};
|
|
84
82
|
|
|
85
83
|
/**
|
|
@@ -87,26 +85,30 @@ const attribute = (name, value, isSingleValue = true) => {
|
|
|
87
85
|
* @return {String}
|
|
88
86
|
*/
|
|
89
87
|
const getValue = (value) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
88
|
+
switch (typeof value) {
|
|
89
|
+
case 'string':
|
|
90
|
+
return encodeAttribute(value);
|
|
91
|
+
case 'boolean':
|
|
92
|
+
case 'number':
|
|
93
|
+
return String(value);
|
|
94
|
+
case 'object':
|
|
95
|
+
switch (true) {
|
|
96
|
+
case value === null:
|
|
97
|
+
return '';
|
|
98
|
+
case Array.isArray(value):
|
|
99
|
+
return value.map(getValue).join('');
|
|
100
|
+
case value instanceof TemplateResult:
|
|
101
|
+
return value.toString();
|
|
102
|
+
case value.__unsafeHTML:
|
|
103
|
+
return value.string;
|
|
104
|
+
default:
|
|
105
|
+
console.log('Cannot getValue for', value);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
case 'function':
|
|
109
|
+
return getValue(value());
|
|
110
|
+
}
|
|
111
|
+
return value == null ? '' : encodeAttribute(String(value));
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
/** @type {Map<TemplateStringsArray, *[]>} */
|
|
@@ -116,218 +118,223 @@ const parsedUpdates = new WeakMap();
|
|
|
116
118
|
const templateParts = new WeakMap();
|
|
117
119
|
|
|
118
120
|
export class TemplateResult {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
/**
|
|
122
|
+
* @param {TemplateStringsArray} strings
|
|
123
|
+
* @param {any[]} values
|
|
124
|
+
*/
|
|
125
|
+
constructor(strings, ...values) {
|
|
126
|
+
this.strings = strings;
|
|
127
|
+
this.values = values;
|
|
128
|
+
}
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
/**
|
|
131
|
+
* @param {Element} domNode
|
|
132
|
+
*/
|
|
133
|
+
renderInto(domNode) {
|
|
134
|
+
let serverSideRendered = false;
|
|
135
|
+
let templatePart = templateParts.get(domNode);
|
|
136
|
+
if (!templatePart) {
|
|
137
|
+
const startNode = Array.from(domNode.childNodes)
|
|
138
|
+
.filter((node) => node.nodeType === COMMENT_NODE)
|
|
139
|
+
.find((node) => node.data === 'template-part');
|
|
138
140
|
|
|
139
|
-
|
|
141
|
+
serverSideRendered = startNode !== undefined;
|
|
140
142
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
templatePart = new TemplatePart(startNode, this);
|
|
144
|
+
templateParts.set(domNode, templatePart);
|
|
143
145
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
if (!serverSideRendered) {
|
|
147
|
+
domNode.replaceChildren(...templatePart.childNodes);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
templatePart.update(this);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
151
153
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
154
|
+
/**
|
|
155
|
+
* @param {String[]} strings
|
|
156
|
+
* @return {(() => String)[]}
|
|
157
|
+
*/
|
|
158
|
+
parse(strings) {
|
|
159
|
+
const html = createTemplateString(strings, '\x04');
|
|
160
|
+
const parts = [];
|
|
161
|
+
let i = 0;
|
|
162
|
+
let match = null;
|
|
163
|
+
while ((match = interpolation.exec(html))) {
|
|
164
|
+
const pre = html.slice(i, match.index);
|
|
165
|
+
i = match.index + match[0].length;
|
|
166
|
+
if (match[2]) {
|
|
167
|
+
// ChildNodePart
|
|
168
|
+
const index = match[2];
|
|
169
|
+
parts.push((value) => `${pre}<!--dom-part-${index}-->${getValue(value)}<!--/dom-part-${index}-->`);
|
|
170
|
+
} else if (match[4]) {
|
|
171
|
+
// AttributePart with single interpolation or the first interpolation right after the attribute=
|
|
172
|
+
const isSingleValue = match[5] !== undefined;
|
|
173
|
+
let name = match[4];
|
|
174
|
+
switch (true) {
|
|
175
|
+
case name[0] === '?':
|
|
176
|
+
const booleanName = name.slice(1).toLowerCase();
|
|
177
|
+
parts.push((value) => {
|
|
178
|
+
if (!value) return '';
|
|
179
|
+
return `${pre} ${booleanName}=""`;
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
case name[0] === '.':
|
|
183
|
+
const lower = name.slice(1).toLowerCase();
|
|
184
|
+
parts.push((value) => {
|
|
185
|
+
let result = pre;
|
|
186
|
+
// null, undefined, and false are not shown at all
|
|
187
|
+
if (value === null || value === undefined || value === '') {
|
|
188
|
+
result += attribute(lower, '');
|
|
189
|
+
} else {
|
|
190
|
+
// in all other cases, just escape it in quotes
|
|
191
|
+
result += attribute(lower, value, isSingleValue);
|
|
192
|
+
}
|
|
193
|
+
return result;
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
case name[0] === '@':
|
|
197
|
+
name = 'on' + name.slice(1);
|
|
198
|
+
case name[0] === 'o' && name[1] === 'n':
|
|
199
|
+
parts.push((value) => {
|
|
200
|
+
return pre;
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
default:
|
|
204
|
+
parts.push((value) => {
|
|
205
|
+
let result = pre;
|
|
206
|
+
if (value != null) {
|
|
207
|
+
result += attribute(name, value, isSingleValue);
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
});
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
// AttributePart in the middle of an attribute value or NodePart
|
|
215
|
+
parts.push((value) => {
|
|
216
|
+
let result = pre;
|
|
217
|
+
// TODO: we currently cannot distinguish between NodeParts and AttributeParts before
|
|
218
|
+
if (isObjectLike(value) && value.directiveClass) {
|
|
219
|
+
// NodePart
|
|
220
|
+
const directive = new value.directiveClass();
|
|
221
|
+
result += directive.stringify(...value.values);
|
|
222
|
+
} else if (value != null) {
|
|
223
|
+
result += encodeAttribute(isObjectLike(value) ? JSON.stringify(value) : value);
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
227
229
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
230
|
+
// We couldn't correctly parse parts from the template
|
|
231
|
+
if (parts.length !== strings.length - 1) {
|
|
232
|
+
throw {
|
|
233
|
+
name: 'ParseTemplateError',
|
|
234
|
+
message: 'Could not parse parts from template correctly. Parts length has not the expected length.',
|
|
235
|
+
strings,
|
|
236
|
+
templateString: html,
|
|
237
|
+
parts,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
238
240
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
241
|
+
if (parts.length) {
|
|
242
|
+
const last = parts[parts.length - 1];
|
|
243
|
+
const chunk = html.slice(i);
|
|
244
|
+
parts[parts.length - 1] = (value) => last(value) + chunk;
|
|
245
|
+
} else {
|
|
246
|
+
parts.push(() => html);
|
|
247
|
+
}
|
|
248
|
+
return parts;
|
|
249
|
+
}
|
|
248
250
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
251
|
+
/**
|
|
252
|
+
* @param {Node[]} childNodes
|
|
253
|
+
* @return {object[]}
|
|
254
|
+
*/
|
|
255
|
+
parseParts(childNodes) {
|
|
256
|
+
// we always create a template fragment so that we can start at the root for traversing the node path
|
|
257
|
+
const range = globalThis.document?.createRange();
|
|
258
|
+
range.setStartBefore(childNodes[0]);
|
|
259
|
+
range.setEndAfter(childNodes[childNodes.length - 1]);
|
|
260
|
+
const template = range.cloneContents();
|
|
259
261
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
262
|
+
const treeWalker = globalThis.document?.createTreeWalker(template, 128);
|
|
263
|
+
let node = treeWalker.nextNode(); //to skip the root template-part
|
|
264
|
+
const parts = [];
|
|
263
265
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
266
|
+
let nestedLevel = 0;
|
|
267
|
+
// search for parts through numbered comment nodes with placeholders
|
|
268
|
+
while ((node = treeWalker.nextNode())) {
|
|
269
|
+
if (/^template-part$/.test(node.data)) {
|
|
270
|
+
nestedLevel++;
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (/^\/template-part$/.test(node.data)) {
|
|
274
|
+
if (nestedLevel > 0) {
|
|
275
|
+
nestedLevel--;
|
|
276
|
+
}
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (nestedLevel > 0) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (/^dom-part-\d+$/.test(node.data)) {
|
|
283
|
+
parts.push({ type: 'node', path: getNodePath(node) });
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (/^dom-part-\d+:/.test(node.data)) {
|
|
287
|
+
const [_, ...attribute] = node.data.split(':');
|
|
288
|
+
const [name, ...initialValue] = attribute.join(':').split('=');
|
|
289
|
+
parts.push({
|
|
290
|
+
type: 'attribute',
|
|
291
|
+
path: getNodePath(node),
|
|
292
|
+
name: name,
|
|
293
|
+
initialValue: initialValue.join('='),
|
|
294
|
+
});
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (/^dom-part-\d+\$/.test(node.data)) {
|
|
298
|
+
parts.push({ type: 'directive', path: getNodePath(node) });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
294
301
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
302
|
+
// We couldn't correctly parse parts from the template
|
|
303
|
+
if (parts.length !== this.strings.length - 1) {
|
|
304
|
+
throw {
|
|
305
|
+
name: 'ParseTemplateError',
|
|
306
|
+
message:
|
|
307
|
+
'Could not parse parts from template correctly. Parts length has not the expected length. -> ' +
|
|
308
|
+
JSON.stringify({
|
|
309
|
+
strings: this.strings,
|
|
310
|
+
templateString: this.templateString,
|
|
311
|
+
childNodes,
|
|
312
|
+
template,
|
|
313
|
+
parts,
|
|
314
|
+
}),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
310
317
|
|
|
311
|
-
|
|
312
|
-
|
|
318
|
+
return parts;
|
|
319
|
+
}
|
|
313
320
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
321
|
+
/**
|
|
322
|
+
* find interpolations in the given template for nodes and attributes and
|
|
323
|
+
* return a string with placeholders as either comment nodes or named attributes.
|
|
324
|
+
* @returns {string} template with tagged placeholders for values
|
|
325
|
+
*/
|
|
326
|
+
get templateString() {
|
|
327
|
+
return createTemplateString(this.strings);
|
|
328
|
+
}
|
|
322
329
|
|
|
323
|
-
|
|
324
|
-
|
|
330
|
+
toString() {
|
|
331
|
+
let updates = parsedUpdates.get(this.strings);
|
|
325
332
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
333
|
+
if (!updates) {
|
|
334
|
+
updates = this.parse(this.strings);
|
|
335
|
+
parsedUpdates.set(this.strings, updates);
|
|
336
|
+
}
|
|
330
337
|
|
|
331
|
-
|
|
332
|
-
|
|
338
|
+
return this.values.length ? this.values.map((value, index) => updates[index](value)).join('') : updates[0]();
|
|
339
|
+
}
|
|
333
340
|
}
|