thunderous 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -227,6 +227,27 @@ createEffect(() => {
227
227
  });
228
228
  ```
229
229
 
230
+ ##### Debugging Signals
231
+
232
+ If you're having a tough time tracing an issue, you can provide the `debugMode` option to any signal to log potentially helpful information to the console. For differentiating values, you can also provide an optional `label` property to easily associate logs with their respective sources. These options can be passed to signals themselves, or to specific calls to setters and getters.
233
+
234
+ ```ts
235
+ const [count, setCount] = createSignal(0, {
236
+ debugMode: true,
237
+ label: 'count signal',
238
+ });
239
+
240
+ setCount(1, {
241
+ debugMode: true,
242
+ label: 'start count',
243
+ });
244
+
245
+ const newCount = count({
246
+ debugMode: true,
247
+ label: 'new count',
248
+ });
249
+ ```
250
+
230
251
  #### Refs
231
252
 
232
253
  The refs property exists for convenience to avoid manually querying the DOM. Since the DOM is only available after rendering, refs will only work in and after the `connectedCallback` method.
package/dist/index.cjs CHANGED
@@ -48,19 +48,64 @@ var setInnerHTML = (element, html2) => {
48
48
 
49
49
  // src/signals.ts
50
50
  var subscriber = null;
51
- var createSignal = (initVal) => {
51
+ var updateQueue = /* @__PURE__ */ new Set();
52
+ var isBatchingUpdates = false;
53
+ var createSignal = (initVal, options) => {
52
54
  const subscribers = /* @__PURE__ */ new Set();
53
55
  let value = initVal;
54
- const getter = () => {
56
+ const getter = (getterOptions) => {
55
57
  if (subscriber !== null) {
56
58
  subscribers.add(subscriber);
57
59
  }
60
+ if ((options == null ? void 0 : options.debugMode) || (getterOptions == null ? void 0 : getterOptions.debugMode)) {
61
+ requestAnimationFrame(() => {
62
+ let label = "anonymous signal";
63
+ if ((options == null ? void 0 : options.label) !== void 0) {
64
+ label = `(${options.label})`;
65
+ if ((getterOptions == null ? void 0 : getterOptions.label) !== void 0) {
66
+ label += ` ${getterOptions.label}`;
67
+ }
68
+ } else if ((getterOptions == null ? void 0 : getterOptions.label) !== void 0) {
69
+ label = getterOptions.label;
70
+ }
71
+ console.log("Signal retrieved:", { value, subscribers, label });
72
+ });
73
+ }
58
74
  return value;
59
75
  };
60
- const setter = (newValue) => {
76
+ const setter = (newValue, setterOptions) => {
77
+ const isObject = typeof newValue === "object" && newValue !== null;
78
+ if (!isObject && value === newValue) return;
79
+ const oldValue = value;
61
80
  value = newValue;
62
81
  for (const fn of subscribers) {
63
- fn();
82
+ updateQueue.add(fn);
83
+ }
84
+ if (!isBatchingUpdates) {
85
+ isBatchingUpdates = true;
86
+ requestAnimationFrame(() => {
87
+ for (const fn of updateQueue) {
88
+ try {
89
+ fn();
90
+ } catch (error) {
91
+ console.error("Error in subscriber:", { error, oldValue, newValue, fn });
92
+ }
93
+ }
94
+ if ((options == null ? void 0 : options.debugMode) || (setterOptions == null ? void 0 : setterOptions.debugMode)) {
95
+ let label = "anonymous signal";
96
+ if ((options == null ? void 0 : options.label) !== void 0) {
97
+ label = `(${options.label})`;
98
+ if ((setterOptions == null ? void 0 : setterOptions.label) !== void 0) {
99
+ label += ` ${setterOptions.label}`;
100
+ }
101
+ } else if ((setterOptions == null ? void 0 : setterOptions.label) !== void 0) {
102
+ label = setterOptions.label;
103
+ }
104
+ console.log("Signal set:", { oldValue, newValue, subscribers, label });
105
+ }
106
+ updateQueue.clear();
107
+ isBatchingUpdates = false;
108
+ });
64
109
  }
65
110
  };
66
111
  return [getter, setter];
@@ -68,16 +113,124 @@ var createSignal = (initVal) => {
68
113
  var derived = (fn) => {
69
114
  const [getter, setter] = createSignal();
70
115
  createEffect(() => {
71
- setter(fn());
116
+ try {
117
+ setter(fn());
118
+ } catch (error) {
119
+ console.error("Error in derived signal:", { error, fn });
120
+ }
72
121
  });
73
122
  return getter;
74
123
  };
75
124
  var createEffect = (fn) => {
76
125
  subscriber = fn;
77
- fn();
126
+ try {
127
+ fn();
128
+ } catch (error) {
129
+ console.error("Error in effect:", { error, fn });
130
+ }
78
131
  subscriber = null;
79
132
  };
80
133
 
134
+ // src/render.ts
135
+ var html = (strings, ...values) => {
136
+ let innerHTML = "";
137
+ const signalMap = /* @__PURE__ */ new Map();
138
+ strings.forEach((string, i) => {
139
+ let value = values[i] ?? "";
140
+ if (typeof value === "function") {
141
+ const uniqueKey = crypto.randomUUID();
142
+ signalMap.set(uniqueKey, value);
143
+ value = `{{signal:${uniqueKey}}}`;
144
+ }
145
+ innerHTML += string + String(value);
146
+ });
147
+ const fragment = parseFragment(innerHTML);
148
+ const callbackBindingRegex = /(\{\{callback:.+\}\})/;
149
+ const signalBindingRegex = /(\{\{signal:.+\}\})/;
150
+ const parseChildren = (element) => {
151
+ for (const child of element.childNodes) {
152
+ if (child instanceof Text && signalBindingRegex.test(child.data)) {
153
+ const textList = child.data.split(signalBindingRegex);
154
+ textList.forEach((text, i) => {
155
+ const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
156
+ const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
157
+ const newText = signal !== null ? signal() : text;
158
+ const newNode = new Text(newText);
159
+ if (i === 0) {
160
+ child.replaceWith(newNode);
161
+ } else {
162
+ element.insertBefore(newNode, child.nextSibling);
163
+ }
164
+ if (signal !== null) {
165
+ createEffect(() => {
166
+ newNode.data = signal();
167
+ });
168
+ }
169
+ });
170
+ }
171
+ if (child instanceof Element) {
172
+ for (const attr of child.attributes) {
173
+ if (signalBindingRegex.test(attr.value)) {
174
+ const textList = attr.value.split(signalBindingRegex);
175
+ createEffect(() => {
176
+ let newText = "";
177
+ for (const text of textList) {
178
+ const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
179
+ const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
180
+ newText += signal !== null ? signal() : text;
181
+ }
182
+ child.setAttribute(attr.name, newText);
183
+ });
184
+ } else if (callbackBindingRegex.test(attr.value)) {
185
+ const uniqueKey = attr.value.replace(/\{\{callback:(.+)\}\}/, "$1");
186
+ child.setAttribute(attr.name, `this.getRootNode().host.__customCallbackFns.get('${uniqueKey}')(event)`);
187
+ }
188
+ }
189
+ parseChildren(child);
190
+ }
191
+ }
192
+ };
193
+ parseChildren(fragment);
194
+ return fragment;
195
+ };
196
+ var _a, _b;
197
+ var adoptedStylesSupported = typeof window !== "undefined" && ((_a = window.ShadowRoot) == null ? void 0 : _a.prototype.hasOwnProperty("adoptedStyleSheets")) && ((_b = window.CSSStyleSheet) == null ? void 0 : _b.prototype.hasOwnProperty("replace"));
198
+ var isCSSStyleSheet = (stylesheet) => {
199
+ return typeof CSSStyleSheet !== "undefined" && stylesheet instanceof CSSStyleSheet;
200
+ };
201
+ var css = (strings, ...values) => {
202
+ let cssText = "";
203
+ const signalMap = /* @__PURE__ */ new Map();
204
+ const signalBindingRegex = /(\{\{signal:.+\}\})/;
205
+ strings.forEach((string, i) => {
206
+ let value = values[i] ?? "";
207
+ if (typeof value === "function") {
208
+ const uniqueKey = crypto.randomUUID();
209
+ signalMap.set(uniqueKey, value);
210
+ value = `{{signal:${uniqueKey}}}`;
211
+ }
212
+ cssText += string + String(value);
213
+ });
214
+ let stylesheet = adoptedStylesSupported ? new CSSStyleSheet() : document.createElement("style");
215
+ const textList = cssText.split(signalBindingRegex);
216
+ createEffect(() => {
217
+ const newCSSTextList = [];
218
+ for (const text of textList) {
219
+ const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
220
+ const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
221
+ const newText = signal !== null ? signal() : text;
222
+ newCSSTextList.push(newText);
223
+ }
224
+ const newCSSText = newCSSTextList.join("");
225
+ if (isCSSStyleSheet(stylesheet)) {
226
+ stylesheet.replace(newCSSText);
227
+ } else {
228
+ stylesheet.textContent = newCSSText;
229
+ }
230
+ });
231
+ return stylesheet;
232
+ };
233
+
81
234
  // src/custom-element.ts
82
235
  var DEFAULT_RENDER_OPTIONS = {
83
236
  formAssociated: false,
@@ -154,7 +307,13 @@ var customElement = (render, options) => {
154
307
  }
155
308
  ),
156
309
  adoptStyleSheet: (stylesheet) => {
157
- this.#shadowRoot.adoptedStyleSheets.push(stylesheet);
310
+ if (isCSSStyleSheet(stylesheet)) {
311
+ this.#shadowRoot.adoptedStyleSheets.push(stylesheet);
312
+ } else {
313
+ requestAnimationFrame(() => {
314
+ this.#shadowRoot.appendChild(stylesheet);
315
+ });
316
+ }
158
317
  }
159
318
  });
160
319
  setInnerHTML(this.#shadowRoot, fragment);
@@ -251,97 +410,6 @@ var createRegistry = () => {
251
410
  getTagName: (element) => registry.get(element)
252
411
  };
253
412
  };
254
-
255
- // src/render.ts
256
- var html = (strings, ...values) => {
257
- let innerHTML = "";
258
- const signalMap = /* @__PURE__ */ new Map();
259
- strings.forEach((string, i) => {
260
- let value = values[i] ?? "";
261
- if (typeof value === "function") {
262
- const uniqueKey = crypto.randomUUID();
263
- signalMap.set(uniqueKey, value);
264
- value = `{{signal:${uniqueKey}}}`;
265
- }
266
- innerHTML += string + String(value);
267
- });
268
- const fragment = parseFragment(innerHTML);
269
- const callbackBindingRegex = /(\{\{callback:.+\}\})/;
270
- const signalBindingRegex = /(\{\{signal:.+\}\})/;
271
- const parseChildren = (element) => {
272
- for (const child of element.childNodes) {
273
- if (child instanceof Text && signalBindingRegex.test(child.data)) {
274
- const textList = child.data.split(signalBindingRegex);
275
- textList.forEach((text, i) => {
276
- const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
277
- const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
278
- const newText = signal !== null ? signal() : text;
279
- const newNode = new Text(newText);
280
- if (i === 0) {
281
- child.replaceWith(newNode);
282
- } else {
283
- element.insertBefore(newNode, child.nextSibling);
284
- }
285
- if (signal !== null) {
286
- createEffect(() => {
287
- newNode.data = signal();
288
- });
289
- }
290
- });
291
- }
292
- if (child instanceof Element) {
293
- for (const attr of child.attributes) {
294
- if (signalBindingRegex.test(attr.value)) {
295
- const textList = attr.value.split(signalBindingRegex);
296
- createEffect(() => {
297
- let newText = "";
298
- for (const text of textList) {
299
- const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
300
- const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
301
- newText += signal !== null ? signal() : text;
302
- }
303
- child.setAttribute(attr.name, newText);
304
- });
305
- } else if (callbackBindingRegex.test(attr.value)) {
306
- const uniqueKey = attr.value.replace(/\{\{callback:(.+)\}\}/, "$1");
307
- child.setAttribute(attr.name, `this.getRootNode().host.__customCallbackFns.get('${uniqueKey}')(event)`);
308
- }
309
- }
310
- parseChildren(child);
311
- }
312
- }
313
- };
314
- parseChildren(fragment);
315
- return fragment;
316
- };
317
- var css = (strings, ...values) => {
318
- let cssText = "";
319
- const signalMap = /* @__PURE__ */ new Map();
320
- const signalBindingRegex = /(\{\{signal:.+\}\})/;
321
- strings.forEach((string, i) => {
322
- let value = values[i] ?? "";
323
- if (typeof value === "function") {
324
- const uniqueKey = crypto.randomUUID();
325
- signalMap.set(uniqueKey, value);
326
- value = `{{signal:${uniqueKey}}}`;
327
- }
328
- cssText += string + String(value);
329
- });
330
- const stylesheet = new CSSStyleSheet();
331
- const textList = cssText.split(signalBindingRegex);
332
- createEffect(() => {
333
- const newCSSTextList = [];
334
- for (const text of textList) {
335
- const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
336
- const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
337
- const newText = signal !== null ? signal() : text;
338
- newCSSTextList.push(newText);
339
- }
340
- const newCSSText = newCSSTextList.join("");
341
- stylesheet.replace(newCSSText);
342
- });
343
- return stylesheet;
344
- };
345
413
  // Annotate the CommonJS export names for ESM import in node:
346
414
  0 && (module.exports = {
347
415
  createEffect,
package/dist/index.d.cts CHANGED
@@ -1,7 +1,15 @@
1
- type SignalGetter<T> = () => T;
2
- type SignalSetter<T> = (newValue: T) => void;
1
+ declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
2
+ type Styles = CSSStyleSheet | HTMLStyleElement;
3
+ declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
4
+
5
+ type SignalOptions = {
6
+ debugMode: boolean;
7
+ label?: string;
8
+ };
9
+ type SignalGetter<T> = (options?: SignalOptions) => T;
10
+ type SignalSetter<T> = (newValue: T, options?: SignalOptions) => void;
3
11
  type Signal<T = unknown> = [SignalGetter<T>, SignalSetter<T>];
4
- declare const createSignal: <T = undefined>(initVal?: T) => Signal<T>;
12
+ declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions) => Signal<T>;
5
13
  declare const derived: <T>(fn: () => T) => SignalGetter<T>;
6
14
  declare const createEffect: (fn: () => void) => void;
7
15
 
@@ -25,7 +33,7 @@ type RenderProps = {
25
33
  customCallback: (fn: () => void) => `{{callback:${string}}}`;
26
34
  attrSignals: Record<string, Signal<string | null>>;
27
35
  refs: Record<string, HTMLElement | null>;
28
- adoptStyleSheet: (stylesheet: CSSStyleSheet) => void;
36
+ adoptStyleSheet: (stylesheet: Styles) => void;
29
37
  };
30
38
  type RenderOptions = {
31
39
  formAssociated: boolean;
@@ -39,7 +47,4 @@ type Registry = {
39
47
  };
40
48
  declare const createRegistry: () => Registry;
41
49
 
42
- declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
43
- declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => CSSStyleSheet;
44
-
45
50
  export { type RenderFunction, type RenderProps, type Signal, type SignalGetter, type SignalSetter, createEffect, createRegistry, createSignal, css, customElement, derived, html };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,15 @@
1
- type SignalGetter<T> = () => T;
2
- type SignalSetter<T> = (newValue: T) => void;
1
+ declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
2
+ type Styles = CSSStyleSheet | HTMLStyleElement;
3
+ declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
4
+
5
+ type SignalOptions = {
6
+ debugMode: boolean;
7
+ label?: string;
8
+ };
9
+ type SignalGetter<T> = (options?: SignalOptions) => T;
10
+ type SignalSetter<T> = (newValue: T, options?: SignalOptions) => void;
3
11
  type Signal<T = unknown> = [SignalGetter<T>, SignalSetter<T>];
4
- declare const createSignal: <T = undefined>(initVal?: T) => Signal<T>;
12
+ declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions) => Signal<T>;
5
13
  declare const derived: <T>(fn: () => T) => SignalGetter<T>;
6
14
  declare const createEffect: (fn: () => void) => void;
7
15
 
@@ -25,7 +33,7 @@ type RenderProps = {
25
33
  customCallback: (fn: () => void) => `{{callback:${string}}}`;
26
34
  attrSignals: Record<string, Signal<string | null>>;
27
35
  refs: Record<string, HTMLElement | null>;
28
- adoptStyleSheet: (stylesheet: CSSStyleSheet) => void;
36
+ adoptStyleSheet: (stylesheet: Styles) => void;
29
37
  };
30
38
  type RenderOptions = {
31
39
  formAssociated: boolean;
@@ -39,7 +47,4 @@ type Registry = {
39
47
  };
40
48
  declare const createRegistry: () => Registry;
41
49
 
42
- declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
43
- declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => CSSStyleSheet;
44
-
45
50
  export { type RenderFunction, type RenderProps, type Signal, type SignalGetter, type SignalSetter, createEffect, createRegistry, createSignal, css, customElement, derived, html };
package/dist/index.js CHANGED
@@ -17,19 +17,64 @@ var setInnerHTML = (element, html2) => {
17
17
 
18
18
  // src/signals.ts
19
19
  var subscriber = null;
20
- var createSignal = (initVal) => {
20
+ var updateQueue = /* @__PURE__ */ new Set();
21
+ var isBatchingUpdates = false;
22
+ var createSignal = (initVal, options) => {
21
23
  const subscribers = /* @__PURE__ */ new Set();
22
24
  let value = initVal;
23
- const getter = () => {
25
+ const getter = (getterOptions) => {
24
26
  if (subscriber !== null) {
25
27
  subscribers.add(subscriber);
26
28
  }
29
+ if ((options == null ? void 0 : options.debugMode) || (getterOptions == null ? void 0 : getterOptions.debugMode)) {
30
+ requestAnimationFrame(() => {
31
+ let label = "anonymous signal";
32
+ if ((options == null ? void 0 : options.label) !== void 0) {
33
+ label = `(${options.label})`;
34
+ if ((getterOptions == null ? void 0 : getterOptions.label) !== void 0) {
35
+ label += ` ${getterOptions.label}`;
36
+ }
37
+ } else if ((getterOptions == null ? void 0 : getterOptions.label) !== void 0) {
38
+ label = getterOptions.label;
39
+ }
40
+ console.log("Signal retrieved:", { value, subscribers, label });
41
+ });
42
+ }
27
43
  return value;
28
44
  };
29
- const setter = (newValue) => {
45
+ const setter = (newValue, setterOptions) => {
46
+ const isObject = typeof newValue === "object" && newValue !== null;
47
+ if (!isObject && value === newValue) return;
48
+ const oldValue = value;
30
49
  value = newValue;
31
50
  for (const fn of subscribers) {
32
- fn();
51
+ updateQueue.add(fn);
52
+ }
53
+ if (!isBatchingUpdates) {
54
+ isBatchingUpdates = true;
55
+ requestAnimationFrame(() => {
56
+ for (const fn of updateQueue) {
57
+ try {
58
+ fn();
59
+ } catch (error) {
60
+ console.error("Error in subscriber:", { error, oldValue, newValue, fn });
61
+ }
62
+ }
63
+ if ((options == null ? void 0 : options.debugMode) || (setterOptions == null ? void 0 : setterOptions.debugMode)) {
64
+ let label = "anonymous signal";
65
+ if ((options == null ? void 0 : options.label) !== void 0) {
66
+ label = `(${options.label})`;
67
+ if ((setterOptions == null ? void 0 : setterOptions.label) !== void 0) {
68
+ label += ` ${setterOptions.label}`;
69
+ }
70
+ } else if ((setterOptions == null ? void 0 : setterOptions.label) !== void 0) {
71
+ label = setterOptions.label;
72
+ }
73
+ console.log("Signal set:", { oldValue, newValue, subscribers, label });
74
+ }
75
+ updateQueue.clear();
76
+ isBatchingUpdates = false;
77
+ });
33
78
  }
34
79
  };
35
80
  return [getter, setter];
@@ -37,16 +82,124 @@ var createSignal = (initVal) => {
37
82
  var derived = (fn) => {
38
83
  const [getter, setter] = createSignal();
39
84
  createEffect(() => {
40
- setter(fn());
85
+ try {
86
+ setter(fn());
87
+ } catch (error) {
88
+ console.error("Error in derived signal:", { error, fn });
89
+ }
41
90
  });
42
91
  return getter;
43
92
  };
44
93
  var createEffect = (fn) => {
45
94
  subscriber = fn;
46
- fn();
95
+ try {
96
+ fn();
97
+ } catch (error) {
98
+ console.error("Error in effect:", { error, fn });
99
+ }
47
100
  subscriber = null;
48
101
  };
49
102
 
103
+ // src/render.ts
104
+ var html = (strings, ...values) => {
105
+ let innerHTML = "";
106
+ const signalMap = /* @__PURE__ */ new Map();
107
+ strings.forEach((string, i) => {
108
+ let value = values[i] ?? "";
109
+ if (typeof value === "function") {
110
+ const uniqueKey = crypto.randomUUID();
111
+ signalMap.set(uniqueKey, value);
112
+ value = `{{signal:${uniqueKey}}}`;
113
+ }
114
+ innerHTML += string + String(value);
115
+ });
116
+ const fragment = parseFragment(innerHTML);
117
+ const callbackBindingRegex = /(\{\{callback:.+\}\})/;
118
+ const signalBindingRegex = /(\{\{signal:.+\}\})/;
119
+ const parseChildren = (element) => {
120
+ for (const child of element.childNodes) {
121
+ if (child instanceof Text && signalBindingRegex.test(child.data)) {
122
+ const textList = child.data.split(signalBindingRegex);
123
+ textList.forEach((text, i) => {
124
+ const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
125
+ const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
126
+ const newText = signal !== null ? signal() : text;
127
+ const newNode = new Text(newText);
128
+ if (i === 0) {
129
+ child.replaceWith(newNode);
130
+ } else {
131
+ element.insertBefore(newNode, child.nextSibling);
132
+ }
133
+ if (signal !== null) {
134
+ createEffect(() => {
135
+ newNode.data = signal();
136
+ });
137
+ }
138
+ });
139
+ }
140
+ if (child instanceof Element) {
141
+ for (const attr of child.attributes) {
142
+ if (signalBindingRegex.test(attr.value)) {
143
+ const textList = attr.value.split(signalBindingRegex);
144
+ createEffect(() => {
145
+ let newText = "";
146
+ for (const text of textList) {
147
+ const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
148
+ const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
149
+ newText += signal !== null ? signal() : text;
150
+ }
151
+ child.setAttribute(attr.name, newText);
152
+ });
153
+ } else if (callbackBindingRegex.test(attr.value)) {
154
+ const uniqueKey = attr.value.replace(/\{\{callback:(.+)\}\}/, "$1");
155
+ child.setAttribute(attr.name, `this.getRootNode().host.__customCallbackFns.get('${uniqueKey}')(event)`);
156
+ }
157
+ }
158
+ parseChildren(child);
159
+ }
160
+ }
161
+ };
162
+ parseChildren(fragment);
163
+ return fragment;
164
+ };
165
+ var _a, _b;
166
+ var adoptedStylesSupported = typeof window !== "undefined" && ((_a = window.ShadowRoot) == null ? void 0 : _a.prototype.hasOwnProperty("adoptedStyleSheets")) && ((_b = window.CSSStyleSheet) == null ? void 0 : _b.prototype.hasOwnProperty("replace"));
167
+ var isCSSStyleSheet = (stylesheet) => {
168
+ return typeof CSSStyleSheet !== "undefined" && stylesheet instanceof CSSStyleSheet;
169
+ };
170
+ var css = (strings, ...values) => {
171
+ let cssText = "";
172
+ const signalMap = /* @__PURE__ */ new Map();
173
+ const signalBindingRegex = /(\{\{signal:.+\}\})/;
174
+ strings.forEach((string, i) => {
175
+ let value = values[i] ?? "";
176
+ if (typeof value === "function") {
177
+ const uniqueKey = crypto.randomUUID();
178
+ signalMap.set(uniqueKey, value);
179
+ value = `{{signal:${uniqueKey}}}`;
180
+ }
181
+ cssText += string + String(value);
182
+ });
183
+ let stylesheet = adoptedStylesSupported ? new CSSStyleSheet() : document.createElement("style");
184
+ const textList = cssText.split(signalBindingRegex);
185
+ createEffect(() => {
186
+ const newCSSTextList = [];
187
+ for (const text of textList) {
188
+ const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
189
+ const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
190
+ const newText = signal !== null ? signal() : text;
191
+ newCSSTextList.push(newText);
192
+ }
193
+ const newCSSText = newCSSTextList.join("");
194
+ if (isCSSStyleSheet(stylesheet)) {
195
+ stylesheet.replace(newCSSText);
196
+ } else {
197
+ stylesheet.textContent = newCSSText;
198
+ }
199
+ });
200
+ return stylesheet;
201
+ };
202
+
50
203
  // src/custom-element.ts
51
204
  var DEFAULT_RENDER_OPTIONS = {
52
205
  formAssociated: false,
@@ -123,7 +276,13 @@ var customElement = (render, options) => {
123
276
  }
124
277
  ),
125
278
  adoptStyleSheet: (stylesheet) => {
126
- this.#shadowRoot.adoptedStyleSheets.push(stylesheet);
279
+ if (isCSSStyleSheet(stylesheet)) {
280
+ this.#shadowRoot.adoptedStyleSheets.push(stylesheet);
281
+ } else {
282
+ requestAnimationFrame(() => {
283
+ this.#shadowRoot.appendChild(stylesheet);
284
+ });
285
+ }
127
286
  }
128
287
  });
129
288
  setInnerHTML(this.#shadowRoot, fragment);
@@ -220,97 +379,6 @@ var createRegistry = () => {
220
379
  getTagName: (element) => registry.get(element)
221
380
  };
222
381
  };
223
-
224
- // src/render.ts
225
- var html = (strings, ...values) => {
226
- let innerHTML = "";
227
- const signalMap = /* @__PURE__ */ new Map();
228
- strings.forEach((string, i) => {
229
- let value = values[i] ?? "";
230
- if (typeof value === "function") {
231
- const uniqueKey = crypto.randomUUID();
232
- signalMap.set(uniqueKey, value);
233
- value = `{{signal:${uniqueKey}}}`;
234
- }
235
- innerHTML += string + String(value);
236
- });
237
- const fragment = parseFragment(innerHTML);
238
- const callbackBindingRegex = /(\{\{callback:.+\}\})/;
239
- const signalBindingRegex = /(\{\{signal:.+\}\})/;
240
- const parseChildren = (element) => {
241
- for (const child of element.childNodes) {
242
- if (child instanceof Text && signalBindingRegex.test(child.data)) {
243
- const textList = child.data.split(signalBindingRegex);
244
- textList.forEach((text, i) => {
245
- const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
246
- const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
247
- const newText = signal !== null ? signal() : text;
248
- const newNode = new Text(newText);
249
- if (i === 0) {
250
- child.replaceWith(newNode);
251
- } else {
252
- element.insertBefore(newNode, child.nextSibling);
253
- }
254
- if (signal !== null) {
255
- createEffect(() => {
256
- newNode.data = signal();
257
- });
258
- }
259
- });
260
- }
261
- if (child instanceof Element) {
262
- for (const attr of child.attributes) {
263
- if (signalBindingRegex.test(attr.value)) {
264
- const textList = attr.value.split(signalBindingRegex);
265
- createEffect(() => {
266
- let newText = "";
267
- for (const text of textList) {
268
- const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
269
- const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
270
- newText += signal !== null ? signal() : text;
271
- }
272
- child.setAttribute(attr.name, newText);
273
- });
274
- } else if (callbackBindingRegex.test(attr.value)) {
275
- const uniqueKey = attr.value.replace(/\{\{callback:(.+)\}\}/, "$1");
276
- child.setAttribute(attr.name, `this.getRootNode().host.__customCallbackFns.get('${uniqueKey}')(event)`);
277
- }
278
- }
279
- parseChildren(child);
280
- }
281
- }
282
- };
283
- parseChildren(fragment);
284
- return fragment;
285
- };
286
- var css = (strings, ...values) => {
287
- let cssText = "";
288
- const signalMap = /* @__PURE__ */ new Map();
289
- const signalBindingRegex = /(\{\{signal:.+\}\})/;
290
- strings.forEach((string, i) => {
291
- let value = values[i] ?? "";
292
- if (typeof value === "function") {
293
- const uniqueKey = crypto.randomUUID();
294
- signalMap.set(uniqueKey, value);
295
- value = `{{signal:${uniqueKey}}}`;
296
- }
297
- cssText += string + String(value);
298
- });
299
- const stylesheet = new CSSStyleSheet();
300
- const textList = cssText.split(signalBindingRegex);
301
- createEffect(() => {
302
- const newCSSTextList = [];
303
- for (const text of textList) {
304
- const uniqueKey = text.replace(/\{\{signal:(.+)\}\}/, "$1");
305
- const signal = uniqueKey !== text ? signalMap.get(uniqueKey) : null;
306
- const newText = signal !== null ? signal() : text;
307
- newCSSTextList.push(newText);
308
- }
309
- const newCSSText = newCSSTextList.join("");
310
- stylesheet.replace(newCSSText);
311
- });
312
- return stylesheet;
313
- };
314
382
  export {
315
383
  createEffect,
316
384
  createRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunderous",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",