@webtides/element-js 1.0.0 → 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 +140 -90
- 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 +631 -631
- package/src/StyledElement.js +104 -104
- package/src/TemplateElement.js +53 -53
- 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 +270 -270
- package/src/dom-parts/directives.js +105 -48
- package/src/dom-parts/html.js +1 -1
- package/src/dom-parts/render.js +6 -6
- package/src/util/AttributeParser.js +47 -47
- package/src/util/DOMHelper.js +190 -190
- package/src/util/Directive.js +31 -31
- package/src/util/GlobalStylesStore.js +56 -53
- 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
|
@@ -22,50 +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
|
-
|
|
38
|
-
|
|
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
|
+
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
54
|
${attributesString.includes('\x01') ? attributeParts : ''}\n
|
|
55
55
|
<${elementTagWithAttributes}>\n
|
|
56
56
|
`.trim();
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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();
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
/**
|
|
@@ -75,9 +75,9 @@ export const createTemplateString = (templateStrings, attributePlaceholders = ''
|
|
|
75
75
|
* @return {String}
|
|
76
76
|
*/
|
|
77
77
|
const attribute = (name, value, isSingleValue = true) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
return ` ${name}="${encodeAttribute(isObjectLike(value) ? JSON.stringify(value) : value)}${
|
|
79
|
+
isSingleValue ? '"' : ''
|
|
80
|
+
}`;
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
/**
|
|
@@ -85,30 +85,30 @@ const attribute = (name, value, isSingleValue = true) => {
|
|
|
85
85
|
* @return {String}
|
|
86
86
|
*/
|
|
87
87
|
const getValue = (value) => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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));
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
/** @type {Map<TemplateStringsArray, *[]>} */
|
|
@@ -118,223 +118,223 @@ const parsedUpdates = new WeakMap();
|
|
|
118
118
|
const templateParts = new WeakMap();
|
|
119
119
|
|
|
120
120
|
export class TemplateResult {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
/**
|
|
122
|
+
* @param {TemplateStringsArray} strings
|
|
123
|
+
* @param {any[]} values
|
|
124
|
+
*/
|
|
125
|
+
constructor(strings, ...values) {
|
|
126
|
+
this.strings = strings;
|
|
127
|
+
this.values = values;
|
|
128
|
+
}
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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');
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
serverSideRendered = startNode !== undefined;
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
templatePart = new TemplatePart(startNode, this);
|
|
144
|
+
templateParts.set(domNode, templatePart);
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
146
|
+
if (!serverSideRendered) {
|
|
147
|
+
domNode.replaceChildren(...templatePart.childNodes);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
templatePart.update(this);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
+
}
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
+
}
|
|
240
240
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
+
}
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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();
|
|
261
261
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
const treeWalker = globalThis.document?.createTreeWalker(template, 128);
|
|
263
|
+
let node = treeWalker.nextNode(); //to skip the root template-part
|
|
264
|
+
const parts = [];
|
|
265
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
+
}
|
|
301
301
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
+
}
|
|
317
317
|
|
|
318
|
-
|
|
319
|
-
|
|
318
|
+
return parts;
|
|
319
|
+
}
|
|
320
320
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
+
}
|
|
329
329
|
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
toString() {
|
|
331
|
+
let updates = parsedUpdates.get(this.strings);
|
|
332
332
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
333
|
+
if (!updates) {
|
|
334
|
+
updates = this.parse(this.strings);
|
|
335
|
+
parsedUpdates.set(this.strings, updates);
|
|
336
|
+
}
|
|
337
337
|
|
|
338
|
-
|
|
339
|
-
|
|
338
|
+
return this.values.length ? this.values.map((value, index) => updates[index](value)).join('') : updates[0]();
|
|
339
|
+
}
|
|
340
340
|
}
|