@solidjs/html 2.0.0-rc.2 → 2.0.0-rc.3

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/html.js CHANGED
@@ -1,18 +1,525 @@
1
- import { RawTextElements, VoidElements, MathMLElements, SVGElements, claimElement, mergeProps, createComponent, spread, insert } from '@solidjs/web';
1
+ import { RawTextElements, spread, claimElement, createComponent, insert, mergeProps, SVGElements, MathMLElements, VoidElements } from '@solidjs/web';
2
2
 
3
- const e=e=>t(e)||e>=48&&e<=58||e===46||e===45,t=e=>e>=65&&e<=90||e>=97&&e<=122||e===95||e===36,n=e=>e>=9&&e<=13||e===32,r=(r,i)=>{let a=[],o=0,s=``,c=0;for(let l=0;l<r.length;l++){let u=r[l],d=u.length;for(c=0;c<d;)switch(o){case 0:{s=``;let e=u.indexOf(`<`,c);e===-1?(c<d&&a.push({type:6,value:u.slice(c)}),c=d):(e>c&&a.push({type:6,value:u.slice(c,e)}),u[e+1]===`!`&&u[e+2]===`-`&&u[e+3]===`-`?(o=3,c=e+4):(a.push({type:0}),o=1,c=e+1));break}case 1:{let r=u.charCodeAt(c);if(n(r))c++;else if(r===62)i.has(s)&&a[a.length-2]?.type!==2?o=2:(o=0,s=``),a.push({type:1}),c++;else if(r===61)a.push({type:4}),c++;else if(r===47){let e=u.charCodeAt(c+1),t=u.slice(c+2).search(/\S/),n=e===47&&a[a.length-1]?.type===0&&t!==-1&&u[c+2+t]===`>`;e===47&&!n?o=4:e===42?o=5:(a.push({type:2}),c++);}else if(r===34||r===39){let e=u[c],t=u.indexOf(e,c+1);if(t===-1)throw Error(`Unterminated string at ${l}:${c}`);a.push({type:5,value:u.slice(c+1,t),quote:e}),c=t+1;}else if(t(r)){let t=c;for(;c<d&&e(u.charCodeAt(c));)c++;let n=u.slice(t,c);s===``&&(s=n),a.push({type:3,value:n});}else if(r===46&&u[c+1]===`.`&&u[c+2]===`.`)a.push({type:8}),c+=3;else throw Error(`Unexpected Character: ${u[c]} at ${l}:${c}`);break}case 2:{let e=RegExp(`<\\s*/\\s*${s}\\s*>`,`g`);e.lastIndex=c;let t=e.exec(u);if(t){let e=t.index;e>c&&a.push({type:6,value:u.slice(c,e)}),o=0,c=e,s=``;}else a.push({type:6,value:u.slice(c)}),c=d;break}case 3:case 4:case 5:{let e=o===4?`
4
- `:o===5?`*/`:`-->`,t=u.indexOf(e,c);t===-1?c=d:(o=o===3?0:1,c=t+e.length);break}}l<r.length-1&&(o===0||o===1||o===2)&&a.push({type:7,value:l});}return a},i=e=>{let t=e.charCodeAt(0);return t>=65&&t<=90},a=(e,t)=>{let n={type:0,children:[]},r=[n],a=0,o=e.length;for(;a<o;){let n=e[a],s=r[r.length-1];switch(n.type){case 6:{let t=n.value;if(t.trim()===``){let t=e[a-1]?.type,n=e[a+1]?.type;if(t===1||n===0){a++;continue}}s.children.push({type:3,value:t}),a++;continue}case 7:s.children.push({type:4,value:n.value}),a++;continue;case 0:{let n=e[++a];if(n.type===2){let n=e[++a],i=e[++a],o=r[r.length-1];if(r.length>1&&i.type===1&&(n?.type===3&&o.name===n.value||(n?.type===7||n.type===2)&&typeof o.name==`number`)){let e=r.pop();e?.type===1&&t.has(e.name)&&(e.children=[]),a++;continue}throw Error(`Mismatched closing tag for <${o.name}>`)}if(n.type===3||n.type===7){let t=n.value,c={type:typeof t==`number`||i(t)?2:1,name:t,props:[],children:[]};for(s.children.push(c),a++;a<o;){let t=e[a];if(t.type===1||t.type===2)break;if(t.type===8){let t=e[a+1];if(t?.type===7)c.props.push({type:3,value:t.value}),a+=2;else throw Error(`Spread operator in <${c.name}> must be followed by an expression`)}else if(t.type===3){let n=t.value;if(e[a+1]?.type===4){a+=2;let t=e[a];if(t.type===7)c.props.push({name:n,type:2,value:t.value}),a++;else if(t.type===5){let e=t.quote;c.props.push({name:n,value:t.value,quote:e,type:1}),a++;}else throw Error(`Attribute value for "${n}" in <${c.name}> must be an expression or a string`)}else c.props.push({type:0,name:n,value:true}),a++;}else throw Error(`Invalid attribute in <${c.name}>`)}let l=e[a];l.type===2?a+=2:l.type===1&&(a++,r.push(c));continue}}default:throw Error(`Unexpected token: ${JSON.stringify(n)} after <${r[r.length-1].name}>`)}}if(r.length>1)throw Error(`Unclosed tag for <${r[r.length-1].name}>`);return n},o=e=>e.length===1?e[0]:e;function s(e){let t=new WeakMap,n=new Set(e.RawTextElements);n.delete(`template`);let i=document.createTreeWalker(document,129),s=t=>e.SVGElements.has(t)?document.createElementNS(`http://www.w3.org/2000/svg`,t):e.MathMLElements.has(t)?document.createElementNS(`http://www.w3.org/1998/Math/MathML`,t):document.createElement(t),c=e=>{let t=(t,...n)=>m(l(t),n,e);return t.components=e,t.jsx=t,t.define=t=>c({...e,...t}),t},l=i=>{let o=t.get(i);return o||(o=a(r(i,n),e.VoidElements),u(o,false),t.set(i,o)),o},u=(e,t)=>{if(e.type===1){if(!t){let n=document.createElement(`template`);n.content.appendChild(f(e)),e.template=n,t=true;}e.children.forEach(e=>u(e,t));}else e.type===2||e.type===0?e.children.forEach(e=>u(e,false)):e.type===3&&!t&&(d.innerHTML=e.value,e.value=d.content.textContent??``);},d=document.createElement(`template`),f=e=>{switch(e.type){case 3:return d.innerHTML=e.value,document.createTextNode(d.content.textContent??``);case 4:return document.createComment(`+`);case 2:return document.createComment(e.name);case 1:let t=false,n=s(e.name),r=e.name===`a`?`href`:e.name===`form`?`action`:void 0;return e.props=e.props.filter(i=>i.type===1?i.name.startsWith(`prop:`)?true:(n.setAttribute(i.name,i.value),!t&&i.name===r&&(e.claim=true),t):i.type===0?(n.setAttribute(i.name,``),!t&&i.name===r&&(e.claim=true),t):i.type===3?(t=true,t):true),(e.name===`template`?n.content:n).append(...e.children.map(f)),n}},p=(t,n,r)=>{switch(t.type){case 3:return t.value;case 4:return n[t.value];case 2:let i=typeof t.name==`string`?r[t.name]:n[t.name];if(i&&typeof i==`function`)return e.createComponent(i,h(t,n,r));throw Error(`Component "${t.name}" not found in registry`);case 1:let a=m(t,n,r),o=h(t,n,r);return e.spread(a,o,true),t.claim&&e.claimElement(a),a}},m=(t,n,r)=>{if(t.type!==1||!t.template)return o(t.children.map(e=>p(e,n,r)));let a=t.template.content.firstChild.cloneNode(true);i.currentNode=a;let s=(t,i)=>{for(let a of t)if(a.type===1||a.type===4||a.type===2){let t=i.nextNode();if(a.type===4||a.type===2)e.insert(t.parentNode,p(a,n,r),t),i.currentNode=t;else {if(a.props.length){let i=h(a,n,r);e.spread(t,i,true);}a.claim&&e.claimElement(t),s(a.children,a.name===`template`?document.createTreeWalker(t.content,129):i);}}};return s(t.children,t.name===`template`?document.createTreeWalker(a.content,129):i),a},h=(t,n,r,i={})=>{for(let r of t.props)switch(r.type){case 0:i[r.name]=true;break;case 1:i[r.name]=r.value;break;case 2:g(i,r.name,n[r.value]);break;case 3:let t=n[r.value];if(!t||typeof t!=`object`)throw Error(`Can only spread objects`);i=e.mergeProps(i,t);break}return t.type===2&&t.children.length&&Object.defineProperty(i,`children`,{get(){return m(t,n,r)}}),i},g=(e,t,n)=>{typeof n==`function`&&n.length===0&&t!==`ref`&&!t.startsWith(`on`)?Object.defineProperty(e,t,{get(){return n()},enumerable:true}):e[t]=n;};return c({})}
3
+ const OPEN_TAG_TOKEN = 0;
4
+ const CLOSE_TAG_TOKEN = 1;
5
+ const SLASH_TOKEN = 2;
6
+ const IDENTIFIER_TOKEN = 3;
7
+ const EQUALS_TOKEN = 4;
8
+ const STRING_TOKEN = 5;
9
+ const TEXT_TOKEN = 6;
10
+ const EXPRESSION_TOKEN = 7;
11
+ const SPREAD_TOKEN = 8;
12
+ const isIdentifierChar = code => {
13
+ return isIdentifierStart(code) || code >= 48 && code <= 58 ||
14
+ code === 46 ||
15
+ code === 45
16
+ ;
17
+ };
18
+ const isIdentifierStart = code => {
19
+ return code >= 65 && code <= 90 ||
20
+ code >= 97 && code <= 122 ||
21
+ code === 95 ||
22
+ code === 36
23
+ ;
24
+ };
25
+ const isWhitespace = code => {
26
+ return code >= 9 && code <= 13 || code === 32;
27
+ };
28
+ const STATE_TEXT = 0;
29
+ const STATE_TAG = 1;
30
+ const STATE_RAW_TEXT = 2;
31
+ const STATE_COMMENT = 3;
32
+ const STATE_LINE_COMMENT = 4;
33
+ const STATE_BLOCK_COMMENT = 5;
34
+ const tokenize = (strings, rawTextElements) => {
35
+ const tokens = [];
36
+ let state = STATE_TEXT;
37
+ let lastTagName = "";
38
+ let cursor = 0;
39
+ for (let i = 0; i < strings.length; i++) {
40
+ const str = strings[i];
41
+ const len = str.length;
42
+ cursor = 0;
43
+ while (cursor < len) {
44
+ switch (state) {
45
+ case STATE_TEXT:
46
+ {
47
+ lastTagName = "";
48
+ const nextTag = str.indexOf("<", cursor);
49
+ if (nextTag === -1) {
50
+ if (cursor < len) tokens.push({
51
+ type: TEXT_TOKEN,
52
+ value: str.slice(cursor)
53
+ });
54
+ cursor = len;
55
+ } else {
56
+ if (nextTag > cursor) tokens.push({
57
+ type: TEXT_TOKEN,
58
+ value: str.slice(cursor, nextTag)
59
+ });
60
+ if (str[nextTag + 1] === "!" && str[nextTag + 2] === "-" && str[nextTag + 3] === "-") {
61
+ state = STATE_COMMENT;
62
+ cursor = nextTag + 4;
63
+ } else {
64
+ tokens.push({
65
+ type: OPEN_TAG_TOKEN
66
+ });
67
+ state = STATE_TAG;
68
+ cursor = nextTag + 1;
69
+ }
70
+ }
71
+ break;
72
+ }
73
+ case STATE_TAG:
74
+ {
75
+ const code = str.charCodeAt(cursor);
76
+ if (isWhitespace(code)) {
77
+ cursor++;
78
+ } else if (code === 62) {
79
+ if (rawTextElements.has(lastTagName) && tokens[tokens.length - 2]?.type !== SLASH_TOKEN) {
80
+ state = STATE_RAW_TEXT;
81
+ } else {
82
+ state = STATE_TEXT;
83
+ lastTagName = "";
84
+ }
85
+ tokens.push({
86
+ type: CLOSE_TAG_TOKEN
87
+ });
88
+ cursor++;
89
+ } else if (code === 61) {
90
+ tokens.push({
91
+ type: EQUALS_TOKEN
92
+ });
93
+ cursor++;
94
+ } else if (code === 47) {
95
+ const next = str.charCodeAt(cursor + 1);
96
+ const nextNonWhitespace = str.slice(cursor + 2).search(/\S/);
97
+ const isShorthandClosingTag = next === 47 && tokens[tokens.length - 1]?.type === OPEN_TAG_TOKEN && nextNonWhitespace !== -1 && str[cursor + 2 + nextNonWhitespace] === ">";
98
+ if (next === 47 && !isShorthandClosingTag) {
99
+ state = STATE_LINE_COMMENT;
100
+ } else if (next === 42) {
101
+ state = STATE_BLOCK_COMMENT;
102
+ } else {
103
+ tokens.push({
104
+ type: SLASH_TOKEN
105
+ });
106
+ cursor++;
107
+ }
108
+ } else if (code === 34 || code === 39) {
109
+ const char = str[cursor];
110
+ const endQuoteIndex = str.indexOf(char, cursor + 1);
111
+ if (endQuoteIndex === -1) {
112
+ throw new Error(`Unterminated string at ${i}:${cursor}`);
113
+ }
114
+ tokens.push({
115
+ type: STRING_TOKEN,
116
+ value: str.slice(cursor + 1, endQuoteIndex),
117
+ quote: char
118
+ });
119
+ cursor = endQuoteIndex + 1;
120
+ } else if (isIdentifierStart(code)) {
121
+ const start = cursor;
122
+ while (cursor < len && isIdentifierChar(str.charCodeAt(cursor))) cursor++;
123
+ const value = str.slice(start, cursor);
124
+ if (lastTagName === "") {
125
+ lastTagName = value;
126
+ }
127
+ tokens.push({
128
+ type: IDENTIFIER_TOKEN,
129
+ value
130
+ });
131
+ } else if (code === 46 && str[cursor + 1] === "." && str[cursor + 2] === ".") {
132
+ tokens.push({
133
+ type: SPREAD_TOKEN
134
+ });
135
+ cursor += 3;
136
+ } else {
137
+ throw new Error(`Unexpected Character: ${str[cursor]} at ${i}:${cursor}`);
138
+ }
139
+ break;
140
+ }
141
+ case STATE_RAW_TEXT:
142
+ {
143
+ const closeTagRegex = new RegExp(`<\\s*/\\s*${lastTagName}\\s*>`, "g");
144
+ closeTagRegex.lastIndex = cursor;
145
+ const match = closeTagRegex.exec(str);
146
+ if (match) {
147
+ const endOfRawIdx = match.index;
148
+ if (endOfRawIdx > cursor) {
149
+ tokens.push({
150
+ type: TEXT_TOKEN,
151
+ value: str.slice(cursor, endOfRawIdx)
152
+ });
153
+ }
154
+ state = STATE_TEXT;
155
+ cursor = endOfRawIdx;
156
+ lastTagName = "";
157
+ } else {
158
+ tokens.push({
159
+ type: TEXT_TOKEN,
160
+ value: str.slice(cursor)
161
+ });
162
+ cursor = len;
163
+ }
164
+ break;
165
+ }
166
+ case STATE_COMMENT:
167
+ case STATE_LINE_COMMENT:
168
+ case STATE_BLOCK_COMMENT:
169
+ {
170
+ const commentEnd = state === STATE_LINE_COMMENT ? "\n" : state === STATE_BLOCK_COMMENT ? "*/" : "-->";
171
+ const commentEndIndex = str.indexOf(commentEnd, cursor);
172
+ if (commentEndIndex === -1) {
173
+ cursor = len;
174
+ } else {
175
+ state = state === STATE_COMMENT ? STATE_TEXT : STATE_TAG;
176
+ cursor = commentEndIndex + commentEnd.length;
177
+ }
178
+ break;
179
+ }
180
+ }
181
+ }
182
+ if (i < strings.length - 1) {
183
+ if (state === STATE_TEXT || state === STATE_TAG || state === STATE_RAW_TEXT) {
184
+ tokens.push({
185
+ type: EXPRESSION_TOKEN,
186
+ value: i
187
+ });
188
+ }
189
+ }
190
+ }
191
+ return tokens;
192
+ };
5
193
 
