thunderous 0.6.3 → 1.0.0

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.d.ts CHANGED
@@ -1,99 +1,675 @@
1
- declare global {
2
- interface Element {
3
- __findHost: () => Element;
4
- }
5
- }
6
- declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
7
- type Styles = CSSStyleSheet | HTMLStyleElement;
8
- declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
9
-
10
- type SignalOptions = {
11
- debugMode: boolean;
12
- label?: string;
13
- };
14
- type SignalGetter<T> = (options?: SignalOptions) => T;
15
- type SignalSetter<T> = (newValue: T, options?: SignalOptions) => void;
16
- type Signal<T = unknown> = [SignalGetter<T>, SignalSetter<T>];
17
1
  /**
18
- * Create a signal with an initial value.
19
- * @example
20
- * ```ts
21
- * const [getCount, setCount] = createSignal(0);
22
- * const increment = () => setCount(getCount() + 1);
23
- * const decrement = () => setCount(getCount() - 1);
24
- * ```
2
+ * @license
3
+ * Copyright (c) 2020 The Polymer Project Authors. All rights reserved.
4
+ * This code may only be used under the BSD style license found at
5
+ * http://polymer.github.io/LICENSE.txt
6
+ * The complete set of authors may be found at
7
+ * http://polymer.github.io/AUTHORS.txt
8
+ * The complete set of contributors may be found at
9
+ * http://polymer.github.io/CONTRIBUTORS.txt
10
+ * Code distributed by Google as part of the polymer project is also
11
+ * subject to an additional IP rights grant found at
12
+ * http://polymer.github.io/PATENTS.txt
25
13
  */
