thunderous 2.4.1 → 2.4.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/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
@@ -459,10 +459,6 @@ var evaluateBindings = (element, fragment) => {
459
459
  createEffect(
460
460
  ({ lastValue: oldChildren, destroy }) => {
461
461
  const result = signal2();
462
- console.trace("Binding array:", {
463
- result: result.map((node) => node.cloneNode(true)),
464
- oldChildren
465
- });
466
462
  const newChildren = asNodeList(result, element);
467
463
  const firstChild = newChildren[0];
468
464
  if (!Array.isArray(result) && newChildren.length === 1 && firstChild instanceof DocumentFragment) {
@@ -712,7 +708,7 @@ var css = (strings, ...values) => {
712
708
  }
713
709
  const newCSSText = newCSSTextList.join("");
714
710
  if (isCSSStyleSheet(stylesheet)) {
715
- stylesheet.replace(newCSSText).catch(console.error);
711
+ stylesheet.replaceSync(newCSSText);
716
712
  } else {
717
713
  stylesheet.textContent = newCSSText;
718
714
  }
@@ -741,6 +737,7 @@ var customElement = (render, options) => {
741
737
  if (shadowRootOptions.registry !== void 0 && "scoped" in shadowRootOptions.registry && shadowRootOptions.registry.scoped) {
742
738
  return shadowRootOptions.registry;
743
739
  }
740
+ return void 0;
744
741
  })();
745
742
  return {
746
743
  define(tagName) {
@@ -800,8 +797,8 @@ var customElement = (render, options) => {
800
797
  #formResetCallbackFns = /* @__PURE__ */ new Set();
801
798
  #formStateRestoreCallbackFns = /* @__PURE__ */ new Set();
802
799
  #clientOnlyCallbackFns = /* @__PURE__ */ new Set();
803
- #shadowRoot = attachShadow ? this.attachShadow(shadowRootOptions) : null;
804
800
  #internals = this.attachInternals();
801
+ #shadowRoot = attachShadow ? this.#internals.shadowRoot ?? this.attachShadow(shadowRootOptions) : null;
805
802
  #observer = options?.observedAttributes !== void 0 ? null : new MutationObserver((mutations) => {
806
803
  for (const mutation of mutations) {
807
804
  const attrName = mutation.attributeName;
@@ -854,7 +851,7 @@ Element: <${this.tagName.toLowerCase()}>
854
851
  this[prop] = newValue;
855
852
  _setter(newValue, options2);
856
853
  };
857
- const getter = (options2) => {
854
+ const getter = ((options2) => {
858
855
  const value = _getter(options2);
859
856
  if (value === void 0 && !allowUndefined) {
860
857
  const error = new Error(
@@ -866,7 +863,7 @@ You must set an initial value before calling a property signal's getter.
866
863
  throw error;
867
864
  }
868
865
  return value;
869
- };
866
+ });
870
867
  getter.getter = true;
871
868
  const publicSignal = [getter, setter];
872
869
  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
@@ -424,10 +424,6 @@ var evaluateBindings = (element, fragment) => {
424
424
  createEffect(
425
425
  ({ lastValue: oldChildren, destroy }) => {
426
426
  const result = signal2();
427
- console.trace("Binding array:", {
428
- result: result.map((node) => node.cloneNode(true)),
429
- oldChildren
430
- });
431
427
  const newChildren = asNodeList(result, element);
432
428
  const firstChild = newChildren[0];
433
429
  if (!Array.isArray(result) && newChildren.length === 1 && firstChild instanceof DocumentFragment) {
@@ -677,7 +673,7 @@ var css = (strings, ...values) => {
677
673
  }
678
674
  const newCSSText = newCSSTextList.join("");
679
675
  if (isCSSStyleSheet(stylesheet)) {
680
- stylesheet.replace(newCSSText).catch(console.error);
676
+ stylesheet.replaceSync(newCSSText);
681
677
  } else {
682
678
  stylesheet.textContent = newCSSText;
683
679
  }
@@ -706,6 +702,7 @@ var customElement = (render, options) => {
706
702
  if (shadowRootOptions.registry !== void 0 && "scoped" in shadowRootOptions.registry && shadowRootOptions.registry.scoped) {
707
703
  return shadowRootOptions.registry;
708
704
  }
705
+ return void 0;
709
706
  })();
710
707
  return {
711
708
  define(tagName) {
@@ -765,8 +762,8 @@ var customElement = (render, options) => {
765
762
  #formResetCallbackFns = /* @__PURE__ */ new Set();
766
763
  #formStateRestoreCallbackFns = /* @__PURE__ */ new Set();
767
764
  #clientOnlyCallbackFns = /* @__PURE__ */ new Set();
768
- #shadowRoot = attachShadow ? this.attachShadow(shadowRootOptions) : null;
769
765
  #internals = this.attachInternals();
766
+ #shadowRoot = attachShadow ? this.#internals.shadowRoot ?? this.attachShadow(shadowRootOptions) : null;
770
767
  #observer = options?.observedAttributes !== void 0 ? null : new MutationObserver((mutations) => {
771
768
  for (const mutation of mutations) {
772
769
  const attrName = mutation.attributeName;
@@ -819,7 +816,7 @@ Element: <${this.tagName.toLowerCase()}>
819
816
  this[prop] = newValue;
820
817
  _setter(newValue, options2);
821
818
  };
822
- const getter = (options2) => {
819
+ const getter = ((options2) => {
823
820
  const value = _getter(options2);
824
821
  if (value === void 0 && !allowUndefined) {
825
822
  const error = new Error(
@@ -831,7 +828,7 @@ You must set an initial value before calling a property signal's getter.
831
828
  throw error;
832
829
  }
833
830
  return value;
834
- };
831
+ });
835
832
  getter.getter = true;
836
833
  const publicSignal = [getter, setter];
837
834
  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.2",
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
- });