6
- const html = s({
7
- insert,
8
- spread,
9
- createComponent,
10
- mergeProps,
11
- claimElement,
12
- SVGElements,
13
- MathMLElements,
14
- VoidElements,
15
- RawTextElements
16
- });
194
+ const isComponentNode = name => {
195
+ const char = name.charCodeAt(0);
196
+ return char >= 65 && char <= 90
197
+ ;
198
+ };
199
+ const ROOT_NODE = 0;
200
+ const ELEMENT_NODE = 1;
201
+ const COMPONENT_NODE = 2;
202
+ const TEXT_NODE = 3;
203
+ const EXPRESSION_NODE = 4;
204
+ const BOOLEAN_PROP = 0;
205
+ const STATIC_PROP = 1;
206
+ const EXPRESSION_PROP = 2;
207
+ const SPREAD_PROP = 3;
208
+ const parse = (tokens, voidElements) => {
209
+ const root = {
210
+ type: ROOT_NODE,
211
+ children: []
212
+ };
213
+ const stack = [root];
214
+ let pos = 0;
215
+ const len = tokens.length;
216
+ while (pos < len) {
217
+ const token = tokens[pos];
218
+ const parent = stack[stack.length - 1];
219
+ switch (token.type) {
220
+ case TEXT_TOKEN:
221
+ {
222
+ const value = token.value;
223
+ if (value.trim() === "") {
224
+ const prevType = tokens[pos - 1]?.type;
225
+ const nextType = tokens[pos + 1]?.type;
226
+ if (prevType === CLOSE_TAG_TOKEN || nextType === OPEN_TAG_TOKEN) {
227
+ pos++;
228
+ continue;
229
+ }
230
+ }
231
+ parent.children.push({
232
+ type: TEXT_NODE,
233
+ value
234
+ });
235
+ pos++;
236
+ continue;
237
+ }
238
+ case EXPRESSION_TOKEN:
239
+ {
240
+ parent.children.push({
241
+ type: EXPRESSION_NODE,
242
+ value: token.value
243
+ });
244
+ pos++;
245
+ continue;
246
+ }
247
+ case OPEN_TAG_TOKEN:
248
+ {
249
+ const nextToken = tokens[++pos];
250
+ if (nextToken.type === SLASH_TOKEN) {
251
+ const nameToken = tokens[++pos];
252
+ const closeToken = tokens[++pos];
253
+ const currentParent = stack[stack.length - 1];
254
+ if (stack.length > 1 && closeToken.type === CLOSE_TAG_TOKEN && (nameToken?.type === IDENTIFIER_TOKEN && currentParent.name === nameToken.value || (nameToken?.type === EXPRESSION_TOKEN || nameToken.type === SLASH_TOKEN) && typeof currentParent.name === "number")) {
255
+ const node = stack.pop();
256
+ if (node?.type === ELEMENT_NODE && voidElements.has(node.name)) {
257
+ node.children = [];
258
+ }
259
+ pos++;
260
+ continue;
261
+ }
262
+ throw new Error(`Mismatched closing tag for <${currentParent.name}>`);
263
+ }
264
+ if (nextToken.type === IDENTIFIER_TOKEN || nextToken.type === EXPRESSION_TOKEN) {
265
+ const tagName = nextToken.value;
266
+ const node = {
267
+ type: typeof tagName === "number" || isComponentNode(tagName) ? COMPONENT_NODE : ELEMENT_NODE,
268
+ name: tagName,
269
+ props: [],
270
+ children: []
271
+ };
272
+ parent.children.push(node);
273
+ pos++;
274
+ while (pos < len) {
275
+ const attrToken = tokens[pos];
276
+ if (attrToken.type === CLOSE_TAG_TOKEN || attrToken.type === SLASH_TOKEN) {
277
+ break;
278
+ }
279
+ if (attrToken.type === SPREAD_TOKEN) {
280
+ const expr = tokens[pos + 1];
281
+ if (expr?.type === EXPRESSION_TOKEN) {
282
+ node.props.push({
283
+ type: SPREAD_PROP,
284
+ value: expr.value
285
+ });
286
+ pos += 2;
287
+ } else {
288
+ throw new Error(`Spread operator in <${node.name}> must be followed by an expression`);
289
+ }
290
+ } else if (attrToken.type === IDENTIFIER_TOKEN) {
291
+ const name = attrToken.value;
292
+ const next = tokens[pos + 1];
293
+ if (next?.type === EQUALS_TOKEN) {
294
+ pos += 2;
295
+ const valToken = tokens[pos];
296
+ if (valToken.type === EXPRESSION_TOKEN) {
297
+ node.props.push({
298
+ name,
299
+ type: EXPRESSION_PROP,
300
+ value: valToken.value
301
+ });
302
+ pos++;
303
+ } else if (valToken.type === STRING_TOKEN) {
304
+ const quote = valToken.quote;
305
+ node.props.push({
306
+ name,
307
+ value: valToken.value,
308
+ quote,
309
+ type: STATIC_PROP
310
+ });
311
+ pos++;
312
+ } else {
313
+ throw new Error(`Attribute value for "${name}" in <${node.name}> must be an expression or a string`);
314
+ }
315
+ } else {
316
+ node.props.push({
317
+ type: BOOLEAN_PROP,
318
+ name,
319
+ value: true
320
+ });
321
+ pos++;
322
+ }
323
+ } else {
324
+ throw new Error(`Invalid attribute in <${node.name}>`);
325
+ }
326
+ }
327
+ const endToken = tokens[pos];
328
+ if (endToken.type === SLASH_TOKEN) {
329
+ pos += 2;
330
+ } else if (endToken.type === CLOSE_TAG_TOKEN) {
331
+ pos++;
332
+ stack.push(node);
333
+ }
334
+ continue;
335
+ }
336
+ }
337
+ default:
338
+ throw new Error(`Unexpected token: ${JSON.stringify(token)} after <${stack[stack.length - 1].name}>`);
339
+ }
340
+ }
341
+ if (stack.length > 1) {
342
+ throw new Error(`Unclosed tag for <${stack[stack.length - 1].name}>`);
343
+ }
344
+ return root;
345
+ };
346
+
347
+ const flat = arr => {
348
+ return arr.length === 1 ? arr[0] : arr;
349
+ };
350
+ function createHtml() {
351
+ const cache = new WeakMap();
352
+ const rawTextElements = new Set(RawTextElements);
353
+ rawTextElements.delete("template");
354
+ const walker = document.createTreeWalker(document, 129);
355
+ const createElement = name => {
356
+ return SVGElements.has(name) ? document.createElementNS("http://www.w3.org/2000/svg", name) : MathMLElements.has(name) ? document.createElementNS("http://www.w3.org/1998/Math/MathML", name) : document.createElement(name);
357
+ };
358
+ const createTaggedJSX = components => {
359
+ const tag = (strings, ...values) => {
360
+ const root = getCachedRoot(strings);
361
+ return renderChildren(root, values, components);
362
+ };
363
+ tag.components = components;
364
+ tag.jsx = tag;
365
+ tag.define = newComponents => {
366
+ return createTaggedJSX({
367
+ ...components,
368
+ ...newComponents
369
+ });
370
+ };
371
+ return tag;
372
+ };
373
+ const getCachedRoot = strings => {
374
+ let root = cache.get(strings);
375
+ if (!root) {
376
+ root = parse(tokenize(strings, rawTextElements), VoidElements);
377
+ buildTemplate(root, false);
378
+ cache.set(strings, root);
379
+ }
380
+ return root;
381
+ };
382
+ const buildTemplate = (node, insideTemplate) => {
383
+ if (node.type === ELEMENT_NODE) {
384
+ if (!insideTemplate) {
385
+ const template = document.createElement("template");
386
+ template.content.appendChild(buildNodes(node));
387
+ node.template = template;
388
+ insideTemplate = true;
389
+ }
390
+ node.children.forEach(child => buildTemplate(child, insideTemplate));
391
+ } else if (node.type === COMPONENT_NODE || node.type === ROOT_NODE) {
392
+ node.children.forEach(child => buildTemplate(child, false));
393
+ } else if (node.type === TEXT_NODE && !insideTemplate) {
394
+ textTemplate.innerHTML = node.value;
395
+ node.value = textTemplate.content.textContent ?? "";
396
+ }
397
+ };
398
+ const textTemplate = document.createElement("template");
399
+ const buildNodes = node => {
400
+ switch (node.type) {
401
+ case TEXT_NODE:
402
+ textTemplate.innerHTML = node.value;
403
+ return document.createTextNode(textTemplate.content.textContent ?? "");
404
+ case EXPRESSION_NODE:
405
+ return document.createComment("+");
406
+ case COMPONENT_NODE:
407
+ return document.createComment(node.name);
408
+ case ELEMENT_NODE:
409
+ let hasSpread = false;
410
+ const elem = createElement(node.name);
411
+ const claimAttr = node.name === "a" ? "href" : node.name === "form" ? "action" : undefined;
412
+ node.props = node.props.filter(prop => {
413
+ if (prop.type === STATIC_PROP) {
414
+ if (prop.name.startsWith("prop:")) return true;
415
+ elem.setAttribute(prop.name, prop.value);
416
+ if (!hasSpread && prop.name === claimAttr) node.claim = true;
417
+ return hasSpread;
418
+ } else if (prop.type === BOOLEAN_PROP) {
419
+ elem.setAttribute(prop.name, "");
420
+ if (!hasSpread && prop.name === claimAttr) node.claim = true;
421
+ return hasSpread;
422
+ } else if (prop.type === SPREAD_PROP) {
423
+ hasSpread = true;
424
+ return hasSpread;
425
+ }
426
+ return true;
427
+ });
428
+ const childRoot = node.name === "template" ? elem.content : elem;
429
+ childRoot.append(...node.children.map(buildNodes));
430
+ return elem;
431
+ }
432
+ };
433
+ const renderNode = (node, values, components) => {
434
+ switch (node.type) {
435
+ case TEXT_NODE:
436
+ return node.value;
437
+ case EXPRESSION_NODE:
438
+ return values[node.value];
439
+ case COMPONENT_NODE:
440
+ const component = typeof node.name === "string" ? components[node.name] : values[node.name];
441
+ if (component && typeof component === "function") {
442
+ return createComponent(component, gatherProps(node, values, components));
443
+ } else {
444
+ throw new Error(`Component "${node.name}" not found in registry`);
445
+ }
446
+ case ELEMENT_NODE:
447
+ const element = renderChildren(node, values, components);
448
+ const props = gatherProps(node, values, components);
449
+ spread(element, props, true);
450
+ if (node.claim) claimElement(element);
451
+ return element;
452
+ }
453
+ };
454
+ const renderChildren = (node, values, components) => {
455
+ if (node.type !== ELEMENT_NODE || !node.template) {
456
+ return flat(node.children.map(n => renderNode(n, values, components)));
457
+ }
458
+ const element = node.template.content.firstChild.cloneNode(true);
459
+ walker.currentNode = element;
460
+ const walkNodes = (nodes, walker) => {
461
+ for (const node of nodes) {
462
+ if (node.type === ELEMENT_NODE || node.type === EXPRESSION_NODE || node.type === COMPONENT_NODE) {
463
+ const domNode = walker.nextNode();
464
+ if (node.type === EXPRESSION_NODE || node.type === COMPONENT_NODE) {
465
+ insert(domNode.parentNode, renderNode(node, values, components), domNode);
466
+ walker.currentNode = domNode;
467
+ } else {
468
+ if (node.props.length) {
469
+ const props = gatherProps(node, values, components);
470
+ spread(domNode, props, true);
471
+ }
472
+ if (node.claim) claimElement(domNode);
473
+ walkNodes(node.children, node.name === "template" ? document.createTreeWalker(domNode.content, 129) : walker);
474
+ }
475
+ }
476
+ }
477
+ };
478
+ walkNodes(node.children, node.name === "template" ? document.createTreeWalker(element.content, 129) : walker);
479
+ return element;
480
+ };
481
+ const gatherProps = (node, values, components, props = {}) => {
482
+ for (const prop of node.props) {
483
+ switch (prop.type) {
484
+ case BOOLEAN_PROP:
485
+ props[prop.name] = true;
486
+ break;
487
+ case STATIC_PROP:
488
+ props[prop.name] = prop.value;
489
+ break;
490
+ case EXPRESSION_PROP:
491
+ applyGetter(props, prop.name, values[prop.value]);
492
+ break;
493
+ case SPREAD_PROP:
494
+ const spreadValue = values[prop.value];
495
+ if (!spreadValue || typeof spreadValue !== "object") throw new Error("Can only spread objects");
496
+ props = mergeProps(props, spreadValue);
497
+ break;
498
+ }
499
+ }
500
+ if (node.type === COMPONENT_NODE && node.children.length) {
501
+ Object.defineProperty(props, "children", {
502
+ get() {
503
+ return renderChildren(node, values, components);
504
+ }
505
+ });
506
+ }
507
+ return props;
508
+ };
509
+ const applyGetter = (props, name, value) => {
510
+ if (typeof value === "function" && value.length === 0 && name !== "ref" && !name.startsWith("on")) {
511
+ Object.defineProperty(props, name, {
512
+ get() {
513
+ return value();
514
+ },
515
+ enumerable: true
516
+ });
517
+ } else {
518
+ props[name] = value;
519
+ }
520
+ };
521
+ return createTaggedJSX({});
522
+ }
523
+ const html = createHtml();
17
524
 
