thunderous 2.4.2 → 2.4.4

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/index.cjs CHANGED
@@ -20,6 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ clearRenderState: () => clearRenderState,
24
+ clearServerCss: () => clearServerCss,
23
25
  clientOnlyCallback: () => clientOnlyCallback,
24
26
  createEffect: () => createEffect,
25
27
  createRegistry: () => createRegistry,
@@ -184,6 +186,9 @@ var serverDefineFns = /* @__PURE__ */ new Set();
184
186
  var onServerDefine = (fn) => {
185
187
  serverDefineFns.add(fn);
186
188
  };
189
+ var clearServerCss = () => {
190
+ serverCss.clear();
191
+ };
187
192
  var serverDefine = ({
188
193
  tagName,
189
194
  serverRender,
@@ -299,8 +304,9 @@ var wrapTemplate = ({ tagName, serverRender, options }) => {
299
304
  return finalScopedRenderString;
300
305
  };
301
306
  var insertTemplates = (tagName, template, inputString) => {
302
- return inputString.replace(new RegExp(`(<s*${tagName}([^>]*)>)`, "gm"), ($1, _, $3) => {
303
- const attrs = $3.split(/(?<=")\s+/).filter((attr) => attr.trim() !== "").map((attr) => {
307
+ const tagRegex = new RegExp(`<\\s*${tagName}((?:\\s+[^>]*)*)\\s*>`, "gm");
308
+ return inputString.replace(tagRegex, ($match, $1) => {
309
+ const attrs = $1.split(/(?<=")\s+/).filter((attr) => attr.trim() !== "").map((attr) => {
304
310
  const [_key, _value] = attr.split("=");
305
311
  const key = _key.trim();
306
312
  const value = _value?.replace(/"/g, "") ?? "";
@@ -310,7 +316,7 @@ var insertTemplates = (tagName, template, inputString) => {
310
316
  for (const [key, value] of attrs) {
311
317
  scopedResult = scopedResult.replace(new RegExp(`{{attr:${key}}}`, "gm"), value);
312
318
  }
313
- return $1 + scopedResult;
319
+ return $match + scopedResult;
314
320
  });
315
321
  };
316
322
  var clientOnlyCallback = (fn) => {
@@ -331,6 +337,13 @@ var renderState = {
331
337
  propertyMap: /* @__PURE__ */ new Map(),
332
338
  registry: typeof customElements !== "undefined" ? customElements : {}
333
339
  };
340
+ var clearRenderState = () => {
341
+ renderState.signalMap.clear();
342
+ renderState.callbackMap.clear();
343
+ renderState.propertyMap.clear();
344
+ renderState.fragmentMap.clear();
345
+ renderState.childrenMap.clear();
346
+ };
334
347
  var logPropertyWarning = (propName, element) => {
335
348
  console.warn(
336
349
  `Property "${propName}" does not exist on element:`,
@@ -340,7 +353,7 @@ var logPropertyWarning = (propName, element) => {
340
353
  };
341
354
  var asNodeList = (value, parent) => {
342
355
  if (typeof value === "string") return [new Text(value)];
343
- if (value instanceof DocumentFragment) return [...value.children];
356
+ if (value instanceof DocumentFragment) return Array.from(value.children);
344
357
  if (Array.isArray(value)) {
345
358
  const nodeList = [];
346
359
  let count = 0;
@@ -406,7 +419,7 @@ var processValue = (value) => {
406
419
  return String(value);
407
420
  };
408
421
  var evaluateBindings = (element, fragment) => {
409
- for (const child of [...element.childNodes]) {
422
+ for (const child of Array.from(element.childNodes)) {
410
423
  if (child instanceof Text && SIGNAL_BINDING_REGEX.test(child.data)) {
411
424
  const textList = child.data.split(SIGNAL_BINDING_REGEX);
412
425
  const nextSibling = child.nextSibling;
@@ -485,10 +498,10 @@ var evaluateBindings = (element, fragment) => {
485
498
  persistedChild.remove();
486
499
  continue;
487
500
  }
488
- for (const attr of [...persistedChild.attributes]) {
501
+ for (const attr of Array.from(persistedChild.attributes)) {
489
502
  if (!newChild.hasAttribute(attr.name)) persistedChild.removeAttribute(attr.name);
490
503
  }
491
- for (const newAttr of [...newChild.attributes]) {
504
+ for (const newAttr of Array.from(newChild.attributes)) {
492
505
  const oldAttrValue = persistedChild.getAttribute(newAttr.name);
493
506
  if (oldAttrValue?.startsWith("this.__customCallbackFns")) continue;
494
507
  persistedChild.setAttribute(newAttr.name, newAttr.value);
@@ -503,7 +516,7 @@ var evaluateBindings = (element, fragment) => {
503
516
  };
504
517
  const bindFragment = (signal2) => {
505
518
  const initialFragment = signal2();
506
- renderState.childrenMap.set(initialFragment, [...initialFragment.childNodes]);
519
+ renderState.childrenMap.set(initialFragment, Array.from(initialFragment.childNodes));
507
520
  createEffect(({ destroy }) => {
508
521
  const result = signal2();
509
522
  const cachedChildren = renderState.childrenMap.get(initialFragment);
@@ -545,7 +558,7 @@ var evaluateBindings = (element, fragment) => {
545
558
  child.replaceWith(childFragment);
546
559
  }
547
560
  } else if (child instanceof Element) {
548
- for (const attr of [...child.attributes]) {
561
+ for (const attr of Array.from(child.attributes)) {
549
562
  const attrName = attr.name;
550
563
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
551
564
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
@@ -1175,6 +1188,8 @@ var createRegistry = (args) => {
1175
1188
  };
1176
1189
  // Annotate the CommonJS export names for ESM import in node:
1177
1190
  0 && (module.exports = {
1191
+ clearRenderState,
1192
+ clearServerCss,
1178
1193
  clientOnlyCallback,
1179
1194
  createEffect,
1180
1195
  createRegistry,
package/dist/index.d.cts CHANGED
@@ -143,7 +143,41 @@ declare const customElement: <Props extends CustomElementProps>(render: RenderFu
143
143
  */
144
144
  declare const createRegistry: (args?: RegistryArgs) => RegistryResult;
145
145
 
146
+ /**
147
+ * Add a callback to handle each call to `define()` on the server.
148
+ *
149
+ * This enables you to intercept those definitions and respond to them,
150
+ * for example to inject declarative shadow DOM templates.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * let response = originalResponse;
155
+ * onServerDefine((tagName, htmlString) => {
156
+ * // ...
157
+ * response = htmlString.replace(tagName, `my-${tagName}`);
158
+ * });
159
+ * ```
160
+ */
146
161
  declare const onServerDefine: (fn: ServerDefineFn) => void;
162
+ /**
163
+ * Thunderous tracks its state using several maps to associate values with
164
+ * their respective elements.
165
+ *
166
+ * This function clears the map that tracks CSS on the server side, to prevent
167
+ * memory leaks and purge stale data from previous renders.
168
+ *
169
+ * If you are building a framework or plugin that depends on Thunderous, you
170
+ * should call this function before every render. Otherwise, the map will
171
+ * accumulate stale data and may create significant performance issues.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * import { clearServerCss } from 'thunderous'
176
+ *
177
+ * clearServerCss();
178
+ * ```
179
+ */
180
+ declare const clearServerCss: () => void;
147
181
  declare const insertTemplates: (tagName: string, template: string, inputString: string) => string;
148
182
  declare const clientOnlyCallback: (fn: (() => void) | (() => Promise<void>)) => void | Promise<void>;
149
183
 
@@ -178,10 +212,29 @@ declare const derived: <T>(fn: () => T, options?: SignalOptions) => SignalGetter
178
212
  */
179
213
  declare const createEffect: <T = unknown>(fn: Effect<T>, value?: T) => void;
180
214
 
215
+ /**
216
+ * Thunderous tracks its state using several maps to associate values with
217
+ * their respective elements.
218
+ *
219
+ * This function clears the maps tracking render state, to prevent memory
220
+ * leaks and purge stale data from previous renders.
221
+ *
222
+ * If you are building a framework or plugin that depends on Thunderous, you
223
+ * should call this function before every render. Otherwise, the maps will
224
+ * accumulate stale data and may create significant performance issues.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * import { clearRenderState } from 'thunderous'
229
+ *
230
+ * clearRenderState();
231
+ * ```
232
+ */
233
+ declare const clearRenderState: () => void;
181
234
  /**
182
235
  * A tagged template function for creating DocumentFragment instances.
183
236
  */
184
237
  declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
185
238
  declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
186
239
 
187
- export { type ElementResult, type HTMLCustomElement, type RegistryResult, type RenderArgs, type RenderFunction, type Signal, type SignalGetter, type SignalSetter, clientOnlyCallback, createEffect, createRegistry, createSignal, css, customElement, derived, html, insertTemplates, onServerDefine };
240
+ export { type ElementResult, type HTMLCustomElement, type RegistryResult, type RenderArgs, type RenderFunction, type Signal, type SignalGetter, type SignalSetter, clearRenderState, clearServerCss, clientOnlyCallback, createEffect, createRegistry, createSignal, css, customElement, derived, html, insertTemplates, onServerDefine };
package/dist/index.d.ts CHANGED
@@ -143,7 +143,41 @@ declare const customElement: <Props extends CustomElementProps>(render: RenderFu
143
143
  */
144
144
  declare const createRegistry: (args?: RegistryArgs) => RegistryResult;
145
145
 
146
+ /**
147
+ * Add a callback to handle each call to `define()` on the server.
148
+ *
149
+ * This enables you to intercept those definitions and respond to them,
150
+ * for example to inject declarative shadow DOM templates.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * let response = originalResponse;
155
+ * onServerDefine((tagName, htmlString) => {
156
+ * // ...
157
+ * response = htmlString.replace(tagName, `my-${tagName}`);
158
+ * });
159
+ * ```
160
+ */
146
161
  declare const onServerDefine: (fn: ServerDefineFn) => void;
162
+ /**
163
+ * Thunderous tracks its state using several maps to associate values with
164
+ * their respective elements.
165
+ *
166
+ * This function clears the map that tracks CSS on the server side, to prevent
167
+ * memory leaks and purge stale data from previous renders.
168
+ *
169
+ * If you are building a framework or plugin that depends on Thunderous, you
170
+ * should call this function before every render. Otherwise, the map will
171
+ * accumulate stale data and may create significant performance issues.
172
+ *
173
+ * @example
174
+ * ```ts
175
+ * import { clearServerCss } from 'thunderous'
176
+ *
177
+ * clearServerCss();
178
+ * ```
179
+ */
180
+ declare const clearServerCss: () => void;
147
181
  declare const insertTemplates: (tagName: string, template: string, inputString: string) => string;
148
182
  declare const clientOnlyCallback: (fn: (() => void) | (() => Promise<void>)) => void | Promise<void>;
149
183
 
@@ -178,10 +212,29 @@ declare const derived: <T>(fn: () => T, options?: SignalOptions) => SignalGetter
178
212
  */
179
213
  declare const createEffect: <T = unknown>(fn: Effect<T>, value?: T) => void;
180
214
 
215
+ /**
216
+ * Thunderous tracks its state using several maps to associate values with
217
+ * their respective elements.
218
+ *
219
+ * This function clears the maps tracking render state, to prevent memory
220
+ * leaks and purge stale data from previous renders.
221
+ *
222
+ * If you are building a framework or plugin that depends on Thunderous, you
223
+ * should call this function before every render. Otherwise, the maps will
224
+ * accumulate stale data and may create significant performance issues.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * import { clearRenderState } from 'thunderous'
229
+ *
230
+ * clearRenderState();
231
+ * ```
232
+ */
233
+ declare const clearRenderState: () => void;
181
234
  /**
182
235
  * A tagged template function for creating DocumentFragment instances.
183
236
  */
184
237
  declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
185
238
  declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
186
239
 
187
- export { type ElementResult, type HTMLCustomElement, type RegistryResult, type RenderArgs, type RenderFunction, type Signal, type SignalGetter, type SignalSetter, clientOnlyCallback, createEffect, createRegistry, createSignal, css, customElement, derived, html, insertTemplates, onServerDefine };
240
+ export { type ElementResult, type HTMLCustomElement, type RegistryResult, type RenderArgs, type RenderFunction, type Signal, type SignalGetter, type SignalSetter, clearRenderState, clearServerCss, clientOnlyCallback, createEffect, createRegistry, createSignal, css, customElement, derived, html, insertTemplates, onServerDefine };
package/dist/index.js CHANGED
@@ -149,6 +149,9 @@ var serverDefineFns = /* @__PURE__ */ new Set();
149
149
  var onServerDefine = (fn) => {
150
150
  serverDefineFns.add(fn);
151
151
  };
152
+ var clearServerCss = () => {
153
+ serverCss.clear();
154
+ };
152
155
  var serverDefine = ({
153
156
  tagName,
154
157
  serverRender,
@@ -264,8 +267,9 @@ var wrapTemplate = ({ tagName, serverRender, options }) => {
264
267
  return finalScopedRenderString;
265
268
  };
266
269
  var insertTemplates = (tagName, template, inputString) => {
267
- return inputString.replace(new RegExp(`(<s*${tagName}([^>]*)>)`, "gm"), ($1, _, $3) => {
268
- const attrs = $3.split(/(?<=")\s+/).filter((attr) => attr.trim() !== "").map((attr) => {
270
+ const tagRegex = new RegExp(`<\\s*${tagName}((?:\\s+[^>]*)*)\\s*>`, "gm");
271
+ return inputString.replace(tagRegex, ($match, $1) => {
272
+ const attrs = $1.split(/(?<=")\s+/).filter((attr) => attr.trim() !== "").map((attr) => {
269
273
  const [_key, _value] = attr.split("=");
270
274
  const key = _key.trim();
271
275
  const value = _value?.replace(/"/g, "") ?? "";
@@ -275,7 +279,7 @@ var insertTemplates = (tagName, template, inputString) => {
275
279
  for (const [key, value] of attrs) {
276
280
  scopedResult = scopedResult.replace(new RegExp(`{{attr:${key}}}`, "gm"), value);
277
281
  }
278
- return $1 + scopedResult;
282
+ return $match + scopedResult;
279
283
  });
280
284
  };
281
285
  var clientOnlyCallback = (fn) => {
@@ -296,6 +300,13 @@ var renderState = {
296
300
  propertyMap: /* @__PURE__ */ new Map(),
297
301
  registry: typeof customElements !== "undefined" ? customElements : {}
298
302
  };
303
+ var clearRenderState = () => {
304
+ renderState.signalMap.clear();
305
+ renderState.callbackMap.clear();
306
+ renderState.propertyMap.clear();
307
+ renderState.fragmentMap.clear();
308
+ renderState.childrenMap.clear();
309
+ };
299
310
  var logPropertyWarning = (propName, element) => {
300
311
  console.warn(
301
312
  `Property "${propName}" does not exist on element:`,
@@ -305,7 +316,7 @@ var logPropertyWarning = (propName, element) => {
305
316
  };
306
317
  var asNodeList = (value, parent) => {
307
318
  if (typeof value === "string") return [new Text(value)];
308
- if (value instanceof DocumentFragment) return [...value.children];
319
+ if (value instanceof DocumentFragment) return Array.from(value.children);
309
320
  if (Array.isArray(value)) {
310
321
  const nodeList = [];
311
322
  let count = 0;
@@ -371,7 +382,7 @@ var processValue = (value) => {
371
382
  return String(value);
372
383
  };
373
384
  var evaluateBindings = (element, fragment) => {
374
- for (const child of [...element.childNodes]) {
385
+ for (const child of Array.from(element.childNodes)) {
375
386
  if (child instanceof Text && SIGNAL_BINDING_REGEX.test(child.data)) {
376
387
  const textList = child.data.split(SIGNAL_BINDING_REGEX);
377
388
  const nextSibling = child.nextSibling;
@@ -450,10 +461,10 @@ var evaluateBindings = (element, fragment) => {
450
461
  persistedChild.remove();
451
462
  continue;
452
463
  }
453
- for (const attr of [...persistedChild.attributes]) {
464
+ for (const attr of Array.from(persistedChild.attributes)) {
454
465
  if (!newChild.hasAttribute(attr.name)) persistedChild.removeAttribute(attr.name);
455
466
  }
456
- for (const newAttr of [...newChild.attributes]) {
467
+ for (const newAttr of Array.from(newChild.attributes)) {
457
468
  const oldAttrValue = persistedChild.getAttribute(newAttr.name);
458
469
  if (oldAttrValue?.startsWith("this.__customCallbackFns")) continue;
459
470
  persistedChild.setAttribute(newAttr.name, newAttr.value);
@@ -468,7 +479,7 @@ var evaluateBindings = (element, fragment) => {
468
479
  };
469
480
  const bindFragment = (signal2) => {
470
481
  const initialFragment = signal2();
471
- renderState.childrenMap.set(initialFragment, [...initialFragment.childNodes]);
482
+ renderState.childrenMap.set(initialFragment, Array.from(initialFragment.childNodes));
472
483
  createEffect(({ destroy }) => {
473
484
  const result = signal2();
474
485
  const cachedChildren = renderState.childrenMap.get(initialFragment);
@@ -510,7 +521,7 @@ var evaluateBindings = (element, fragment) => {
510
521
  child.replaceWith(childFragment);
511
522
  }
512
523
  } else if (child instanceof Element) {
513
- for (const attr of [...child.attributes]) {
524
+ for (const attr of Array.from(child.attributes)) {
514
525
  const attrName = attr.name;
515
526
  if (SIGNAL_BINDING_REGEX.test(attr.value)) {
516
527
  const textList = attr.value.split(SIGNAL_BINDING_REGEX);
@@ -1139,6 +1150,8 @@ var createRegistry = (args) => {
1139
1150
  };
1140
1151
  };
1141
1152
  export {
1153
+ clearRenderState,
1154
+ clearServerCss,
1142
1155
  clientOnlyCallback,
1143
1156
  createEffect,
1144
1157
  createRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thunderous",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
4
4
  "description": "A lightweight, functional web components library that brings the power of signals to your UI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",