26
- declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions) => Signal<T>;
27
- /**
28
- * Create a derived signal that depends on other signals.
29
- * @example
30
- * ```ts
31
- * const [getCount, setCount] = createSignal(0);
32
- * const doubleCount = derived(() => getCount() * 2);
33
- * ```
34
- */
35
- declare const derived: <T>(fn: () => T) => SignalGetter<T>;
36
- /**
37
- * Create an effect that runs when signals change.
38
- * @example
39
- * ```ts
40
- * const [getCount, setCount] = createSignal(0);
41
- * createEffect(() => {
42
- * console.log('Count:', getCount());
43
- * });
44
- * ```
45
- */
46
- declare const createEffect: (fn: () => void) => void;
14
+ if (typeof window !== 'undefined') {
15
+ if (!ShadowRoot.prototype.createElement) {
16
+ const NativeHTMLElement = window.HTMLElement;
17
+ const nativeDefine = window.customElements.define;
18
+ const nativeGet = window.customElements.get;
19
+ const nativeRegistry = window.customElements;
47
20
 
48
- declare global {
49
- interface DocumentFragment {
50
- host: HTMLElement;
21
+ const definitionForElement = new WeakMap();
22
+ const pendingRegistryForElement = new WeakMap();
23
+ const globalDefinitionForConstructor = new WeakMap();
24
+ // TBD: This part of the spec proposal is unclear:
25
+ // > Another option for looking up registries is to store an element's
26
+ // > originating registry with the element. The Chrome DOM team was concerned
27
+ // > about the small additional memory overhead on all elements. Looking up the
28
+ // > root avoids this.
29
+ const scopeForElement = new WeakMap();
30
+
31
+ // Constructable CE registry class, which uses the native CE registry to
32
+ // register stand-in elements that can delegate out to CE classes registered
33
+ // in scoped registries
34
+ window.CustomElementRegistry = class {
35
+ constructor() {
36
+ this._definitionsByTag = new Map();
37
+ this._definitionsByClass = new Map();
38
+ this._whenDefinedPromises = new Map();
39
+ this._awaitingUpgrade = new Map();
40
+ }
41
+
42
+ define(tagName, elementClass) {
43
+ tagName = tagName.toLowerCase();
44
+ if (this._getDefinition(tagName) !== undefined) {
45
+ throw new DOMException(
46
+ `Failed to execute 'define' on 'CustomElementRegistry': the name "${tagName}" has already been used with this registry`
47
+ );
48
+ }
49
+ if (this._definitionsByClass.get(elementClass) !== undefined) {
50
+ throw new DOMException(
51
+ `Failed to execute 'define' on 'CustomElementRegistry': this constructor has already been used with this registry`
52
+ );
53
+ }
54
+ // Since observedAttributes can't change, we approximate it by patching
55
+ // set/remove/toggleAttribute on the user's class
56
+ const attributeChangedCallback =
57
+ elementClass.prototype.attributeChangedCallback;
58
+ const observedAttributes = new Set(elementClass.observedAttributes || []);
59
+ patchAttributes(
60
+ elementClass,
61
+ observedAttributes,
62
+ attributeChangedCallback
63
+ );
64
+ // Register the definition
65
+ const definition = {
66
+ elementClass,
67
+ connectedCallback: elementClass.prototype.connectedCallback,
68
+ disconnectedCallback: elementClass.prototype.disconnectedCallback,
69
+ adoptedCallback: elementClass.prototype.adoptedCallback,
70
+ attributeChangedCallback,
71
+ 'formAssociated': elementClass['formAssociated'],
72
+ 'formAssociatedCallback':
73
+ elementClass.prototype['formAssociatedCallback'],
74
+ 'formDisabledCallback': elementClass.prototype['formDisabledCallback'],
75
+ 'formResetCallback': elementClass.prototype['formResetCallback'],
76
+ 'formStateRestoreCallback':
77
+ elementClass.prototype['formStateRestoreCallback'],
78
+ observedAttributes,
79
+ };
80
+ this._definitionsByTag.set(tagName, definition);
81
+ this._definitionsByClass.set(elementClass, definition);
82
+ // Register a stand-in class which will handle the registry lookup & delegation
83
+ let standInClass = nativeGet.call(nativeRegistry, tagName);
84
+ if (!standInClass) {
85
+ standInClass = createStandInElement(tagName);
86
+ nativeDefine.call(nativeRegistry, tagName, standInClass);
87
+ }
88
+ if (this === window.customElements) {
89
+ globalDefinitionForConstructor.set(elementClass, definition);
90
+ definition.standInClass = standInClass;
91
+ }
92
+ // Upgrade any elements created in this scope before define was called
93
+ const awaiting = this._awaitingUpgrade.get(tagName);
94
+ if (awaiting) {
95
+ this._awaitingUpgrade.delete(tagName);
96
+ for (const element of awaiting) {
97
+ pendingRegistryForElement.delete(element);
98
+ customize(element, definition, true);
99
+ }
100
+ }
101
+ // Flush whenDefined callbacks
102
+ const info = this._whenDefinedPromises.get(tagName);
103
+ if (info !== undefined) {
104
+ info.resolve(elementClass);
105
+ this._whenDefinedPromises.delete(tagName);
106
+ }
107
+ return elementClass;
108
+ }
109
+
110
+ upgrade() {
111
+ creationContext.push(this);
112
+ nativeRegistry.upgrade.apply(nativeRegistry, arguments);
113
+ creationContext.pop();
114
+ }
115
+
116
+ get(tagName) {
117
+ const definition = this._definitionsByTag.get(tagName);
118
+ return definition?.elementClass;
119
+ }
120
+
121
+ _getDefinition(tagName) {
122
+ return this._definitionsByTag.get(tagName);
123
+ }
124
+
125
+ whenDefined(tagName) {
126
+ const definition = this._getDefinition(tagName);
127
+ if (definition !== undefined) {
128
+ return Promise.resolve(definition.elementClass);
129
+ }
130
+ let info = this._whenDefinedPromises.get(tagName);
131
+ if (info === undefined) {
132
+ info = {};
133
+ info.promise = new Promise((r) => (info.resolve = r));
134
+ this._whenDefinedPromises.set(tagName, info);
135
+ }
136
+ return info.promise;
137
+ }
138
+
139
+ _upgradeWhenDefined(element, tagName, shouldUpgrade) {
140
+ let awaiting = this._awaitingUpgrade.get(tagName);
141
+ if (!awaiting) {
142
+ this._awaitingUpgrade.set(tagName, (awaiting = new Set()));
143
+ }
144
+ if (shouldUpgrade) {
145
+ awaiting.add(element);
146
+ } else {
147
+ awaiting.delete(element);
148
+ }
149
+ }
150
+ };
151
+
152
+ // User extends this HTMLElement, which returns the CE being upgraded
153
+ let upgradingInstance;
154
+ window.HTMLElement = function HTMLElement() {
155
+ // Upgrading case: the StandInElement constructor was run by the browser's
156
+ // native custom elements and we're in the process of running the
157
+ // "constructor-call trick" on the natively constructed instance, so just
158
+ // return that here
159
+ let instance = upgradingInstance;
160
+ if (instance) {
161
+ upgradingInstance = undefined;
162
+ return instance;
163
+ }
164
+ // Construction case: we need to construct the StandInElement and return
165
+ // it; note the current spec proposal only allows new'ing the constructor
166
+ // of elements registered with the global registry
167
+ const definition = globalDefinitionForConstructor.get(this.constructor);
168
+ if (!definition) {
169
+ throw new TypeError(
170
+ 'Illegal constructor (custom element class must be registered with global customElements registry to be newable)'
171
+ );
172
+ }
173
+ instance = Reflect.construct(
174
+ NativeHTMLElement,
175
+ [],
176
+ definition.standInClass
177
+ );
178
+ Object.setPrototypeOf(instance, this.constructor.prototype);
179
+ definitionForElement.set(instance, definition);
180
+ return instance;
181
+ };
182
+ window.HTMLElement.prototype = NativeHTMLElement.prototype;
183
+
184
+ // Helpers to return the scope for a node where its registry would be located
185
+ const isValidScope = (node) =>
186
+ node === document || node instanceof ShadowRoot;
187
+ const registryForNode = (node) => {
188
+ // TODO: the algorithm for finding the scope is a bit up in the air; assigning
189
+ // a one-time scope at creation time would require walking every tree ever
190
+ // created, which is avoided for now
191
+ let scope = node.getRootNode();
192
+ // If we're not attached to the document (i.e. in a disconnected tree or
193
+ // fragment), we need to get the scope from the creation context; that should
194
+ // be a Document or ShadowRoot, unless it was created via innerHTML
195
+ if (!isValidScope(scope)) {
196
+ const context = creationContext[creationContext.length - 1];
197
+ // When upgrading via registry.upgrade(), the registry itself is put on the
198
+ // creationContext stack
199
+ if (context instanceof CustomElementRegistry) {
200
+ return context;
201
+ }
202
+ // Otherwise, get the root node of the element this was created from
203
+ scope = context.getRootNode();
204
+ // The creation context wasn't a Document or ShadowRoot or in one; this
205
+ // means we're being innerHTML'ed into a disconnected element; for now, we
206
+ // hope that root node was created imperatively, where we stash _its_
207
+ // scopeForElement. Beyond that, we'd need more costly tracking.
208
+ if (!isValidScope(scope)) {
209
+ scope = scopeForElement.get(scope)?.getRootNode() || document;
210
+ }
211
+ }
212
+ return scope.customElements;
213
+ };
214
+
215
+ // Helper to create stand-in element for each tagName registered that delegates
216
+ // out to the registry for the given element
217
+ const createStandInElement = (tagName) => {
218
+ return class ScopedCustomElementBase {
219
+ static get ['formAssociated']() {
220
+ return true;
221
+ }
222
+ constructor() {
223
+ // Create a raw HTMLElement first
224
+ const instance = Reflect.construct(
225
+ NativeHTMLElement,
226
+ [],
227
+ this.constructor
228
+ );
229
+ // We need to install the minimum HTMLElement prototype so that
230
+ // scopeForNode can use DOM API to determine our construction scope;
231
+ // upgrade will eventually install the full CE prototype
232
+ Object.setPrototypeOf(instance, HTMLElement.prototype);
233
+ // Get the node's scope, and its registry (falls back to global registry)
234
+ const registry = registryForNode(instance) || window.customElements;
235
+ const definition = registry._getDefinition(tagName);
236
+ if (definition) {
237
+ customize(instance, definition);
238
+ } else {
239
+ pendingRegistryForElement.set(instance, registry);
240
+ }
241
+ return instance;
242
+ }
243
+
244
+ connectedCallback() {
245
+ const definition = definitionForElement.get(this);
246
+ if (definition) {
247
+ // Delegate out to user callback
248
+ definition.connectedCallback &&
249
+ definition.connectedCallback.apply(this, arguments);
250
+ } else {
251
+ // Register for upgrade when defined (only when connected, so we don't leak)
252
+ pendingRegistryForElement
253
+ .get(this)
254
+ ._upgradeWhenDefined(this, tagName, true);
255
+ }
256
+ }
257
+
258
+ disconnectedCallback() {
259
+ const definition = definitionForElement.get(this);
260
+ if (definition) {
261
+ // Delegate out to user callback
262
+ definition.disconnectedCallback &&
263
+ definition.disconnectedCallback.apply(this, arguments);
264
+ } else {
265
+ // Un-register for upgrade when defined (so we don't leak)
266
+ pendingRegistryForElement
267
+ .get(this)
268
+ ._upgradeWhenDefined(this, tagName, false);
269
+ }
270
+ }
271
+
272
+ adoptedCallback() {
273
+ const definition = definitionForElement.get(this);
274
+ definition?.adoptedCallback?.apply(this, arguments);
275
+ }
276
+
277
+ // Form-associated custom elements lifecycle methods
278
+ ['formAssociatedCallback']() {
279
+ const definition = definitionForElement.get(this);
280
+ if (definition && definition['formAssociated']) {
281
+ definition?.['formAssociatedCallback']?.apply(this, arguments);
282
+ }
283
+ }
284
+
285
+ ['formDisabledCallback']() {
286
+ const definition = definitionForElement.get(this);
287
+ if (definition?.['formAssociated']) {
288
+ definition?.['formDisabledCallback']?.apply(this, arguments);
289
+ }
290
+ }
291
+
292
+ ['formResetCallback']() {
293
+ const definition = definitionForElement.get(this);
294
+ if (definition?.['formAssociated']) {
295
+ definition?.['formResetCallback']?.apply(this, arguments);
296
+ }
297
+ }
298
+
299
+ ['formStateRestoreCallback']() {
300
+ const definition = definitionForElement.get(this);
301
+ if (definition?.['formAssociated']) {
302
+ definition?.['formStateRestoreCallback']?.apply(this, arguments);
303
+ }
304
+ }
305
+
306
+ // no attributeChangedCallback or observedAttributes since these
307
+ // are simulated via setAttribute/removeAttribute patches
308
+ };
309
+ };
310
+
311
+ // Helper to patch CE class setAttribute/getAttribute/toggleAttribute to
312
+ // implement attributeChangedCallback
313
+ const patchAttributes = (
314
+ elementClass,
315
+ observedAttributes,
316
+ attributeChangedCallback
317
+ ) => {
318
+ if (
319
+ observedAttributes.size === 0 ||
320
+ attributeChangedCallback === undefined
321
+ ) {
322
+ return;
323
+ }
324
+ const setAttribute = elementClass.prototype.setAttribute;
325
+ if (setAttribute) {
326
+ elementClass.prototype.setAttribute = function (n, value) {
327
+ const name = n.toLowerCase();
328
+ if (observedAttributes.has(name)) {
329
+ const old = this.getAttribute(name);
330
+ setAttribute.call(this, name, value);
331
+ attributeChangedCallback.call(this, name, old, value);
332
+ } else {
333
+ setAttribute.call(this, name, value);
334
+ }
335
+ };
336
+ }
337
+ const removeAttribute = elementClass.prototype.removeAttribute;
338
+ if (removeAttribute) {
339
+ elementClass.prototype.removeAttribute = function (n) {
340
+ const name = n.toLowerCase();
341
+ if (observedAttributes.has(name)) {
342
+ const old = this.getAttribute(name);
343
+ removeAttribute.call(this, name);
344
+ attributeChangedCallback.call(this, name, old, null);
345
+ } else {
346
+ removeAttribute.call(this, name);
347
+ }
348
+ };
349
+ }
350
+ const toggleAttribute = elementClass.prototype.toggleAttribute;
351
+ if (toggleAttribute) {
352
+ elementClass.prototype.toggleAttribute = function (n, force) {
353
+ const name = n.toLowerCase();
354
+ if (observedAttributes.has(name)) {
355
+ const old = this.getAttribute(name);
356
+ toggleAttribute.call(this, name, force);
357
+ const newValue = this.getAttribute(name);
358
+ attributeChangedCallback.call(this, name, old, newValue);
359
+ } else {
360
+ toggleAttribute.call(this, name, force);
361
+ }
362
+ };
363
+ }
364
+ };
365
+
366
+ // Helper to patch CE class hierarchy changing those CE classes created before applying the polyfill
367
+ // to make them work with the new patched CustomElementsRegistry
368
+ const patchHTMLElement = (elementClass) => {
369
+ const parentClass = Object.getPrototypeOf(elementClass);
370
+
371
+ if (parentClass !== window.HTMLElement) {
372
+ if (parentClass === NativeHTMLElement) {
373
+ return Object.setPrototypeOf(elementClass, window.HTMLElement);
374
+ }
375
+
376
+ return patchHTMLElement(parentClass);
377
+ }
378
+ };
379
+
380
+ // Helper to upgrade an instance with a CE definition using "constructor call trick"
381
+ const customize = (instance, definition, isUpgrade = false) => {
382
+ Object.setPrototypeOf(instance, definition.elementClass.prototype);
383
+ definitionForElement.set(instance, definition);
384
+ upgradingInstance = instance;
385
+ try {
386
+ new definition.elementClass();
387
+ } catch (_) {
388
+ patchHTMLElement(definition.elementClass);
389
+ new definition.elementClass();
390
+ }
391
+ if (definition.attributeChangedCallback) {
392
+ // Approximate observedAttributes from the user class, since the stand-in element had none
393
+ definition.observedAttributes.forEach((attr) => {
394
+ if (instance.hasAttribute(attr)) {
395
+ definition.attributeChangedCallback.call(
396
+ instance,
397
+ attr,
398
+ null,
399
+ instance.getAttribute(attr)
400
+ );
401
+ }
402
+ });
403
+ }
404
+ if (isUpgrade && definition.connectedCallback && instance.isConnected) {
405
+ definition.connectedCallback.call(instance);
406
+ }
407
+ };
408
+
409
+ // Patch attachShadow to set customElements on shadowRoot when provided
410
+ const nativeAttachShadow = Element.prototype.attachShadow;
411
+ Element.prototype.attachShadow = function (init) {
412
+ const shadowRoot = nativeAttachShadow.apply(this, arguments);
413
+ if (init.customElements) {
414
+ shadowRoot.customElements = init.customElements;
415
+ }
416
+ return shadowRoot;
417
+ };
418
+
419
+ // Install scoped creation API on Element & ShadowRoot
420
+ let creationContext = [document];
421
+ const installScopedCreationMethod = (ctor, method, from = undefined) => {
422
+ const native = (from ? Object.getPrototypeOf(from) : ctor.prototype)[
423
+ method
424
+ ];
425
+ ctor.prototype[method] = function () {
426
+ creationContext.push(this);
427
+ const ret = native.apply(from || this, arguments);
428
+ // For disconnected elements, note their creation scope so that e.g.
429
+ // innerHTML into them will use the correct scope; note that
430
+ // insertAdjacentHTML doesn't return an element, but that's fine since
431
+ // it will have a parent that should have a scope
432
+ if (ret !== undefined) {
433
+ scopeForElement.set(ret, this);
434
+ }
435
+ creationContext.pop();
436
+ return ret;
437
+ };
438
+ };
439
+ installScopedCreationMethod(ShadowRoot, 'createElement', document);
440
+ installScopedCreationMethod(ShadowRoot, 'importNode', document);
441
+ installScopedCreationMethod(Element, 'insertAdjacentHTML');
442
+ installScopedCreationMethod(Node, 'appendChild');
443
+ installScopedCreationMethod(Element, 'append');
444
+
445
+
446
+ // Install scoped innerHTML on Element & ShadowRoot
447
+ const installScopedCreationSetter = (ctor, name) => {
448
+ const descriptor = Object.getOwnPropertyDescriptor(ctor.prototype, name);
449
+ Object.defineProperty(ctor.prototype, name, {
450
+ ...descriptor,
451
+ set(value) {
452
+ creationContext.push(this);
453
+ descriptor.set.call(this, value);
454
+ creationContext.pop();
455
+ },
456
+ });
457
+ };
458
+ installScopedCreationSetter(Element, 'innerHTML');
459
+ installScopedCreationSetter(ShadowRoot, 'innerHTML');
460
+
461
+ // Install global registry
462
+ Object.defineProperty(window, 'customElements', {
463
+ value: new CustomElementRegistry(),
464
+ configurable: true,
465
+ writable: true,
466
+ });
467
+
468
+ if (
469
+ !!window['ElementInternals'] &&
470
+ !!window['ElementInternals'].prototype['setFormValue']
471
+ ) {
472
+ const internalsToHostMap = new WeakMap();
473
+ const attachInternals = HTMLElement.prototype['attachInternals'];
474
+ const methods = [
475
+ 'setFormValue',
476
+ 'setValidity',
477
+ 'checkValidity',
478
+ 'reportValidity',
479
+ ];
480
+
481
+ HTMLElement.prototype['attachInternals'] = function (...args) {
482
+ const internals = attachInternals.call(this, ...args);
483
+ internalsToHostMap.set(internals, this);
484
+ return internals;
485
+ };
486
+
487
+ methods.forEach((method) => {
488
+ const proto = window['ElementInternals'].prototype;
489
+ const originalMethod = proto[method];
490
+
491
+ proto[method] = function (...args) {
492
+ const host = internalsToHostMap.get(this);
493
+ const definition = definitionForElement.get(host);
494
+ if (definition['formAssociated'] === true) {
495
+ return originalMethod?.call(this, ...args);
496
+ } else {
497
+ throw new DOMException(
498
+ `Failed to execute ${originalMethod} on 'ElementInternals': The target element is not a form-associated custom element.`
499
+ );
500
+ }
501
+ };
502
+ });
503
+
504
+ // Emulate the native RadioNodeList object
505
+ class RadioNodeList extends Array {
506
+ constructor(elements) {
507
+ super(...elements);
508
+ this._elements = elements;
509
+ }
510
+
511
+ get ['value']() {
512
+ return (
513
+ this._elements.find((element) => element['checked'] === true)
514
+ ?.value || ''
515
+ );
516
+ }
517
+ }
518
+
519
+ // Emulate the native HTMLFormControlsCollection object
520
+ class HTMLFormControlsCollection {
521
+ constructor(elements) {
522
+ const entries = new Map();
523
+ elements.forEach((element, index) => {
524
+ const name = element.getAttribute('name');
525
+ const nameReference = entries.get(name) || [];
526
+ this[+index] = element;
527
+ nameReference.push(element);
528
+ entries.set(name, nameReference);
529
+ });
530
+ this['length'] = elements.length;
531
+ entries.forEach((value, key) => {
532
+ if (!value) return;
533
+ if (value.length === 1) {
534
+ this[key] = value[0];
535
+ } else {
536
+ this[key] = new RadioNodeList(value);
537
+ }
538
+ });
539
+ }
540
+
541
+ ['namedItem'](key) {
542
+ return this[key];
543
+ }
544
+ }
545
+
546
+ // Override the built-in HTMLFormElements.prototype.elements getter
547
+ const formElementsDescriptor = Object.getOwnPropertyDescriptor(
548
+ HTMLFormElement.prototype,
549
+ 'elements'
550
+ );
551
+
552
+ Object.defineProperty(HTMLFormElement.prototype, 'elements', {
553
+ get: function () {
554
+ const nativeElements = formElementsDescriptor.get.call(this, []);
555
+
556
+ const include = [];
557
+
558
+ for (const element of nativeElements) {
559
+ const definition = definitionForElement.get(element);
560
+
561
+ // Only purposefully formAssociated elements or built-ins will feature in elements
562
+ if (!definition || definition['formAssociated'] === true) {
563
+ include.push(element);
564
+ }
565
+ }
566
+
567
+ return new HTMLFormControlsCollection(include);
568
+ },
569
+ });
570
+ }
571
+ }
572
+ class TrackableCustomElementRegistry extends window.CustomElementRegistry {
573
+ __tagNames = new Set();
574
+ define(tagName, constructor, options) {
575
+ super.define(tagName, constructor, options);
576
+ this.__tagNames.add(tagName);
51
577
  }
578
+ }
579
+ window.CustomElementRegistry = TrackableCustomElementRegistry;
52
580
  }
581
+
582
+ declare global {
583
+ interface DocumentFragment {
584
+ host: HTMLElement;
585
+ }
586
+ interface Element {
587
+ __findHost: () => Element;
588
+ }
589
+ interface CustomElementRegistry {
590
+ __tagNames: Set<string>;
591
+ }
592
+ }
593
+
594
+ type TagName = `${string}-${string}`;
595
+
53
596
  type ElementResult = {
54
- define: (tagname: `${string}-${string}`) => ElementResult;
55
- register: (registry: RegistryResult) => ElementResult;
56
- eject: () => CustomElementConstructor;
597
+ define: (tagName: TagName, options?: ElementDefinitionOptions) => ElementResult;
598
+ register: (registry: RegistryResult | CustomElementRegistry) => ElementResult;
599
+ eject: () => CustomElementConstructor;
57
600
  };
601
+
58
602
  type AttributeChangedCallback = (name: string, oldValue: string | null, newValue: string | null) => void;
603
+
59
604
  type CustomElementProps = Record<PropertyKey, unknown>;
605
+
60
606
  type RenderArgs<Props extends CustomElementProps> = {
61
- elementRef: HTMLElement;
62
- root: ShadowRoot | HTMLElement;
63
- internals: ElementInternals;
64
- attributeChangedCallback: (fn: AttributeChangedCallback) => void;
65
- connectedCallback: (fn: () => void) => void;
66
- disconnectedCallback: (fn: () => void) => void;
67
- adoptedCallback: (fn: () => void) => void;
68
- formDisabledCallback: (fn: () => void) => void;
69
- formResetCallback: (fn: () => void) => void;
70
- formStateRestoreCallback: (fn: () => void) => void;
71
- formAssociatedCallback: (fn: () => void) => void;
72
- customCallback: (fn: () => void) => `{{callback:${string}}}`;
73
- attrSignals: Record<string, Signal<string | null>>;
74
- propSignals: {
75
- [K in keyof Props]: Signal<Props[K]>;
76
- };
77
- refs: Record<string, HTMLElement | null>;
78
- adoptStyleSheet: (stylesheet: Styles) => void;
607
+ elementRef: HTMLElement;
608
+ root: ShadowRoot | HTMLElement;
609
+ internals: ElementInternals;
610
+ attributeChangedCallback: (fn: AttributeChangedCallback) => void;
611
+ connectedCallback: (fn: () => void) => void;
612
+ disconnectedCallback: (fn: () => void) => void;
613
+ adoptedCallback: (fn: () => void) => void;
614
+ formDisabledCallback: (fn: () => void) => void;
615
+ formResetCallback: (fn: () => void) => void;
616
+ formStateRestoreCallback: (fn: () => void) => void;
617
+ formAssociatedCallback: (fn: () => void) => void;
618
+ clientOnlyCallback: (fn: () => void) => void;
619
+ customCallback: (fn: () => void) => `{{callback:${string}}}`;
620
+ attrSignals: Record<string, Signal<string | null>>;
621
+ propSignals: {
622
+ [K in keyof Props]: Signal<Props[K]>;
623
+ };
624
+ refs: Record<string, HTMLElement | null>;
625
+ adoptStyleSheet: (stylesheet: Styles) => void;
79
626
  };
627
+
80
628
  type Coerce<T = unknown> = (value: string) => T;
629
+
81
630
  type RenderOptions = {
82
- formAssociated: boolean;
83
- observedAttributes: string[];
84
- attributesAsProperties: [string, Coerce][];
85
- attachShadow: boolean;
86
- shadowRootOptions: Partial<ShadowRootInit> & {
87
- customElements?: CustomElementRegistry;
88
- registry?: CustomElementRegistry | RegistryResult;
89
- };
631
+ formAssociated: boolean;
632
+ observedAttributes: string[];
633
+ attributesAsProperties: [string, Coerce][];
634
+ attachShadow: boolean;
635
+ shadowRootOptions: Partial<ShadowRootInit> & {
636
+ customElements?: CustomElementRegistry; // necessary with the polyfill
637
+ registry?: CustomElementRegistry | RegistryResult; // current state of the proposal
638
+ clonable?: boolean; // missing from typescript but present in the spec
639
+ };
90
640
  };
641
+
91
642
  type RenderFunction<Props extends CustomElementProps> = (args: RenderArgs<Props>) => DocumentFragment;
92
- declare global {
93
- interface CustomElementRegistry {
94
- __tagNames: Set<string>;
95
- }
96
- }
643
+
644
+ type ServerRenderFunction = (args: RenderArgs<CustomElementProps>) => string;
645
+
646
+ type ServerRenderOptions = { serverRender: ServerRenderFunction } & RenderOptions;
647
+
648
+ type RegistryResult = {
649
+ __serverCss: Map<string, string[]>;
650
+ __serverRenderOpts: Map<string, ServerRenderOptions>;
651
+ define: (
652
+ tagName: TagName,
653
+ CustomElement: CustomElementConstructor | ElementResult,
654
+ options?: ElementDefinitionOptions,
655
+ ) => RegistryResult;
656
+ getTagName: (CustomElement: CustomElementConstructor | ElementResult) => string | undefined;
657
+ getAllTagNames: () => string[];
658
+ eject: () => CustomElementRegistry;
659
+ scoped: boolean;
660
+ };
661
+
662
+ type RegistryArgs = {
663
+ scoped: boolean;
664
+ };
665
+
666
+ type Styles = CSSStyleSheet | HTMLStyleElement;
667
+
668
+ type SignalOptions = { debugMode: boolean; label?: string };
669
+ type SignalGetter<T> = (options?: SignalOptions) => T;
670
+ type SignalSetter<T> = (newValue: T, options?: SignalOptions) => void;
671
+ type Signal<T = unknown> = [SignalGetter<T>, SignalSetter<T>];
672
+
97
673
  /**
98
674
  * Create a custom element that can be defined for use in the DOM.
99
675
  * @example
@@ -105,15 +681,7 @@ declare global {
105
681
  * ```
106
682
  */
107
683
  declare const customElement: <Props extends CustomElementProps>(render: RenderFunction<Props>, options?: Partial<RenderOptions>) => ElementResult;
108
- type RegistryResult = {
109
- register: (tagName: string, CustomElement: CustomElementConstructor | ElementResult) => void;
110
- getTagName: (CustomElement: CustomElementConstructor | ElementResult) => string | undefined;
111
- eject: () => CustomElementRegistry;
112
- scoped: boolean;
113
- };
114
- type RegistryArgs = {
115
- scoped: boolean;
116
- };
684
+
117
685
  /**
118
686
  * Create a registry for custom elements.
119
687
  *
@@ -131,4 +699,43 @@ type RegistryArgs = {
131
699
  */
132
700
  declare const createRegistry: (args?: RegistryArgs) => RegistryResult;
133
701
 
134
- export { type RenderArgs, type RenderFunction, type RenderArgs as RenderProps, type Signal, type SignalGetter, type SignalSetter, createEffect, createRegistry, createSignal, css, customElement, derived, html };
702
+ type ServerDefineFn = (tagName: string, htmlString: string) => void;
703
+ declare const onServerDefine: (fn: ServerDefineFn) => void;
704
+ declare const insertTemplates: (tagName: string, template: string, inputString: string) => string;
705
+ declare const clientOnlyCallback: (fn: (() => void) | (() => Promise<void>)) => void | Promise<void>;
706
+
707
+ /**
708
+ * Create a signal with an initial value.
709
+ * @example
710
+ * ```ts
711
+ * const [getCount, setCount] = createSignal(0);
712
+ * const increment = () => setCount(getCount() + 1);
713
+ * const decrement = () => setCount(getCount() - 1);
714
+ * ```
715
+ */
716
+ declare const createSignal: <T = undefined>(initVal?: T, options?: SignalOptions) => Signal<T>;
717
+ /**
718
+ * Create a derived signal that depends on other signals.
719
+ * @example
720
+ * ```ts
721
+ * const [getCount, setCount] = createSignal(0);
722
+ * const doubleCount = derived(() => getCount() * 2);
723
+ * ```
724
+ */
725
+ declare const derived: <T>(fn: () => T) => SignalGetter<T>;
726
+ /**
727
+ * Create an effect that runs when signals change.
728
+ * @example
729
+ * ```ts
730
+ * const [getCount, setCount] = createSignal(0);
731
+ * createEffect(() => {
732
+ * console.log('Count:', getCount());
733
+ * });
734
+ * ```
735
+ */
736
+ declare const createEffect: (fn: () => void) => void;
737
+
738
+ declare const html: (strings: TemplateStringsArray, ...values: unknown[]) => DocumentFragment;
739
+ declare const css: (strings: TemplateStringsArray, ...values: unknown[]) => Styles;
740
+
741
+ export { type RenderArgs, type RenderFunction, type RenderArgs as RenderProps, type Signal, type SignalGetter, type SignalSetter, clientOnlyCallback, createEffect, createRegistry, createSignal, css, customElement, derived, html, insertTemplates, onServerDefine };