18
525
  export { html as default };
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@solidjs/html",
3
3
  "description": "Tagged-template-literal templating for Solid — write components with no build step.",
4
- "version": "2.0.0-rc.2",
4
+ "version": "2.0.0-rc.3",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
7
7
  "homepage": "https://solidjs.com",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "git+https://github.com/solidjs/solid.git",
11
- "directory": "packages/solid-html"
11
+ "directory": "packages/html"
12
12
  },
13
13
  "publishConfig": {
14
14
  "access": "public"
@@ -36,21 +36,21 @@
36
36
  }
37
37
  }
38
38
  },
39
- "peerDependencies": {
40
- "@solidjs/web": "^2.0.0-rc.2"
41
- },
42
- "devDependencies": {
43
- "@solidjs/web": "2.0.0-rc.2",
44
- "solid-js": "2.0.0-rc.2"
45
- },
46
39
  "scripts": {
47
40
  "build": "npm-run-all -nl build:*",
48
41
  "build:clean": "rimraf dist/ coverage/",
49
42
  "build:js": "rollup -c",
50
43
  "types": "npm-run-all -nl types:clean types:html types:cjs",
51
44
  "types:clean": "rimraf types/ types-cjs/",
52
- "types:html": "tsc --project ./tsconfig.json && ncp ../../node_modules/@dom-expressions/tagged-jsx/dist/index.d.mts ./types/tagged-jsx.d.ts",
45
+ "types:html": "tsc --project ./tsconfig.json",
53
46
  "types:cjs": "node ../../scripts/sync-dual-types.mjs ./types ./types-cjs",
54
47
  "test": "vitest run"
48
+ },
49
+ "peerDependencies": {
50
+ "@solidjs/web": "^2.0.0-rc.3"
51
+ },
52
+ "devDependencies": {
53
+ "@solidjs/web": "workspace:*",
54
+ "solid-js": "workspace:*"
55
55
  }
