thunderous 2.4.1 → 2.4.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/index.cjs CHANGED
@@ -49,14 +49,14 @@ var DEFAULT_RENDER_OPTIONS = {
49
49
  };
50
50
 
51
51
  // src/signals.ts
52
- var sym = null;
52
+ var ident = null;
53
53
  var effects = /* @__PURE__ */ new WeakMap();
54
54
  var createSignal = (initVal, options) => {
55
55
  const subscribers = /* @__PURE__ */ new Set();
56
56
  let value = initVal;
57
57
  const getter = (getterOptions) => {
58
- if (sym !== null) {
59
- subscribers.add(sym);
58
+ if (ident !== null) {
59
+ subscribers.add(ident);
60
60
  }
61
61
  if (options?.debugMode === true || getterOptions?.debugMode === true) {
62
62
  let label = "anonymous signal";
@@ -70,7 +70,7 @@ var createSignal = (initVal, options) => {
70
70
  }
71
71
  console.log("Signal retrieved:", {
72
72
  value,
73
- subscribers: Array.from(subscribers).map((sym2) => effects.get(sym2)),
73
+ subscribers: Array.from(subscribers).map((sym) => effects.get(sym)),
74
74
  label
75
75
  });
76
76
  }
@@ -96,22 +96,22 @@ var createSignal = (initVal, options) => {
96
96
  }
97
97
  const oldValue = value;
98
98
  value = newValue;
99
- for (const sym2 of subscribers) {
100
- const effectRef = effects.get(sym2);
99
+ for (const sym of subscribers) {
100
+ const effectRef = effects.get(sym);
101
101
  if (effectRef !== void 0) {
102
102
  try {
103
103
  effectRef.fn({
104
104
  lastValue: effectRef.value,
105
105
  destroy: () => {
106
- effects.delete(sym2);
107
- queueMicrotask(() => subscribers.delete(sym2));
106
+ effects.delete(sym);
107
+ queueMicrotask(() => subscribers.delete(sym));
108
108
  }
109
109
  });
110
110
  } catch (error) {
111
111
  console.error("Error in subscriber:", { error, oldValue, newValue, fn: effectRef.fn });
112
112
  }
113
113
  } else {
114
- queueMicrotask(() => subscribers.delete(sym2));
114
+ queueMicrotask(() => subscribers.delete(sym));
115
115
  }
116
116
  }
117
117
  if (options?.debugMode === true || setterOptions?.debugMode === true) {
@@ -127,7 +127,7 @@ var createSignal = (initVal, options) => {
127
127
  console.log("Signal set:", {
128
128
  oldValue,
129
129
  newValue,
130
- subscribers: Array.from(subscribers).map((sym2) => effects.get(sym2)),
130
+ subscribers: Array.from(subscribers).map((sym) => effects.get(sym)),
131
131
  label
132
132
  });
133
133
  }
@@ -146,19 +146,19 @@ var derived = (fn, options) => {
146
146
  return getter;
147
147
  };
148
148
  var createEffect = (fn, value) => {
149
- const privateSym = sym = Symbol();
150
- effects.set(sym, { fn, value });
149
+ const privateIdent = ident = {};
150
+ effects.set(ident, { fn, value });
151
151
  try {
152
152
  fn({
153
153
  lastValue: value,
154
154
  destroy: () => {
155
- effects.delete(privateSym);
155
+ effects.delete(privateIdent);
156
156
  }
157
157
  });
158
158
  } catch (error) {
159
159
  console.error("Error in effect:", { error, fn });
160
160
  }
161
- sym = null;
161
+ ident = null;
162
162
  };
163
163
 
164
164
  // src/utilities.ts
@@ -299,8 +299,9 @@ var wrapTemplate = ({ tagName, serverRender, options }) => {
299
299
  return finalScopedRenderString;
300
300
  };
301
301
  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) => {
302
+ const tagRegex = new RegExp(`<\\s*${tagName}((?:\\s+[^>]*)*)\\s*>`, "gm");
303
+ return inputString.replace(tagRegex, ($match, $1) => {
304
+ const attrs = $1.split(/(?<=")\s+/).filter((attr) => attr.trim() !== "").map((attr) => {
304
305
  const [_key, _value] = attr.split("=");
305
306
  const key = _key.trim();
306
307
  const value = _value?.replace(/"/g, "") ?? "";
@@ -310,7 +311,7 @@ var insertTemplates = (tagName, template, inputString) => {
310
311
  for (const [key, value] of attrs) {
311
312
  scopedResult = scopedResult.replace(new RegExp(`{{attr:${key}}}`, "gm"), value);
312
313
  }
313
- return $1 + scopedResult;
314
+ return $match + scopedResult;
314
315
  });
315
316
  };
316
317
  var clientOnlyCallback = (fn) => {
@@ -459,10 +460,6 @@ var evaluateBindings = (element, fragment) => {
459
460
  createEffect(
460
461
  ({ lastValue: oldChildren, destroy }) => {
461
462
  const result = signal2();
462
- console.trace("Binding array:", {
463
- result: result.map((node) => node.cloneNode(true)),
464
- oldChildren
465
- });
466
463
  const newChildren = asNodeList(result, element);
467
464
  const firstChild = newChildren[0];
468
465
  if (!Array.isArray(result) && newChildren.length === 1 && firstChild instanceof DocumentFragment) {
@@ -712,7 +709,7 @@ var css = (strings, ...values) => {
712
709
  }
713
710
  const newCSSText = newCSSTextList.join("");
714
711
  if (isCSSStyleSheet(stylesheet)) {
715
- stylesheet.replace(newCSSText).catch(console.error);
712
+ stylesheet.replaceSync(newCSSText);
716
713
  } else {
717
714
  stylesheet.textContent = newCSSText;
718
715
  }
@@ -741,6 +738,7 @@ var customElement = (render, options) => {
741
738
  if (shadowRootOptions.registry !== void 0 && "scoped" in shadowRootOptions.registry && shadowRootOptions.registry.scoped) {
742
739
  return shadowRootOptions.registry;
743
740
  }
741
+ return void 0;
744
742
  })();
745
743
  return {
746
744
  define(tagName) {
@@ -800,8 +798,8 @@ var customElement = (render, options) => {
800
798
  #formResetCallbackFns = /* @__PURE__ */ new Set();
801
799
  #formStateRestoreCallbackFns = /* @__PURE__ */ new Set();
802
800
  #clientOnlyCallbackFns = /* @__PURE__ */ new Set();
803
- #shadowRoot = attachShadow ? this.attachShadow(shadowRootOptions) : null;
804
801
  #internals = this.attachInternals();
802
+ #shadowRoot = attachShadow ? this.#internals.shadowRoot ?? this.attachShadow(shadowRootOptions) : null;
805
803
  #observer = options?.observedAttributes !== void 0 ? null : new MutationObserver((mutations) => {
806
804
  for (const mutation of mutations) {
807
805
  const attrName = mutation.attributeName;
@@ -854,7 +852,7 @@ Element: <${this.tagName.toLowerCase()}>
854
852
  this[prop] = newValue;
855
853
  _setter(newValue, options2);
856
854
  };
857
- const getter = (options2) => {
855
+ const getter = ((options2) => {
858
856
  const value = _getter(options2);
859
857
  if (value === void 0 && !allowUndefined) {
860
858
  const error = new Error(
@@ -866,7 +864,7 @@ You must set an initial value before calling a property signal's getter.
866
864
  throw error;
867
865
  }
868
866
  return value;
869
- };
867
+ });
870
868
  getter.getter = true;
871
869
  const publicSignal = [getter, setter];
872
870
  publicSignal.init = (value) => {
package/dist/index.d.cts CHANGED
@@ -107,13 +107,11 @@ type Signal<T = unknown> = [SignalGetter<T>, SignalSetter<T>];
107
107
  type SignalWithInit<T = unknown> = Signal<T> & { init: (value: T) => Signal<T> };
108
108
 
109
109
  // Flexible typing is necessary to support generic functions
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
110
  type AnyFn = (...args: any[]) => any;
112
111
 
113
112
  type HTMLCustomElement<T extends Record<PropertyKey, unknown>> = Omit<HTMLElement, keyof T> & T;
114
113
 
115
114
  // Again, flexible typing is necessary to support these generics
116
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
115
  type Effect<T = any> = (args: { lastValue: T; destroy: () => void }) => T | void;
118
116
 
119
117
  /**
@@ -186,4 +184,4 @@ declare const createEffect: <T = unknown>(fn: Effect<T>, value?: T) => void;
186
184
  declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
187
185
  declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
188
186
 
189
- export { type HTMLCustomElement, type RenderArgs, type RenderFunction, type Signal, type SignalGetter, type SignalSetter, clientOnlyCallback, createEffect, createRegistry, createSignal, css, customElement, derived, html, insertTemplates, onServerDefine };
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 };
package/dist/index.d.ts CHANGED
@@ -107,13 +107,11 @@ type Signal<T = unknown> = [SignalGetter<T>, SignalSetter<T>];
107
107
  type SignalWithInit<T = unknown> = Signal<T> & { init: (value: T) => Signal<T> };
108
108
 
109
109
  // Flexible typing is necessary to support generic functions
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
110
  type AnyFn = (...args: any[]) => any;
112
111
 
113
112
  type HTMLCustomElement<T extends Record<PropertyKey, unknown>> = Omit<HTMLElement, keyof T> & T;
114
113
 
115
114
  // Again, flexible typing is necessary to support these generics
116
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
115
  type Effect<T = any> = (args: { lastValue: T; destroy: () => void }) => T | void;
118
116
 
119
117
  /**
@@ -186,4 +184,4 @@ declare const createEffect: <T = unknown>(fn: Effect<T>, value?: T) => void;
186
184
  declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
187
185
  declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
188
186
 
189
- export { type HTMLCustomElement, type RenderArgs, type RenderFunction, type Signal, type SignalGetter, type SignalSetter, clientOnlyCallback, createEffect, createRegistry, createSignal, css, customElement, derived, html, insertTemplates, onServerDefine };
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 };
package/dist/index.js CHANGED
@@ -14,14 +14,14 @@ var DEFAULT_RENDER_OPTIONS = {
14
14
  };
15
15
 
16
16
  // src/signals.ts
17
- var sym = null;
17
+ var ident = null;
18
18
  var effects = /* @__PURE__ */ new WeakMap();
19
19
  var createSignal = (initVal, options) => {
20
20
  const subscribers = /* @__PURE__ */ new Set();
21
21
  let value = initVal;
22
22
  const getter = (getterOptions) => {
23
- if (sym !== null) {
24
- subscribers.add(sym);
23
+ if (ident !== null) {
24
+ subscribers.add(ident);
25
25
  }
26
26
  if (options?.debugMode === true || getterOptions?.debugMode === true) {
27
27
  let label = "anonymous signal";
@@ -35,7 +35,7 @@ var createSignal = (initVal, options) => {
35
35
  }
36
36
  console.log("Signal retrieved:", {
37
37
  value,
38
- subscribers: Array.from(subscribers).map((sym2) => effects.get(sym2)),
38
+ subscribers: Array.from(subscribers).map((sym) => effects.get(sym)),
39
39
  label
40
40
  });
41
41
  }
@@ -61,22 +61,22 @@ var createSignal = (initVal, options) => {
61
61
  }
62
62
  const oldValue = value;
63
63
  value = newValue;
64
- for (const sym2 of subscribers) {
65
- const effectRef = effects.get(sym2);
64
+ for (const sym of subscribers) {
65
+ const effectRef = effects.get(sym);
66
66
  if (effectRef !== void 0) {
67
67
  try {
68
68
  effectRef.fn({
69
69
  lastValue: effectRef.value,
70
70
  destroy: () => {
71
- effects.delete(sym2);
72
- queueMicrotask(() => subscribers.delete(sym2));
71
+ effects.delete(sym);
72
+ queueMicrotask(() => subscribers.delete(sym));
73
73
  }
74
74
  });
75
75
  } catch (error) {
76
76
  console.error("Error in subscriber:", { error, oldValue, newValue, fn: effectRef.fn });
77
77
  }
78
78
  } else {
79
- queueMicrotask(() => subscribers.delete(sym2));
79
+ queueMicrotask(() => subscribers.delete(sym));
80
80
  }
81
81
  }
82
82
  if (options?.debugMode === true || setterOptions?.debugMode === true) {
@@ -92,7 +92,7 @@ var createSignal = (initVal, options) => {
92
92
  console.log("Signal set:", {
93
93
  oldValue,
94
94
  newValue,
95
- subscribers: Array.from(subscribers).map((sym2) => effects.get(sym2)),
95
+ subscribers: Array.from(subscribers).map((sym) => effects.get(sym)),
96
96
  label
97
97
  });
98
98
  }
@@ -111,19 +111,19 @@ var derived = (fn, options) => {
111
111
  return getter;
112
112
  };
113
113
  var createEffect = (fn, value) => {
114
- const privateSym = sym = Symbol();
115
- effects.set(sym, { fn, value });
114
+ const privateIdent = ident = {};
115
+ effects.set(ident, { fn, value });
116
116
  try {
117
117
  fn({
118
118
  lastValue: value,
119
119
  destroy: () => {
120
- effects.delete(privateSym);
120
+ effects.delete(privateIdent);
121
121
  }
122
122
  });
123
123
  } catch (error) {
124
124
  console.error("Error in effect:", { error, fn });
125
125
  }
126
- sym = null;
126
+ ident = null;
127
127
  };
128
128
 
129
129
  // src/utilities.ts
@@ -264,8 +264,9 @@ var wrapTemplate = ({ tagName, serverRender, options }) => {
264
264
  return finalScopedRenderString;
265
265
  };
266
266
  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) => {
267
+ const tagRegex = new RegExp(`<\\s*${tagName}((?:\\s+[^>]*)*)\\s*>`, "gm");
268
+ return inputString.replace(tagRegex, ($match, $1) => {
269
+ const attrs = $1.split(/(?<=")\s+/).filter((attr) => attr.trim() !== "").map((attr) => {
269
270
  const [_key, _value] = attr.split("=");
270
271
  const key = _key.trim();
271
272
  const value = _value?.replace(/"/g, "") ?? "";
@@ -275,7 +276,7 @@ var insertTemplates = (tagName, template, inputString) => {
275
276
  for (const [key, value] of attrs) {
276
277
  scopedResult = scopedResult.replace(new RegExp(`{{attr:${key}}}`, "gm"), value);
277
278
  }
278
- return $1 + scopedResult;
279
+ return $match + scopedResult;
279
280
  });
280
281
  };
281
282
  var clientOnlyCallback = (fn) => {
@@ -424,10 +425,6 @@ var evaluateBindings = (element, fragment) => {
424
425
  createEffect(
425
426
  ({ lastValue: oldChildren, destroy }) => {
426
427
  const result = signal2();
427
- console.trace("Binding array:", {
428
- result: result.map((node) => node.cloneNode(true)),
429
- oldChildren
430
- });
431
428
  const newChildren = asNodeList(result, element);
432
429
  const firstChild = newChildren[0];
433
430
  if (!Array.isArray(result) && newChildren.length === 1 && firstChild instanceof DocumentFragment) {
@@ -677,7 +674,7 @@ var css = (strings, ...values) => {
677
674
  }
678
675
  const newCSSText = newCSSTextList.join("");
679
676
  if (isCSSStyleSheet(stylesheet)) {
680
- stylesheet.replace(newCSSText).catch(console.error);
677
+ stylesheet.replaceSync(newCSSText);
681
678
  } else {
682
679
  stylesheet.textContent = newCSSText;
683
680
  }
@@ -706,6 +703,7 @@ var customElement = (render, options) => {
706
703
  if (shadowRootOptions.registry !== void 0 && "scoped" in shadowRootOptions.registry && shadowRootOptions.registry.scoped) {
707
704
  return shadowRootOptions.registry;
708
705
  }
706
+ return void 0;
709
707
  })();
710
708
  return {
711
709
  define(tagName) {
@@ -765,8 +763,8 @@ var customElement = (render, options) => {
765
763
  #formResetCallbackFns = /* @__PURE__ */ new Set();
766
764
  #formStateRestoreCallbackFns = /* @__PURE__ */ new Set();
767
765
  #clientOnlyCallbackFns = /* @__PURE__ */ new Set();
768
- #shadowRoot = attachShadow ? this.attachShadow(shadowRootOptions) : null;
769
766
  #internals = this.attachInternals();
767
+ #shadowRoot = attachShadow ? this.#internals.shadowRoot ?? this.attachShadow(shadowRootOptions) : null;
770
768
  #observer = options?.observedAttributes !== void 0 ? null : new MutationObserver((mutations) => {
771
769
  for (const mutation of mutations) {
772
770
  const attrName = mutation.attributeName;
@@ -819,7 +817,7 @@ Element: <${this.tagName.toLowerCase()}>
819
817
  this[prop] = newValue;
820
818
  _setter(newValue, options2);
821
819
  };
822
- const getter = (options2) => {
820
+ const getter = ((options2) => {
823
821
  const value = _getter(options2);
824
822
  if (value === void 0 && !allowUndefined) {
825
823
  const error = new Error(
@@ -831,7 +829,7 @@ You must set an initial value before calling a property signal's getter.
831
829
  throw error;
832
830
  }
833
831
  return value;
834
- };
832
+ });
835
833
  getter.getter = true;
836
834
  const publicSignal = [getter, setter];
837
835
  publicSignal.init = (value) => {
package/package.json CHANGED
@@ -1,70 +1,55 @@
1
1
  {
2
- "name": "thunderous",
3
- "version": "2.4.1",
4
- "description": "",
5
- "type": "module",
6
- "main": "./dist/index.cjs",
7
- "module": "./dist/index.js",
8
- "types": "./dist/index.d.ts",
9
- "files": [
10
- "/dist",
11
- "/package.json",
12
- "/README.md",
13
- "/LICENSE"
14
- ],
15
- "author": "Jonathan DeWitt",
16
- "repository": {
17
- "type": "git",
18
- "url": "https://github.com/Thunder-Solutions/Thunderous"
19
- },
20
- "keywords": [
21
- "thunderous",
22
- "web components",
23
- "functional",
24
- "signals",
25
- "custom elements"
26
- ],
27
- "bugs": {
28
- "url": "https://github.com/Thunder-Solutions/Thunderous/issues"
29
- },
30
- "homepage": "https://github.com/Thunder-Solutions/Thunderous#readme",
31
- "license": "MIT",
32
- "scripts": {
33
- "demo": "cd demo && npm start",
34
- "demo:ssr": "cd demo && npm run ssr",
35
- "www": "cd www && npm run dev",
36
- "build": "tsup src/index.ts --format cjs,esm --dts --no-clean",
37
- "test": "find src -name '*.test.ts' | xargs c8 node --import tsx --test",
38
- "typecheck": "tsc --noEmit",
39
- "lint": "eslint . && prettier --check .",
40
- "lint:fix": "eslint . --fix && prettier --write .",
41
- "preversion": "npm run typecheck && npm run lint && npm test && npm run build",
42
- "version": "node postversion.js",
43
- "prepublishOnly": "npm run typecheck && npm run lint && npm test && npm run build"
44
- },
45
- "devDependencies": {
46
- "@types/dompurify": "^3.2.0",
47
- "@types/eslint": "^8.56.10",
48
- "@types/node": "^22.10.1",
49
- "@typescript-eslint/eslint-plugin": "^8.17.0",
50
- "@typescript-eslint/parser": "^8.17.0",
51
- "c8": "^10.1.2",
52
- "eslint": "^8.57.0",
53
- "eslint-plugin-import": "^2.31.0",
54
- "eslint-plugin-node": "^11.1.0",
55
- "eslint-plugin-promise": "^7.1.0",
56
- "express": "^4.17.1",
57
- "prettier": "^3.3.3",
58
- "tsup": "^8.3.0",
59
- "tsx": "^4.19.2",
60
- "typescript": "^5.7.2"
61
- },
62
- "peerDependencies": {
63
- "@webcomponents/scoped-custom-element-registry": "^0.0.10"
64
- },
65
- "peerDependenciesMeta": {
66
- "@webcomponents/scoped-custom-element-registry": {
67
- "optional": true
68
- }
69
- }
70
- }
2
+ "name": "thunderous",
3
+ "version": "2.4.3",
4
+ "description": "A lightweight, functional web components library that brings the power of signals to your UI.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "/dist",
11
+ "/package.json",
12
+ "/README.md",
13
+ "/LICENSE"
14
+ ],
15
+ "author": "Jonathan DeWitt <jon.dewitt@thunder.solutions>",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/Thunder-Solutions/Thunderous"
19
+ },
20
+ "keywords": [
21
+ "thunderous",
22
+ "web components",
23
+ "functional",
24
+ "signals",
25
+ "custom elements"
26
+ ],
27
+ "bugs": {
28
+ "url": "https://github.com/Thunder-Solutions/Thunderous/issues"
29
+ },
30
+ "homepage": "https://github.com/Thunder-Solutions/Thunderous#readme",
31
+ "license": "MIT",
32
+ "peerDependencies": {
33
+ "@webcomponents/scoped-custom-element-registry": "^0.0.10"
34
+ },
35
+ "peerDependenciesMeta": {
36
+ "@webcomponents/scoped-custom-element-registry": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "scripts": {
41
+ "demo": "cd demo && npm start",
42
+ "demo:ssr": "cd demo && npm run ssr",
43
+ "build": "tsup src/index.ts --format cjs,esm --dts --no-clean",
44
+ "test": "npm run test:server && npm run test:client",
45
+ "test:server": "find src/__test__/server -name '*.test.ts' | xargs c8 tsx --test",
46
+ "test:client": "PLAYWRIGHT_BROWSERS_PATH=../../.browsers playwright test",
47
+ "typecheck": "tsc --noEmit",
48
+ "lint": "eslint .",
49
+ "lint:fix": "eslint . --fix",
50
+ "format": "prettier --check . --ignore-path ../../.gitignore",
51
+ "format:fix": "prettier --write . --ignore-path ../../.gitignore",
52
+ "preversion": "npm run typecheck && npm run lint && npm test && npm run build",
53
+ "version": "node postversion.js"
54
+ }
55
+ }
@@ -1,48 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'assert';
3
- import { customElement } from '../custom-element';
4
- import { html } from '../render';
5
- import { createRegistry } from '../registry';
6
- import { NOOP } from '../utilities';
7
- await describe('customElement', async () => {
8
- await it('does not throw on the server', () => {
9
- assert.doesNotThrow(() => customElement(() => html `<div></div>`));
10
- });
11
- await it('returns an element result class', () => {
12
- const MyElement = customElement(() => html `<div></div>`);
13
- assert.ok(MyElement);
14
- const keys = Object.keys(MyElement);
15
- assert(keys.every((key) => ['define', 'register', 'eject'].includes(key)));
16
- });
17
- await it('supports scoped registries', () => {
18
- const registry = createRegistry({ scoped: true });
19
- assert.doesNotThrow(() => customElement(() => html `<div></div>`, { shadowRootOptions: { registry } }));
20
- });
21
- await it('returns self for chaining', () => {
22
- const MyElement = customElement(() => html `<div></div>`);
23
- const registry = createRegistry();
24
- assert.strictEqual(MyElement.define('my-element'), MyElement);
25
- assert.strictEqual(MyElement.register(registry), MyElement);
26
- });
27
- await it('registers the element with a registry', () => {
28
- const registry = createRegistry();
29
- const MyElement = customElement(() => html `<div></div>`)
30
- .register(registry)
31
- .define('my-element');
32
- assert.strictEqual(registry.getTagName(MyElement), 'MY-ELEMENT');
33
- });
34
- await it('logs an error when registering after defining in a scoped registry', (testContext) => {
35
- testContext.mock.method(console, 'error', NOOP);
36
- const errorMock = console.error.mock;
37
- const registry = createRegistry({ scoped: true });
38
- const MyElement = customElement(() => html `<div></div>`);
39
- MyElement.define('my-element');
40
- MyElement.register(registry);
41
- assert.strictEqual(errorMock.callCount(), 1);
42
- assert.strictEqual(errorMock.calls[0].arguments[0], 'Must call `register()` before `define()` for scoped registries.');
43
- });
44
- await it('throws an error when ejecting on the server', () => {
45
- const MyElement = customElement(() => html `<div></div>`);
46
- assert.throws(() => MyElement.eject());
47
- });
48
- });
@@ -1,60 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'assert';
3
- import { createRegistry } from '../registry';
4
- import { customElement } from '../custom-element';
5
- import { html } from '../render';
6
- import { NOOP } from '../utilities';
7
- await describe('createRegistry', async () => {
8
- await it('creates a global registry', () => {
9
- const registry = createRegistry();
10
- assert.ok(registry);
11
- assert.strictEqual(registry.scoped, false);
12
- });
13
- await it('creates a scoped registry', () => {
14
- const registry = createRegistry({ scoped: true });
15
- assert.ok(registry);
16
- assert.strictEqual(registry.scoped, true);
17
- });
18
- await it('defines a custom element', () => {
19
- const registry = createRegistry();
20
- const MyElement = customElement(() => html `<div></div>`);
21
- assert.doesNotThrow(() => registry.define('my-element', MyElement));
22
- });
23
- await it('warns about duplicate custom elements', (testContext) => {
24
- testContext.mock.method(console, 'warn', NOOP);
25
- const warnMock = console.warn.mock;
26
- const registry = createRegistry();
27
- const MyElement = customElement(() => html `<div></div>`);
28
- const MyElement2 = customElement(() => html `<div></div>`);
29
- registry.define('my-element', MyElement);
30
- registry.define('my-element', MyElement2);
31
- assert.strictEqual(warnMock.callCount(), 1);
32
- assert.strictEqual(warnMock.calls[0].arguments[0], 'Custom element tag name "MY-ELEMENT" was already defined. Skipping...');
33
- registry.define('my-element-2', MyElement);
34
- assert.strictEqual(warnMock.callCount(), 2);
35
- assert.strictEqual(warnMock.calls[1].arguments[0], 'MY-ELEMENT-2 was already defined. Skipping...');
36
- });
37
- await it('gets the tag name of a custom element', () => {
38
- const registry = createRegistry();
39
- const tagName = 'my-element';
40
- const MyElement = customElement(() => html `<div></div>`);
41
- registry.define(tagName, MyElement);
42
- const result = registry.getTagName(MyElement);
43
- assert.strictEqual(result, tagName.toUpperCase());
44
- });
45
- await it('gets all tag names defined in the registry', () => {
46
- const registry = createRegistry();
47
- const MyElement = customElement(() => html `<div></div>`);
48
- const MyElement2 = customElement(() => html `<div></div>`);
49
- registry.define('my-element', MyElement);
50
- registry.define('my-element-2', MyElement2);
51
- const result = registry.getAllTagNames();
52
- assert.deepStrictEqual(result, ['MY-ELEMENT', 'MY-ELEMENT-2']);
53
- });
54
- await it('throws an error if ejected on the server', () => {
55
- const registry = createRegistry();
56
- const MyElement = customElement(() => html `<div></div>`);
57
- registry.define('my-element', MyElement);
58
- assert.throws(() => registry.eject(), { message: 'Cannot eject a registry on the server.' });
59
- });
60
- });
@@ -1,48 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'assert';
3
- import { html, css } from '../render';
4
- import { NOOP } from '../utilities';
5
- await describe('html', async () => {
6
- await it('renders a simple string', () => {
7
- const result = html `<div></div>`;
8
- assert.strictEqual(result, '<div></div>');
9
- });
10
- await it('renders a string with interpolated values', () => {
11
- const result = html `<div>${'Hello, world!'} ${1} ${true}</div>`;
12
- assert.strictEqual(result, '<div>Hello, world! 1 true</div>');
13
- });
14
- await it('renders a string with signals', () => {
15
- const mockGetter = () => 'Hello, world!';
16
- const result = html `<div>${mockGetter}</div>`;
17
- assert.strictEqual(result, '<div>Hello, world!</div>');
18
- });
19
- });
20
- await describe('css', async () => {
21
- await it('renders a simple string', () => {
22
- // prettier-ignore
23
- const result = css `div { color: red; }`;
24
- assert.strictEqual(result, 'div { color: red; }');
25
- });
26
- await it('renders a string with interpolated values', () => {
27
- // prettier-ignore
28
- const result = css `div { --str: ${'str'}; --num: ${1}; --bool: ${true}; }`;
29
- assert.strictEqual(result, 'div { --str: str; --num: 1; --bool: true; }');
30
- });
31
- await it('logs an error if a non-primitive value is interpolated', (testContext) => {
32
- testContext.mock.method(console, 'error', NOOP);
33
- const errorMock = console.error.mock;
34
- const obj = {};
35
- // prettier-ignore
36
- const result = css `div { --obj: ${obj}; }`;
37
- assert.strictEqual(result, 'div { --obj: ; }');
38
- assert.strictEqual(errorMock.callCount(), 1);
39
- assert.strictEqual(errorMock.calls[0].arguments[0], 'Objects are not valid in CSS values. Received:');
40
- assert.strictEqual(errorMock.calls[0].arguments[1], obj);
41
- });
42
- await it('renders a string with signals', () => {
43
- const mockGetter = () => 'red';
44
- // prettier-ignore
45
- const result = css `div { color: ${mockGetter}; }`;
46
- assert.strictEqual(result, 'div { color: red; }');
47
- });
48
- });