@solidjs/html 2.0.0-experimental.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016-2023 Ryan Carniato
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Solid Tagged Template Literals
2
+
3
+ This sub module provides a Tagged Template Literal `html` method for Solid. This is useful to use Solid in non-compiled environments. This method can be used as replacement for JSX.
4
+
5
+ `html` uses `${}` to escape into JavaScript expressions. Components are closed with `<//>`
6
+
7
+ ```js
8
+ // create an element with a title attribute
9
+ html`<button title="My button">Click Me</button>`
10
+
11
+ // create a component with a title prop
12
+ html`<${Button} title="My button">Click me<//>`
13
+
14
+ // create an element with dynamic attribute and spread
15
+ html`<div title=${() => selectedClass()} ...${props} />`
16
+ ```
17
+
18
+ Using `html` is slightly less efficient than JSX(but more than HyperScript), requires a larger runtime that isn't treeshakeable, and cannot leverage expression analysis, so it requires manual wrapping of expressions and has a few other caveats (see below).
19
+
20
+ ## Example
21
+
22
+ ```js
23
+ import { render } from "solid-js/web";
24
+ import html from "solid-js/html";
25
+ import { createSignal } from "solid-js";
26
+
27
+ function Button(props) {
28
+ return html`<button class="btn-primary" ...${props} />`;
29
+ }
30
+
31
+ function Counter() {
32
+ const [count, setCount] = createSignal(0);
33
+ const increment = (e) => setCount((c) => c + 1);
34
+
35
+ return html`<${Button} type="button" onClick=${increment}>${count}<//>`;
36
+ }
37
+
38
+ render(Counter, document.getElementById("app"));
39
+ ```
40
+
41
+ ## Differences from JSX
42
+
43
+ There are a few differences from Solid's JSX that are important to note.
44
+
45
+ 1. Reactive expression must be manually wrapped in functions to be reactive.
46
+
47
+ ```js
48
+ // jsx
49
+ <div id={props.id}>{firstName() + lastName()}</div>
50
+
51
+ // hyperscript
52
+ html`<div id=${() => props.id}>${() => firstName() + lastName()}</div>`
53
+ ```
54
+
55
+ 2. Events on components require explicit event in the arguments
56
+
57
+ Solid's Tagged Template Literals automatically wraps functions passed to props of components with no arguments in getters so you need to provide one to prevent this. The same applies to render props like in the `<For>` component.
58
+
59
+ ```js
60
+ // good
61
+ html`<${Button} onClick=${(e) => console.log("Hi")} />`;
62
+
63
+ // bad
64
+ html`<${Button} onClick=${() => console.log("Hi")} />`;
65
+ ```
66
+
67
+ 4. All refs are callback form
68
+
69
+ We can't do the compiled assignment trick so only the callback form is supported.
70
+
71
+ ```js
72
+ let myEl;
73
+ html`<div ref=${(el) => myEl = el} />`;
74
+ ```
75
+
76
+ 5. There can be multiple top level elements
77
+
78
+ No need for fragments just:
79
+ ```js
80
+ html`
81
+ <div>1</div>
82
+ <div>2</div>
83
+ `
84
+ ```
package/dist/html.cjs ADDED
@@ -0,0 +1,587 @@
1
+ 'use strict';
2
+
3
+ var web = require('@solidjs/web');
4
+
5
+ const tagRE = /(?:<!--[\S\s]*?-->|<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>)/g;
6
+ const attrRE = /(?:\s(?<boolean>[^/\s><=]+?)(?=[\s/>]))|(?:(?<name>\S+?)(?:\s*=\s*(?:(['"])(?<quotedValue>[\s\S]*?)\3|(?<unquotedValue>[^\s>]+))))/g;
7
+ const lookup = {
8
+ area: true,
9
+ base: true,
10
+ br: true,
11
+ col: true,
12
+ embed: true,
13
+ hr: true,
14
+ img: true,
15
+ input: true,
16
+ keygen: true,
17
+ link: true,
18
+ menuitem: true,
19
+ meta: true,
20
+ param: true,
21
+ source: true,
22
+ track: true,
23
+ wbr: true
24
+ };
25
+ function parseTag(tag) {
26
+ const res = {
27
+ type: 'tag',
28
+ name: '',
29
+ voidElement: false,
30
+ attrs: [],
31
+ children: []
32
+ };
33
+ const tagMatch = tag.match(/<\/?([^\s]+?)[/\s>]/);
34
+ if (tagMatch) {
35
+ res.name = tagMatch[1];
36
+ if (lookup[tagMatch[1].toLowerCase()] || tag.charAt(tag.length - 2) === '/') {
37
+ res.voidElement = true;
38
+ }
39
+ if (res.name.startsWith('!--')) {
40
+ const endIndex = tag.indexOf('-->');
41
+ return {
42
+ type: 'comment',
43
+ comment: endIndex !== -1 ? tag.slice(4, endIndex) : ''
44
+ };
45
+ }
46
+ }
47
+ const reg = new RegExp(attrRE);
48
+ for (const match of tag.matchAll(reg)) {
49
+ if ((match[1] || match[2]).startsWith('use:')) {
50
+ res.attrs.push({
51
+ type: 'directive',
52
+ name: match[1] || match[2],
53
+ value: match[4] || match[5] || ''
54
+ });
55
+ } else {
56
+ res.attrs.push({
57
+ type: 'attr',
58
+ name: match[1] || match[2],
59
+ value: match[4] || match[5] || ''
60
+ });
61
+ }
62
+ }
63
+ return res;
64
+ }
65
+ function pushTextNode(list, html, start) {
66
+ const end = html.indexOf('<', start);
67
+ const content = html.slice(start, end === -1 ? void 0 : end);
68
+ if (!/^\s*$/.test(content)) {
69
+ list.push({
70
+ type: 'text',
71
+ content: content
72
+ });
73
+ }
74
+ }
75
+ function pushCommentNode(list, tag) {
76
+ const content = tag.replace('<!--', '').replace('-->', '');
77
+ if (!/^\s*$/.test(content)) {
78
+ list.push({
79
+ type: 'comment',
80
+ content: content
81
+ });
82
+ }
83
+ }
84
+ function parse(html) {
85
+ const result = [];
86
+ let current = void 0;
87
+ let level = -1;
88
+ const arr = [];
89
+ const byTag = {};
90
+ html.replace(tagRE, (tag, index) => {
91
+ const isOpen = tag.charAt(1) !== '/';
92
+ const isComment = tag.slice(0, 4) === '<!--';
93
+ const start = index + tag.length;
94
+ const nextChar = html.charAt(start);
95
+ let parent = void 0;
96
+ if (isOpen && !isComment) {
97
+ level++;
98
+ current = parseTag(tag);
99
+ if (!current.voidElement && nextChar && nextChar !== '<') {
100
+ pushTextNode(current.children, html, start);
101
+ }
102
+ byTag[current.tagName] = current;
103
+ if (level === 0) {
104
+ result.push(current);
105
+ }
106
+ parent = arr[level - 1];
107
+ if (parent) {
108
+ parent.children.push(current);
109
+ }
110
+ arr[level] = current;
111
+ }
112
+ if (isComment) {
113
+ if (level < 0) {
114
+ pushCommentNode(result, tag);
115
+ } else {
116
+ pushCommentNode(arr[level].children, tag);
117
+ }
118
+ }
119
+ if (isComment || !isOpen || current.voidElement) {
120
+ if (!isComment) {
121
+ level--;
122
+ }
123
+ if (nextChar !== '<' && nextChar) {
124
+ parent = level === -1 ? result : arr[level].children;
125
+ pushTextNode(parent, html, start);
126
+ }
127
+ }
128
+ });
129
+ return result;
130
+ }
131
+ function attrString(attrs) {
132
+ const buff = [];
133
+ for (const attr of attrs) {
134
+ buff.push(attr.name + '="' + attr.value.replace(/"/g, '&quot;') + '"');
135
+ }
136
+ if (!buff.length) {
137
+ return '';
138
+ }
139
+ return ' ' + buff.join(' ');
140
+ }
141
+ function stringifier(buff, doc) {
142
+ switch (doc.type) {
143
+ case 'text':
144
+ return buff + doc.content;
145
+ case 'tag':
146
+ buff += '<' + doc.name + (doc.attrs ? attrString(doc.attrs) : '') + (doc.voidElement ? '/>' : '>');
147
+ if (doc.voidElement) {
148
+ return buff;
149
+ }
150
+ return buff + doc.children.reduce(stringifier, '') + '</' + doc.name + '>';
151
+ case 'comment':
152
+ return buff += '<!--' + doc.content + '-->';
153
+ }
154
+ }
155
+ function stringify(doc) {
156
+ return doc.reduce(function (token, rootEl) {
157
+ return token + stringifier('', rootEl);
158
+ }, '');
159
+ }
160
+ const cache = new Map();
161
+ const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
162
+ const spaces = " \\f\\n\\r\\t";
163
+ const almostEverything = "[^" + spaces + "\\/>\"'=]+";
164
+ const attrName = "[ " + spaces + "]+(?:use:<!--#-->|" + almostEverything + ")";
165
+ const tagName = "<([A-Za-z$#]+[A-Za-z0-9:_-]*)((?:";
166
+ const attrPartials = "(?:\\s*=\\s*(?:'[^']*?'|\"[^\"]*?\"|\\([^)]*?\\)|<[^>]*?>|" + almostEverything + "))?)";
167
+ const attrSeeker = new RegExp(tagName + attrName + attrPartials + "+)([ " + spaces + "]*/?>)", "g");
168
+ const findAttributes = new RegExp("(" + attrName + "\\s*=\\s*)(<!--#-->|['\"(]([\\w\\s]*<!--#-->[\\w\\s]*)*['\")])", "gi");
169
+ const selfClosing = new RegExp(tagName + attrName + attrPartials + "*)([ " + spaces + "]*/>)", "g");
170
+ const marker = "<!--#-->";
171
+ const reservedNameSpaces = new Set(["class", "on", "style", "use", "prop", "attr"]);
172
+ function attrReplacer($0, $1, $2, $3) {
173
+ return "<" + $1 + $2.replace(findAttributes, replaceAttributes) + $3;
174
+ }
175
+ function replaceAttributes($0, $1, $2) {
176
+ return $1.replace(/<!--#-->/g, "###") + ($2[0] === '"' || $2[0] === "'" ? $2.replace(/<!--#-->/g, "###") : '"###"');
177
+ }
178
+ function fullClosing($0, $1, $2) {
179
+ return VOID_ELEMENTS.test($1) ? $0 : "<" + $1 + $2 + "></" + $1 + ">";
180
+ }
181
+ function parseDirective(name, value, tag, options) {
182
+ if (name === "use:###" && value === "###") {
183
+ const count = options.counter++;
184
+ options.exprs.push(`typeof exprs[${count}] === "function" ? r.use(exprs[${count}], ${tag}, exprs[${options.counter++}]) : (()=>{throw new Error("use:### must be a function")})()`);
185
+ } else {
186
+ throw new Error(`Not support syntax ${name} must be use:{function}`);
187
+ }
188
+ }
189
+ function createHTML(r, {
190
+ delegateEvents = true,
191
+ functionBuilder = (...args) => new Function(...args)
192
+ } = {}) {
193
+ let uuid = 1;
194
+ r.wrapProps = props => {
195
+ const d = Object.getOwnPropertyDescriptors(props);
196
+ for (const k in d) {
197
+ if (typeof d[k].value === "function" && !d[k].value.length) r.dynamicProperty(props, k);
198
+ }
199
+ return props;
200
+ };
201
+ r.resolveFn = fn => typeof fn === "function" ? fn() : fn;
202
+ function createTemplate(statics, opt) {
203
+ let i = 0,
204
+ markup = "";
205
+ for (; i < statics.length - 1; i++) {
206
+ markup = markup + statics[i] + "<!--#-->";
207
+ }
208
+ markup = markup + statics[i];
209
+ const replaceList = [[selfClosing, fullClosing], [/<(<!--#-->)/g, "<###"], [/\.\.\.(<!--#-->)/g, "###"], [attrSeeker, attrReplacer], [/>\n+\s*/g, ">"], [/\n+\s*</g, "<"], [/\s+</g, " <"], [/>\s+/g, "> "]];
210
+ markup = replaceList.reduce((acc, x) => {
211
+ return acc.replace(x[0], x[1]);
212
+ }, markup);
213
+ const pars = parse(markup);
214
+ const [html, code] = parseTemplate(pars, opt.funcBuilder),
215
+ templates = [];
216
+ for (let i = 0; i < html.length; i++) {
217
+ templates.push(document.createElement("template"));
218
+ templates[i].innerHTML = html[i];
219
+ const nomarkers = templates[i].content.querySelectorAll("script,style");
220
+ for (let j = 0; j < nomarkers.length; j++) {
221
+ const d = nomarkers[j].firstChild?.data || "";
222
+ if (d.indexOf(marker) > -1) {
223
+ const parts = d.split(marker).reduce((memo, p, i) => {
224
+ i && memo.push("");
225
+ memo.push(p);
226
+ return memo;
227
+ }, []);
228
+ nomarkers[i].firstChild.replaceWith(...parts);
229
+ }
230
+ }
231
+ }
232
+ templates[0].create = code;
233
+ cache.set(statics, templates);
234
+ return templates;
235
+ }
236
+ function parseKeyValue(node, tag, name, value, isSVG, options) {
237
+ let expr, parts, namespace;
238
+ if (value === "###") {
239
+ expr = `_$v`;
240
+ options.counter++;
241
+ } else {
242
+ const chunks = value.split("###");
243
+ options.counter = chunks.length - 1 + options.counter;
244
+ expr = chunks.map((v, i) => i ? ` + _$v[${i - 1}] + "${v}"` : `"${v}"`).join("");
245
+ }
246
+ if ((parts = name.split(":")) && parts[1] && reservedNameSpaces.has(parts[0])) {
247
+ name = parts[1];
248
+ namespace = parts[0];
249
+ }
250
+ const isChildProp = r.ChildProperties.has(name);
251
+ const isProp = r.Properties.has(name);
252
+ if (name === "style") {
253
+ options.exprs.push(`r.style(${tag},${expr},_$p)`);
254
+ } else if (name === "class") {
255
+ options.exprs.push(`r.className(${tag},${expr},${isSVG},_$p)`);
256
+ } else if (namespace !== "attr" && (isChildProp || !isSVG && (r.getPropAlias(name, node.name.toUpperCase()) || isProp) || namespace === "prop")) {
257
+ options.exprs.push(`${tag}.${r.getPropAlias(name, node.name.toUpperCase()) || name} = ${expr}`);
258
+ } else {
259
+ const ns = isSVG && name.indexOf(":") > -1 && r.SVGNamespace[name.split(":")[0]];
260
+ if (ns) options.exprs.push(`r.setAttributeNS(${tag},"${ns}","${name}",${expr})`);else options.exprs.push(`r.setAttribute(${tag},"${name}",${expr})`);
261
+ }
262
+ }
263
+ function parseAttribute(node, tag, name, value, isSVG, options) {
264
+ if (name.slice(0, 2) === "on") {
265
+ if (!name.includes(":")) {
266
+ const lc = name.slice(2).toLowerCase();
267
+ const delegate = delegateEvents && r.DelegatedEvents.has(lc);
268
+ options.exprs.push(`r.addEventListener(${tag},"${lc}",exprs[${options.counter++}],${delegate})`);
269
+ delegate && options.delegatedEvents.add(lc);
270
+ } else {
271
+ options.exprs.push(`${tag}.addEventListener("${name.slice(3)}",exprs[${options.counter++}])`);
272
+ }
273
+ } else if (name === "ref") {
274
+ options.exprs.push(`exprs[${options.counter++}](${tag})`);
275
+ } else {
276
+ const childOptions = Object.assign({}, options, {
277
+ exprs: []
278
+ }),
279
+ count = options.counter;
280
+ parseKeyValue(node, tag, name, value, isSVG, childOptions);
281
+ options.decl.push(`_fn${count} = (_$v, _$p) => {\n${childOptions.exprs.join(";\n")};\n}`);
282
+ if (value === "###") {
283
+ options.exprs.push(`typeof exprs[${count}] === "function" ? r.effect(() => exprs[${count}](), _fn${count}) : _fn${count}(exprs[${count}])`);
284
+ } else {
285
+ let check = "";
286
+ let list = "";
287
+ let reactiveList = "";
288
+ for (let i = count; i < childOptions.counter; i++) {
289
+ if (i !== count) {
290
+ check += " || ";
291
+ list += ",";
292
+ reactiveList += ",";
293
+ }
294
+ check += `typeof exprs[${i}] === "function"`;
295
+ list += `exprs[${i}]`;
296
+ reactiveList += `r.resolveFn(exprs[${i}])`;
297
+ }
298
+ options.exprs.push(check + ` ? r.effect(() => [${reactiveList}], _fn${count}) : _fn${count}([${list}])`);
299
+ }
300
+ options.counter = childOptions.counter;
301
+ options.wrap = false;
302
+ }
303
+ }
304
+ function processChildren(node, options) {
305
+ const childOptions = Object.assign({}, options, {
306
+ first: true,
307
+ multi: false,
308
+ parent: options.path
309
+ });
310
+ if (node.children.length > 1) {
311
+ for (let i = 0; i < node.children.length; i++) {
312
+ const child = node.children[i];
313
+ if (child.type === "comment" && child.content === "#" || child.type === "tag" && child.name === "###") {
314
+ childOptions.multi = true;
315
+ break;
316
+ }
317
+ }
318
+ }
319
+ let i = 0;
320
+ while (i < node.children.length) {
321
+ const child = node.children[i];
322
+ if (child.name === "###") {
323
+ if (childOptions.multi) {
324
+ node.children[i] = {
325
+ type: "comment",
326
+ content: "#"
327
+ };
328
+ i++;
329
+ } else node.children.splice(i, 1);
330
+ processComponent(child, childOptions);
331
+ continue;
332
+ }
333
+ parseNode(child, childOptions);
334
+ if (!childOptions.multi && child.type === "comment" && child.content === "#") node.children.splice(i, 1);else i++;
335
+ }
336
+ options.counter = childOptions.counter;
337
+ options.templateId = childOptions.templateId;
338
+ options.hasCustomElement = options.hasCustomElement || childOptions.hasCustomElement;
339
+ options.isImportNode = options.isImportNode || childOptions.isImportNode;
340
+ }
341
+ function processComponentProps(propGroups) {
342
+ let result = [];
343
+ for (const props of propGroups) {
344
+ if (Array.isArray(props)) {
345
+ if (!props.length) continue;
346
+ result.push(`r.wrapProps({${props.join(",") || ""}})`);
347
+ } else result.push(props);
348
+ }
349
+ return result.length > 1 ? `r.mergeProps(${result.join(",")})` : result[0];
350
+ }
351
+ function processComponent(node, options) {
352
+ let props = [];
353
+ const keys = Object.keys(node.attrs),
354
+ propGroups = [props],
355
+ componentIdentifier = options.counter++;
356
+ for (let i = 0; i < keys.length; i++) {
357
+ const {
358
+ type,
359
+ name,
360
+ value
361
+ } = node.attrs[i];
362
+ if (type === "attr") {
363
+ if (name === "###") {
364
+ propGroups.push(`exprs[${options.counter++}]`);
365
+ propGroups.push(props = []);
366
+ } else if (value === "###") {
367
+ props.push(`${name}: exprs[${options.counter++}]`);
368
+ } else props.push(`${name}: "${value}"`);
369
+ } else if (type === "directive") {
370
+ const tag = `_$el${uuid++}`;
371
+ const topDecl = !options.decl.length;
372
+ options.decl.push(topDecl ? "" : `${tag} = ${options.path}.${options.first ? "firstChild" : "nextSibling"}`);
373
+ parseDirective(name, value, tag, options);
374
+ }
375
+ }
376
+ if (node.children.length === 1 && node.children[0].type === "comment" && node.children[0].content === "#") {
377
+ props.push(`children: () => exprs[${options.counter++}]`);
378
+ } else if (node.children.length) {
379
+ const children = {
380
+ type: "fragment",
381
+ children: node.children
382
+ },
383
+ childOptions = Object.assign({}, options, {
384
+ first: true,
385
+ decl: [],
386
+ exprs: [],
387
+ parent: false
388
+ });
389
+ parseNode(children, childOptions);
390
+ props.push(`children: () => { ${childOptions.exprs.join(";\n")}}`);
391
+ options.templateId = childOptions.templateId;
392
+ options.counter = childOptions.counter;
393
+ }
394
+ let tag;
395
+ if (options.multi) {
396
+ tag = `_$el${uuid++}`;
397
+ options.decl.push(`${tag} = ${options.path}.${options.first ? "firstChild" : "nextSibling"}`);
398
+ }
399
+ if (options.parent) options.exprs.push(`r.insert(${options.parent}, r.createComponent(exprs[${componentIdentifier}],${processComponentProps(propGroups)})${tag ? `, ${tag}` : ""})`);else options.exprs.push(`${options.fragment ? "" : "return "}r.createComponent(exprs[${componentIdentifier}],${processComponentProps(propGroups)})`);
400
+ options.path = tag;
401
+ options.first = false;
402
+ }
403
+ function parseNode(node, options) {
404
+ if (node.type === "fragment") {
405
+ const parts = [];
406
+ node.children.forEach(child => {
407
+ if (child.type === "tag") {
408
+ if (child.name === "###") {
409
+ const childOptions = Object.assign({}, options, {
410
+ first: true,
411
+ fragment: true,
412
+ decl: [],
413
+ exprs: []
414
+ });
415
+ processComponent(child, childOptions);
416
+ parts.push(childOptions.exprs[0]);
417
+ options.counter = childOptions.counter;
418
+ options.templateId = childOptions.templateId;
419
+ return;
420
+ }
421
+ options.templateId++;
422
+ const id = uuid;
423
+ const childOptions = Object.assign({}, options, {
424
+ first: true,
425
+ decl: [],
426
+ exprs: []
427
+ });
428
+ options.templateNodes.push([child]);
429
+ parseNode(child, childOptions);
430
+ parts.push(`function() { ${childOptions.decl.join(",\n") + ";\n" + childOptions.exprs.join(";\n") + `;\nreturn _$el${id};\n`}}()`);
431
+ options.counter = childOptions.counter;
432
+ options.templateId = childOptions.templateId;
433
+ } else if (child.type === "text") {
434
+ parts.push(`"${child.content}"`);
435
+ } else if (child.type === "comment") {
436
+ if (child.content === "#") parts.push(`exprs[${options.counter++}]`);else if (child.content) {
437
+ for (let i = 0; i < child.content.split("###").length - 1; i++) {
438
+ parts.push(`exprs[${options.counter++}]`);
439
+ }
440
+ }
441
+ }
442
+ });
443
+ options.exprs.push(`return [${parts.join(", \n")}]`);
444
+ } else if (node.type === "tag") {
445
+ const tag = `_$el${uuid++}`;
446
+ const topDecl = !options.decl.length;
447
+ const templateId = options.templateId;
448
+ options.decl.push(topDecl ? "" : `${tag} = ${options.path}.${options.first ? "firstChild" : "nextSibling"}`);
449
+ const isSVG = r.SVGElements.has(node.name);
450
+ options.hasCustomElement = node.name.includes("-") || node.attrs.some(e => e.name === "is");
451
+ options.isImportNode = (node.name === "img" || node.name === "iframe") && node.attrs.some(e => e.name === "loading" && e.value === "lazy");
452
+ if (node.attrs.some(e => e.name === "###")) {
453
+ const spreadArgs = [];
454
+ let current = "";
455
+ const newAttrs = [];
456
+ for (let i = 0; i < node.attrs.length; i++) {
457
+ const {
458
+ type,
459
+ name,
460
+ value
461
+ } = node.attrs[i];
462
+ if (type === "attr") {
463
+ if (value.includes("###")) {
464
+ let count = options.counter++;
465
+ current += `${name}: ${name !== "ref" ? `typeof exprs[${count}] === "function" ? exprs[${count}]() : ` : ""}exprs[${count}],`;
466
+ } else if (name === "###") {
467
+ if (current.length) {
468
+ spreadArgs.push(`()=>({${current}})`);
469
+ current = "";
470
+ }
471
+ spreadArgs.push(`exprs[${options.counter++}]`);
472
+ } else {
473
+ newAttrs.push(node.attrs[i]);
474
+ }
475
+ } else if (type === "directive") {
476
+ parseDirective(name, value, tag, options);
477
+ }
478
+ }
479
+ node.attrs = newAttrs;
480
+ if (current.length) {
481
+ spreadArgs.push(`()=>({${current}})`);
482
+ }
483
+ options.exprs.push(`r.spread(${tag},${spreadArgs.length === 1 ? `typeof ${spreadArgs[0]} === "function" ? r.mergeProps(${spreadArgs[0]}) : ${spreadArgs[0]}` : `r.mergeProps(${spreadArgs.join(",")})`},${isSVG},${!!node.children.length})`);
484
+ } else {
485
+ for (let i = 0; i < node.attrs.length; i++) {
486
+ const {
487
+ type,
488
+ name,
489
+ value
490
+ } = node.attrs[i];
491
+ if (type === "directive") {
492
+ parseDirective(name, value, tag, options);
493
+ node.attrs.splice(i, 1);
494
+ i--;
495
+ } else if (type === "attr") {
496
+ if (value.includes("###")) {
497
+ node.attrs.splice(i, 1);
498
+ i--;
499
+ parseAttribute(node, tag, name, value, isSVG, options);
500
+ }
501
+ }
502
+ }
503
+ }
504
+ options.path = tag;
505
+ options.first = false;
506
+ processChildren(node, options);
507
+ if (topDecl) {
508
+ options.decl[0] = options.hasCustomElement || options.isImportNode ? `const ${tag} = r.untrack(() => document.importNode(tmpls[${templateId}].content.firstChild, true))` : `const ${tag} = tmpls[${templateId}].content.firstChild.cloneNode(true)`;
509
+ }
510
+ } else if (node.type === "text") {
511
+ const tag = `_$el${uuid++}`;
512
+ options.decl.push(`${tag} = ${options.path}.${options.first ? "firstChild" : "nextSibling"}`);
513
+ options.path = tag;
514
+ options.first = false;
515
+ } else if (node.type === "comment") {
516
+ const tag = `_$el${uuid++}`;
517
+ options.decl.push(`${tag} = ${options.path}.${options.first ? "firstChild" : "nextSibling"}`);
518
+ if (node.content === "#") {
519
+ if (options.multi) {
520
+ options.exprs.push(`r.insert(${options.parent}, exprs[${options.counter++}], ${tag})`);
521
+ } else options.exprs.push(`r.insert(${options.parent}, exprs[${options.counter++}])`);
522
+ }
523
+ options.path = tag;
524
+ options.first = false;
525
+ }
526
+ }
527
+ function parseTemplate(nodes, funcBuilder) {
528
+ const options = {
529
+ path: "",
530
+ decl: [],
531
+ exprs: [],
532
+ delegatedEvents: new Set(),
533
+ counter: 0,
534
+ first: true,
535
+ multi: false,
536
+ templateId: 0,
537
+ templateNodes: []
538
+ },
539
+ id = uuid,
540
+ origNodes = nodes;
541
+ let toplevel;
542
+ if (nodes.length > 1) {
543
+ nodes = [{
544
+ type: "fragment",
545
+ children: nodes
546
+ }];
547
+ }
548
+ if (nodes[0].name === "###") {
549
+ toplevel = true;
550
+ processComponent(nodes[0], options);
551
+ } else parseNode(nodes[0], options);
552
+ r.delegateEvents(Array.from(options.delegatedEvents));
553
+ const templateNodes = [origNodes].concat(options.templateNodes);
554
+ return [templateNodes.map(t => stringify(t)), funcBuilder("tmpls", "exprs", "r", options.decl.join(",\n") + ";\n" + options.exprs.join(";\n") + (toplevel ? "" : `;\nreturn _$el${id};\n`))];
555
+ }
556
+ function html(statics, ...args) {
557
+ const templates = cache.get(statics) || createTemplate(statics, {
558
+ funcBuilder: functionBuilder
559
+ });
560
+ return templates[0].create(templates, args, r);
561
+ }
562
+ return html;
563
+ }
564
+
565
+ const html = createHTML({
566
+ effect: web.effect,
567
+ style: web.style,
568
+ insert: web.insert,
569
+ untrack: web.untrack,
570
+ spread: web.spread,
571
+ createComponent: web.createComponent,
572
+ delegateEvents: web.delegateEvents,
573
+ className: web.className,
574
+ mergeProps: web.mergeProps,
575
+ dynamicProperty: web.dynamicProperty,
576
+ setAttribute: web.setAttribute,
577
+ setAttributeNS: web.setAttributeNS,
578
+ addEventListener: web.addEventListener,
579
+ getPropAlias: web.getPropAlias,
580
+ Properties: web.Properties,
581
+ ChildProperties: web.ChildProperties,
582
+ DelegatedEvents: web.DelegatedEvents,
583
+ SVGElements: web.SVGElements,
584
+ SVGNamespace: web.SVGNamespace
585
+ });
586
+
587
+ module.exports = html;