56
- }
56
+ }
package/types/index.d.ts CHANGED
@@ -1,3 +1 @@
1
- import type { TaggedJSXInstance } from "./tagged-jsx.js";
2
- declare const html: TaggedJSXInstance<{}>;
3
- export default html;
1
+ export { default, type TaggedJSXInstance, type ComponentRegistry, type FunctionComponent } from "./tagged-jsx.js";
@@ -0,0 +1,67 @@
1
+ import { Token } from "./tokenize.js";
2
+ export declare const ROOT_NODE = 0;
3
+ export declare const ELEMENT_NODE = 1;
4
+ export declare const COMPONENT_NODE = 2;
5
+ export declare const TEXT_NODE = 3;
6
+ export declare const EXPRESSION_NODE = 4;
7
+ export declare const BOOLEAN_PROP = 0;
8
+ export declare const STATIC_PROP = 1;
9
+ export declare const EXPRESSION_PROP = 2;
10
+ export declare const SPREAD_PROP = 3;
11
+ export type NodeType = typeof ROOT_NODE | typeof ELEMENT_NODE | typeof COMPONENT_NODE | typeof TEXT_NODE | typeof EXPRESSION_NODE;
12
+ export type PropType = typeof BOOLEAN_PROP | typeof STATIC_PROP | typeof EXPRESSION_PROP | typeof SPREAD_PROP;
13
+ export type ChildNode = ElementNode | TextNode | ExpressionNode | ComponentNode;
14
+ export interface RootNode {
15
+ type: typeof ROOT_NODE;
16
+ children: ChildNode[];
17
+ }
18
+ export interface ElementNode {
19
+ type: typeof ELEMENT_NODE;
20
+ name: string;
21
+ props: PropNode[];
22
+ children: ChildNode[];
23
+ template?: HTMLTemplateElement;
24
+ /**
25
+ * Element-claim contract target (`a[href]`, `form[action]`, or a spread
26
+ * that may carry those). Stamped at template build — before static props
27
+ * are filtered out — so clones can be claimed even when the attribute was
28
+ * baked into the template.
29
+ */
30
+ claim?: boolean;
31
+ }
32
+ export interface ComponentNode {
33
+ type: typeof COMPONENT_NODE;
34
+ name: string | number;
35
+ props: PropNode[];
36
+ children: ChildNode[];
37
+ }
38
+ export interface TextNode {
39
+ type: typeof TEXT_NODE;
40
+ value: string;
41
+ }
42
+ export interface ExpressionNode {
43
+ type: typeof EXPRESSION_NODE;
44
+ value: number;
45
+ }
46
+ export interface BooleanProp {
47
+ name: string;
48
+ type: typeof BOOLEAN_PROP;
49
+ value: boolean;
50
+ }
51
+ export interface StringProp {
52
+ name: string;
53
+ type: typeof STATIC_PROP;
54
+ value: string;
55
+ quote?: "'" | '"';
56
+ }
57
+ export interface ExpressionProp {
58
+ name: string;
59
+ type: typeof EXPRESSION_PROP;
60
+ value: number;
61
+ }
62
+ export interface SpreadProp {
63
+ type: typeof SPREAD_PROP;
64
+ value: number;
65
+ }
66
+ export type PropNode = BooleanProp | StringProp | ExpressionProp | SpreadProp;
67
+ export declare const parse: (tokens: Token[], voidElements: Set<string>) => RootNode;