@thyn/vite-plugin 0.0.312 → 0.0.314

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/dom.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ export declare class Node {
2
+ nodeType: number;
3
+ childNodes: Node[];
4
+ constructor(nodeType: number);
5
+ get textContent(): string;
6
+ set textContent(value: string);
7
+ }
8
+ export declare class Text extends Node {
9
+ private _text;
10
+ constructor(text: string);
11
+ get textContent(): string;
12
+ set textContent(value: string);
13
+ }
14
+ export declare class Element extends Node {
15
+ tagName: string;
16
+ attributes: {
17
+ name: string;
18
+ value: string;
19
+ }[];
20
+ constructor(tagName: string);
21
+ getAttribute(name: string): string | null;
22
+ setAttribute(name: string, value: string): void;
23
+ removeAttribute(name: string): void;
24
+ hasAttribute(name: string): boolean;
25
+ get classList(): {
26
+ add: (className: string) => void;
27
+ };
28
+ get children(): Element[];
29
+ set innerHTML(html: string);
30
+ }
31
+ export declare class DocumentFragment extends Node {
32
+ constructor();
33
+ get firstElementChild(): Element | null;
34
+ }
35
+ export declare function parseHTML(html: string): DocumentFragment;
package/dist/dom.js ADDED
@@ -0,0 +1,142 @@
1
+ export class Node {
2
+ constructor(nodeType) {
3
+ this.childNodes = [];
4
+ this.nodeType = nodeType;
5
+ }
6
+ get textContent() {
7
+ return this.childNodes.map(node => node.textContent).join('');
8
+ }
9
+ set textContent(value) {
10
+ this.childNodes = [new Text(value)];
11
+ }
12
+ }
13
+ export class Text extends Node {
14
+ constructor(text) {
15
+ super(3); // Node.TEXT_NODE
16
+ this._text = text;
17
+ }
18
+ get textContent() {
19
+ return this._text;
20
+ }
21
+ set textContent(value) {
22
+ this._text = value;
23
+ }
24
+ }
25
+ export class Element extends Node {
26
+ constructor(tagName) {
27
+ super(1); // Node.ELEMENT_NODE
28
+ this.attributes = [];
29
+ this.tagName = tagName;
30
+ }
31
+ getAttribute(name) {
32
+ const attr = this.attributes.find(a => a.name === name);
33
+ return attr ? attr.value : null;
34
+ }
35
+ setAttribute(name, value) {
36
+ const attr = this.attributes.find(a => a.name === name);
37
+ if (attr) {
38
+ attr.value = value;
39
+ }
40
+ else {
41
+ this.attributes.push({ name, value });
42
+ }
43
+ }
44
+ removeAttribute(name) {
45
+ this.attributes = this.attributes.filter(a => a.name !== name);
46
+ }
47
+ hasAttribute(name) {
48
+ return this.attributes.some(a => a.name === name);
49
+ }
50
+ get classList() {
51
+ return {
52
+ add: (className) => {
53
+ const existing = this.getAttribute('class') || '';
54
+ const classes = existing.split(/\s+/).filter(Boolean);
55
+ if (!classes.includes(className)) {
56
+ classes.push(className);
57
+ this.setAttribute('class', classes.join(' '));
58
+ }
59
+ }
60
+ };
61
+ }
62
+ get children() {
63
+ return this.childNodes.filter(node => node.nodeType === 1);
64
+ }
65
+ set innerHTML(html) {
66
+ const fragment = parseHTML(html);
67
+ this.childNodes = fragment.childNodes;
68
+ }
69
+ }
70
+ export class DocumentFragment extends Node {
71
+ constructor() {
72
+ super(11); // Node.DOCUMENT_FRAGMENT_NODE
73
+ }
74
+ get firstElementChild() {
75
+ return this.childNodes.find(node => node.nodeType === 1) || null;
76
+ }
77
+ }
78
+ const voidElements = new Set([
79
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'
80
+ ]);
81
+ export function parseHTML(html) {
82
+ const fragment = new DocumentFragment();
83
+ const stack = [fragment];
84
+ const tagRegex = new RegExp('<(?:\\/([a-zA-Z0-9\\-:]+)|([a-zA-Z0-9\\-:]+)((?:[^>"\']|"[^"]*"|\'[^\']*\')*)(\\/)?)>|<!--[\\s\\S]*?-->', 'g');
85
+ let lastIndex = 0;
86
+ let match;
87
+ while ((match = tagRegex.exec(html)) !== null) {
88
+ const [fullMatch, closeTag, openTag, attrs, selfClosing] = match;
89
+ const index = match.index;
90
+ // Process text before the tag
91
+ if (index > lastIndex) {
92
+ const textContent = html.slice(lastIndex, index);
93
+ if (textContent) {
94
+ stack[stack.length - 1].childNodes.push(new Text(textContent));
95
+ }
96
+ }
97
+ lastIndex = index + fullMatch.length;
98
+ // Ignore comments
99
+ if (fullMatch.startsWith('<!--')) {
100
+ continue;
101
+ }
102
+ if (openTag) {
103
+ const element = new Element(openTag);
104
+ if (attrs) {
105
+ // Match attributes: name="value", name='value', name=value, name
106
+ const attrRegex = /([a-zA-Z0-9\-:@\._#]+)\s*(?:=\s*(?:"([^"]*)"|'([^']*)'|([^"'\s]+)))?/g;
107
+ let attrMatch;
108
+ while ((attrMatch = attrRegex.exec(attrs)) !== null) {
109
+ const name = attrMatch[1];
110
+ const value = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? '';
111
+ element.setAttribute(name, value);
112
+ }
113
+ }
114
+ stack[stack.length - 1].childNodes.push(element);
115
+ const isVoid = voidElements.has(openTag.toLowerCase());
116
+ const isSelfClosingTag = !!selfClosing;
117
+ if (!isVoid && !isSelfClosingTag) {
118
+ stack.push(element);
119
+ }
120
+ }
121
+ else if (closeTag) {
122
+ // Find the corresponding opening tag in the stack
123
+ // We search backwards to handle potential malformed HTML gracefully-ish
124
+ for (let i = stack.length - 1; i > 0; i--) {
125
+ const node = stack[i];
126
+ if (node instanceof Element && node.tagName.toLowerCase() === closeTag.toLowerCase()) {
127
+ // Found the matching tag, pop everything up to this point
128
+ stack.splice(i);
129
+ break;
130
+ }
131
+ }
132
+ }
133
+ }
134
+ // Process any remaining text
135
+ if (lastIndex < html.length) {
136
+ const textContent = html.slice(lastIndex);
137
+ if (textContent) {
138
+ stack[stack.length - 1].childNodes.push(new Text(textContent));
139
+ }
140
+ }
141
+ return fragment;
142
+ }
@@ -0,0 +1,31 @@
1
+ interface Node {
2
+ nodeType: number;
3
+ nodeName: string;
4
+ textContent: string;
5
+ childNodes: Node[];
6
+ }
7
+ interface Element extends Node {
8
+ tagName: string;
9
+ attributes: Array<{
10
+ name: string;
11
+ value: string;
12
+ }>;
13
+ children: Element[];
14
+ firstElementChild: Element | null;
15
+ hasAttribute(name: string): boolean;
16
+ getAttribute(name: string): string | null;
17
+ setAttribute(name: string, value: string): void;
18
+ removeAttribute(name: string): void;
19
+ classList: {
20
+ add(className: string): void;
21
+ };
22
+ }
23
+ interface DocumentFragment {
24
+ childNodes: Node[];
25
+ firstElementChild: Element | null;
26
+ }
27
+ interface TemplateElement extends Element {
28
+ content: DocumentFragment;
29
+ }
30
+ export declare function parseHTML(html: string): TemplateElement;
31
+ export {};
@@ -0,0 +1,275 @@
1
+ function parseAttributes(attrStr) {
2
+ const attrs = [];
3
+ let i = 0;
4
+ while (i < attrStr.length) {
5
+ // Skip whitespace
6
+ while (i < attrStr.length && /\s/.test(attrStr[i]))
7
+ i++;
8
+ if (i >= attrStr.length)
9
+ break;
10
+ // Parse attribute name
11
+ let name = "";
12
+ while (i < attrStr.length && !/[\s=]/.test(attrStr[i])) {
13
+ name += attrStr[i];
14
+ i++;
15
+ }
16
+ if (!name)
17
+ break;
18
+ // Skip whitespace
19
+ while (i < attrStr.length && /\s/.test(attrStr[i]))
20
+ i++;
21
+ let value = "";
22
+ if (i < attrStr.length && attrStr[i] === "=") {
23
+ i++; // skip '='
24
+ // Skip whitespace
25
+ while (i < attrStr.length && /\s/.test(attrStr[i]))
26
+ i++;
27
+ if (i < attrStr.length) {
28
+ const quote = attrStr[i];
29
+ if (quote === '"' || quote === "'") {
30
+ i++; // skip opening quote
31
+ while (i < attrStr.length && attrStr[i] !== quote) {
32
+ value += attrStr[i];
33
+ i++;
34
+ }
35
+ if (i < attrStr.length)
36
+ i++; // skip closing quote
37
+ }
38
+ else {
39
+ // Unquoted value - take until whitespace
40
+ while (i < attrStr.length && !/\s/.test(attrStr[i])) {
41
+ value += attrStr[i];
42
+ i++;
43
+ }
44
+ }
45
+ }
46
+ }
47
+ attrs.push({ name, value });
48
+ }
49
+ return attrs;
50
+ }
51
+ function createTextNode(text) {
52
+ return {
53
+ nodeType: 3,
54
+ nodeName: "#text",
55
+ textContent: text,
56
+ childNodes: [],
57
+ };
58
+ }
59
+ function createElement(tagName, attributes = []) {
60
+ const children = [];
61
+ const childNodes = [];
62
+ const element = {
63
+ nodeType: 1,
64
+ nodeName: tagName.toUpperCase(),
65
+ tagName: tagName.toUpperCase(),
66
+ textContent: "",
67
+ attributes: [...attributes],
68
+ children,
69
+ childNodes,
70
+ firstElementChild: null,
71
+ hasAttribute(name) {
72
+ return this.attributes.some((attr) => attr.name === name);
73
+ },
74
+ getAttribute(name) {
75
+ const attr = this.attributes.find((attr) => attr.name === name);
76
+ return attr ? attr.value : null;
77
+ },
78
+ setAttribute(name, value) {
79
+ const existing = this.attributes.find((attr) => attr.name === name);
80
+ if (existing) {
81
+ existing.value = value;
82
+ }
83
+ else {
84
+ this.attributes.push({ name, value });
85
+ }
86
+ },
87
+ removeAttribute(name) {
88
+ this.attributes = this.attributes.filter((attr) => attr.name !== name);
89
+ },
90
+ classList: {
91
+ add: (className) => {
92
+ const existing = element.getAttribute("class");
93
+ const classes = existing ? existing.split(" ").filter(Boolean) : [];
94
+ if (!classes.includes(className)) {
95
+ classes.push(className);
96
+ element.setAttribute("class", classes.join(" "));
97
+ }
98
+ },
99
+ },
100
+ };
101
+ return element;
102
+ }
103
+ // Find next tag position, properly handling quoted strings
104
+ function findNextTag(html, startIndex) {
105
+ let i = startIndex;
106
+ while (i < html.length) {
107
+ // Find the next '<'
108
+ while (i < html.length && html[i] !== '<') {
109
+ i++;
110
+ }
111
+ if (i >= html.length)
112
+ return null;
113
+ const tagStart = i;
114
+ i++; // skip '<'
115
+ // Check if it's a closing tag
116
+ const isClose = i < html.length && html[i] === '/';
117
+ if (isClose)
118
+ i++;
119
+ // Parse tag name
120
+ let tagName = '';
121
+ while (i < html.length && /[a-zA-Z0-9-]/.test(html[i])) {
122
+ tagName += html[i];
123
+ i++;
124
+ }
125
+ if (!tagName) {
126
+ // Not a valid tag, continue searching
127
+ i = tagStart + 1;
128
+ continue;
129
+ }
130
+ // Parse attributes, respecting quotes
131
+ let attrs = '';
132
+ let inQuote = null;
133
+ let tagEnd = -1;
134
+ while (i < html.length) {
135
+ const char = html[i];
136
+ if (inQuote) {
137
+ attrs += char;
138
+ if (char === inQuote) {
139
+ inQuote = null;
140
+ }
141
+ i++;
142
+ }
143
+ else if (char === '"' || char === "'") {
144
+ attrs += char;
145
+ inQuote = char;
146
+ i++;
147
+ }
148
+ else if (char === '>') {
149
+ tagEnd = i + 1; // Include the '>'
150
+ i++;
151
+ break;
152
+ }
153
+ else {
154
+ attrs += char;
155
+ i++;
156
+ }
157
+ }
158
+ if (tagEnd === -1) {
159
+ // Malformed tag (no closing >), continue searching
160
+ i = tagStart + 1;
161
+ continue;
162
+ }
163
+ // Check for self-closing
164
+ const trimmedAttrs = attrs.trim();
165
+ const isSelfClose = trimmedAttrs.endsWith('/');
166
+ const finalAttrs = isSelfClose ? trimmedAttrs.slice(0, -1).trim() : trimmedAttrs;
167
+ return {
168
+ index: tagStart,
169
+ endIndex: tagEnd,
170
+ isClose,
171
+ tagName,
172
+ attrs: finalAttrs,
173
+ isSelfClose
174
+ };
175
+ }
176
+ return null;
177
+ }
178
+ export function parseHTML(html) {
179
+ const match = html.match(/<template([^>]*)>([\s\S]*)<\/template>/i);
180
+ if (!match) {
181
+ throw new Error("No <template> tag found in HTML");
182
+ }
183
+ const content = match[2].trim();
184
+ const stack = [];
185
+ const textChunks = [];
186
+ const fragmentChildren = [];
187
+ const fragmentElements = [];
188
+ let pos = 0;
189
+ const flushText = () => {
190
+ if (textChunks.length > 0) {
191
+ const text = textChunks.join("");
192
+ textChunks.length = 0;
193
+ const textNode = createTextNode(text);
194
+ if (stack.length > 0) {
195
+ const parent = stack[stack.length - 1];
196
+ parent.childNodes.push(textNode);
197
+ }
198
+ else {
199
+ fragmentChildren.push(textNode);
200
+ }
201
+ }
202
+ };
203
+ while (pos < content.length) {
204
+ const tagInfo = findNextTag(content, pos);
205
+ if (!tagInfo) {
206
+ // No more tags, add remaining as text
207
+ if (pos < content.length) {
208
+ textChunks.push(content.slice(pos));
209
+ }
210
+ break;
211
+ }
212
+ // Add text before this tag
213
+ if (tagInfo.index > pos) {
214
+ textChunks.push(content.slice(pos, tagInfo.index));
215
+ }
216
+ const { isClose, tagName, attrs, isSelfClose, endIndex } = tagInfo;
217
+ if (isClose) {
218
+ flushText();
219
+ if (stack.length > 0) {
220
+ const closedElement = stack.pop();
221
+ if (closedElement.tagName.toLowerCase() !== tagName.toLowerCase()) {
222
+ throw new Error(`Mismatched tags: expected </${closedElement.tagName}>, got </${tagName}>`);
223
+ }
224
+ // Update parent's firstElementChild if needed
225
+ const parent = stack.length > 0 ? stack[stack.length - 1] : null;
226
+ if (parent && !parent.firstElementChild) {
227
+ parent.firstElementChild = closedElement;
228
+ }
229
+ }
230
+ }
231
+ else {
232
+ flushText();
233
+ const attributes = parseAttributes(attrs);
234
+ const element = createElement(tagName, attributes);
235
+ if (stack.length === 0) {
236
+ // Top-level element
237
+ fragmentChildren.push(element);
238
+ fragmentElements.push(element);
239
+ }
240
+ else {
241
+ const parent = stack[stack.length - 1];
242
+ parent.children.push(element);
243
+ parent.childNodes.push(element);
244
+ if (!parent.firstElementChild) {
245
+ parent.firstElementChild = element;
246
+ }
247
+ }
248
+ if (!isSelfClose) {
249
+ stack.push(element);
250
+ }
251
+ }
252
+ // Move position past this tag
253
+ pos = endIndex;
254
+ }
255
+ // Flush any remaining text
256
+ if (stack.length === 0) {
257
+ flushText();
258
+ }
259
+ if (stack.length > 0) {
260
+ throw new Error(`Unclosed tags remain: ${stack.map(e => e.tagName).join(', ')}`);
261
+ }
262
+ const fragment = {
263
+ childNodes: fragmentChildren,
264
+ firstElementChild: fragmentElements[0] || null,
265
+ };
266
+ const templateAttrs = parseAttributes(match[1].trim());
267
+ const templateElement = {
268
+ ...createElement("template", templateAttrs),
269
+ content: fragment,
270
+ };
271
+ templateElement.childNodes = [...fragmentChildren];
272
+ templateElement.children = [...fragmentElements];
273
+ templateElement.firstElementChild = fragmentElements[0] || null;
274
+ return templateElement;
275
+ }