ripple 0.2.199 → 0.2.201
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/package.json +5 -4
- package/src/compiler/index.d.ts +1 -5
- package/src/compiler/phases/1-parse/index.js +145 -11
- package/src/compiler/phases/2-analyze/index.js +24 -8
- package/src/compiler/phases/2-analyze/prune.js +5 -3
- package/src/compiler/phases/3-transform/client/index.js +312 -165
- package/src/compiler/phases/3-transform/segments.js +220 -70
- package/src/compiler/phases/3-transform/server/index.js +227 -77
- package/src/compiler/source-map-utils.js +74 -10
- package/src/compiler/types/index.d.ts +63 -21
- package/src/compiler/types/parse.d.ts +3 -1
- package/src/compiler/utils.js +34 -0
- package/src/helpers.d.ts +5 -0
- package/src/runtime/index-server.js +27 -47
- package/src/runtime/internal/client/composite.js +5 -0
- package/src/runtime/internal/client/events.js +1 -9
- package/src/runtime/internal/client/for.js +6 -4
- package/src/runtime/internal/client/hydration.js +2 -2
- package/src/runtime/internal/client/index.js +1 -1
- package/src/runtime/internal/client/operations.js +4 -4
- package/src/runtime/internal/client/render.js +0 -2
- package/src/runtime/internal/client/template.js +9 -1
- package/src/runtime/internal/client/types.d.ts +18 -0
- package/src/runtime/internal/client/utils.js +1 -1
- package/src/runtime/internal/server/index.js +106 -3
- package/src/utils/builders.js +25 -5
- package/tests/client/basic/basic.attributes.test.ripple +1 -1
- package/tests/client/basic/basic.components.test.ripple +47 -0
- package/tests/client/basic/basic.rendering.test.ripple +1 -1
- package/tests/client/composite/composite.props.test.ripple +49 -4
- package/tests/client/dynamic-elements.test.ripple +44 -0
- package/tests/client/switch.test.ripple +40 -0
- package/tests/client/tsconfig.json +11 -0
- package/tests/client.d.ts +5 -22
- package/tests/common.d.ts +24 -0
- package/tests/hydration/compiled/server/basic.js +109 -24
- package/tests/hydration/compiled/server/events.js +161 -72
- package/tests/hydration/compiled/server/for.js +202 -102
- package/tests/hydration/compiled/server/if.js +130 -50
- package/tests/hydration/compiled/server/reactivity.js +51 -12
- package/tests/server/__snapshots__/compiler.test.ripple.snap +11 -4
- package/tests/server/basic.attributes.test.ripple +459 -0
- package/tests/server/basic.components.test.ripple +237 -0
- package/tests/server/basic.test.ripple +25 -0
- package/tests/server/compiler.test.ripple +2 -3
- package/tests/server/composite.props.test.ripple +161 -0
- package/tests/server/dynamic-elements.test.ripple +438 -0
- package/tests/server/head.test.ripple +102 -0
- package/tests/server/switch.test.ripple +40 -0
- package/tests/server/tsconfig.json +11 -0
- package/tests/server.d.ts +7 -0
- package/tests/setup-client.js +6 -2
- package/tests/setup-server.js +16 -0
- package/types/index.d.ts +2 -2
- package/types/server.d.ts +4 -3
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Readable } from 'stream';
|
|
7
|
-
import { DERIVED, UNINITIALIZED } from '../client/constants.js';
|
|
8
|
-
import { is_tracked_object } from '../client/utils.js';
|
|
7
|
+
import { DERIVED, UNINITIALIZED, TRACKED } from '../client/constants.js';
|
|
8
|
+
import { is_tracked_object, get_descriptor, define_property, is_array } from '../client/utils.js';
|
|
9
9
|
import { escape } from '../../../utils/escaping.js';
|
|
10
10
|
import { is_boolean_attribute } from '../../../compiler/utils.js';
|
|
11
11
|
import { clsx } from 'clsx';
|
|
@@ -180,6 +180,8 @@ class Output {
|
|
|
180
180
|
#parent = null;
|
|
181
181
|
/** @type {import('stream').Readable | null} */
|
|
182
182
|
#stream = null;
|
|
183
|
+
/** @type {null | 'head'} */
|
|
184
|
+
target = null;
|
|
183
185
|
|
|
184
186
|
/**
|
|
185
187
|
* @param {Output | null} parent
|
|
@@ -195,6 +197,11 @@ class Output {
|
|
|
195
197
|
* @returns {void}
|
|
196
198
|
*/
|
|
197
199
|
push(str) {
|
|
200
|
+
if (this.target === 'head') {
|
|
201
|
+
this.head += str;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
198
205
|
if (this.#stream) {
|
|
199
206
|
this.#stream.push(str);
|
|
200
207
|
} else {
|
|
@@ -476,7 +483,7 @@ export function spread_attrs(attrs, css_hash) {
|
|
|
476
483
|
}
|
|
477
484
|
|
|
478
485
|
if (name === 'class' && css_hash) {
|
|
479
|
-
value = value == null ? css_hash : [value, css_hash];
|
|
486
|
+
value = value == null || value === css_hash ? css_hash : [value, css_hash];
|
|
480
487
|
}
|
|
481
488
|
|
|
482
489
|
attr_str += attr(name, value, is_boolean_attribute(name));
|
|
@@ -484,3 +491,99 @@ export function spread_attrs(attrs, css_hash) {
|
|
|
484
491
|
|
|
485
492
|
return attr_str;
|
|
486
493
|
}
|
|
494
|
+
|
|
495
|
+
var empty_get_set = { get: undefined, set: undefined };
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* @param {any} v
|
|
499
|
+
* @param {(value: any) => any} [get]
|
|
500
|
+
* @param {(next: any, prev: any) => any} [set]
|
|
501
|
+
* @returns {Tracked}
|
|
502
|
+
*/
|
|
503
|
+
function tracked(v, get, set) {
|
|
504
|
+
return {
|
|
505
|
+
a: get || set ? { get, set } : empty_get_set,
|
|
506
|
+
c: 0,
|
|
507
|
+
f: TRACKED,
|
|
508
|
+
v,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* @param {any} v
|
|
514
|
+
* @param {(value: any) => any} [get]
|
|
515
|
+
* @param {(next: any, prev: any) => any} [set]
|
|
516
|
+
* @returns {Tracked | Derived}
|
|
517
|
+
*/
|
|
518
|
+
export function track(v, get, set) {
|
|
519
|
+
var is_tracked = is_tracked_object(v);
|
|
520
|
+
|
|
521
|
+
if (is_tracked) {
|
|
522
|
+
return v;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (typeof v === 'function') {
|
|
526
|
+
return {
|
|
527
|
+
a: get || set ? { get, set } : empty_get_set,
|
|
528
|
+
c: 0,
|
|
529
|
+
co: active_component,
|
|
530
|
+
d: null,
|
|
531
|
+
f: TRACKED | DERIVED,
|
|
532
|
+
fn: v,
|
|
533
|
+
v: UNINITIALIZED,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return tracked(v, get, set);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* @param {Record<string|symbol, any>} v
|
|
542
|
+
* @param {(symbol | string)[]} l
|
|
543
|
+
* @returns {Tracked[]}
|
|
544
|
+
*/
|
|
545
|
+
export function track_split(v, l) {
|
|
546
|
+
var is_tracked = is_tracked_object(v);
|
|
547
|
+
|
|
548
|
+
if (is_tracked || typeof v !== 'object' || v === null || is_array(v)) {
|
|
549
|
+
throw new TypeError('Invalid value: expected a non-tracked object');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/** @type {Tracked[]} */
|
|
553
|
+
var out = [];
|
|
554
|
+
/** @type {Record<string|symbol, any>} */
|
|
555
|
+
var rest = {};
|
|
556
|
+
/** @type {Record<PropertyKey, 1>} */
|
|
557
|
+
var done = {};
|
|
558
|
+
var props = Reflect.ownKeys(v);
|
|
559
|
+
|
|
560
|
+
for (let i = 0, key, t; i < l.length; i++) {
|
|
561
|
+
key = l[i];
|
|
562
|
+
|
|
563
|
+
if (props.includes(key)) {
|
|
564
|
+
if (is_tracked_object(v[key])) {
|
|
565
|
+
t = v[key];
|
|
566
|
+
} else {
|
|
567
|
+
t = tracked(undefined);
|
|
568
|
+
t = define_property(t, '__v', /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
|
|
569
|
+
}
|
|
570
|
+
} else {
|
|
571
|
+
t = tracked(undefined);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
out[i] = t;
|
|
575
|
+
done[key] = 1;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
for (let i = 0, key; i < props.length; i++) {
|
|
579
|
+
key = props[i];
|
|
580
|
+
if (done[key]) {
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
define_property(rest, key, /** @type {PropertyDescriptor} */ (get_descriptor(v, key)));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
out.push(tracked(rest));
|
|
587
|
+
|
|
588
|
+
return out;
|
|
589
|
+
}
|
package/src/utils/builders.js
CHANGED
|
@@ -8,13 +8,22 @@ import { sanitize_template_string } from './sanitize_template_string.js';
|
|
|
8
8
|
* @template {AST.Node} T
|
|
9
9
|
* @param {T} node
|
|
10
10
|
* @param {AST.NodeWithLocation | undefined} loc_info
|
|
11
|
+
* @param {boolean} is_deep_copy
|
|
11
12
|
* @returns {T}
|
|
12
13
|
*/
|
|
13
|
-
function set_location(node, loc_info) {
|
|
14
|
+
export function set_location(node, loc_info, is_deep_copy = false) {
|
|
14
15
|
if (loc_info) {
|
|
15
16
|
node.start = loc_info.start;
|
|
16
17
|
node.end = loc_info.end;
|
|
17
|
-
|
|
18
|
+
|
|
19
|
+
if (is_deep_copy) {
|
|
20
|
+
node.loc = {
|
|
21
|
+
start: { ...loc_info.loc.start },
|
|
22
|
+
end: { ...loc_info.loc.end },
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
node.loc = loc_info.loc;
|
|
26
|
+
}
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
return node;
|
|
@@ -173,7 +182,7 @@ export function call(callee, ...args) {
|
|
|
173
182
|
|
|
174
183
|
/**
|
|
175
184
|
* @param {string | AST.Expression} callee
|
|
176
|
-
* @param {...AST.Expression} args
|
|
185
|
+
* @param {...(AST.Expression | AST.SpreadElement | false | undefined)} args
|
|
177
186
|
* @returns {AST.ChainExpression}
|
|
178
187
|
*/
|
|
179
188
|
export function maybe_call(callee, ...args) {
|
|
@@ -362,14 +371,25 @@ export function literal(value, loc_info) {
|
|
|
362
371
|
* @param {string | AST.Expression | AST.PrivateIdentifier} property
|
|
363
372
|
* @param {boolean} computed
|
|
364
373
|
* @param {boolean} optional
|
|
374
|
+
* @param {AST.NodeWithLocation} [loc_info]
|
|
365
375
|
* @returns {AST.MemberExpression}
|
|
366
376
|
*/
|
|
367
|
-
export function member(object, property, computed = false, optional = false) {
|
|
377
|
+
export function member(object, property, computed = false, optional = false, loc_info) {
|
|
368
378
|
if (typeof property === 'string') {
|
|
369
379
|
property = id(property);
|
|
370
380
|
}
|
|
371
381
|
|
|
372
|
-
|
|
382
|
+
/** @type {AST.MemberExpression} */
|
|
383
|
+
const node = {
|
|
384
|
+
type: 'MemberExpression',
|
|
385
|
+
object,
|
|
386
|
+
property,
|
|
387
|
+
computed,
|
|
388
|
+
optional,
|
|
389
|
+
metadata: { path: [] },
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
return set_location(node, loc_info);
|
|
373
393
|
}
|
|
374
394
|
|
|
375
395
|
/**
|
|
@@ -139,7 +139,7 @@ describe('basic client > attribute rendering', () => {
|
|
|
139
139
|
expect(div.classList.contains('active')).toBe(false);
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
-
it('applies scoped ripple
|
|
142
|
+
it('applies scoped ripple class to multiple elements with dynamic class expressions', () => {
|
|
143
143
|
component Basic() {
|
|
144
144
|
let selected = track(1);
|
|
145
145
|
|
|
@@ -26,6 +26,53 @@ describe('basic client > components & composition', () => {
|
|
|
26
26
|
expect(paragraph.textContent).toBe('Card content here');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
it('does not render a falsy component call', () => {
|
|
30
|
+
component Card(props: PropsWithChildren<{}>) {
|
|
31
|
+
<div class="card">
|
|
32
|
+
<props.children />
|
|
33
|
+
</div>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
component Basic() {
|
|
37
|
+
<Card>
|
|
38
|
+
component test() {
|
|
39
|
+
<p>{'Card content here'}</p>
|
|
40
|
+
}
|
|
41
|
+
</Card>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
render(Basic);
|
|
45
|
+
|
|
46
|
+
const card = container.querySelector('.card');
|
|
47
|
+
const paragraph = card.querySelector('p');
|
|
48
|
+
|
|
49
|
+
expect(card).toBeTruthy();
|
|
50
|
+
expect(paragraph).toBeFalsy();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders a component when children is set a component prop', () => {
|
|
54
|
+
component Card(props: PropsWithChildren<{}>) {
|
|
55
|
+
<div class="card">
|
|
56
|
+
<props.children />
|
|
57
|
+
</div>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
component Basic() {
|
|
61
|
+
component children() {
|
|
62
|
+
<p>{'Card content here'}</p>
|
|
63
|
+
}
|
|
64
|
+
<Card {children} />
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
render(Basic);
|
|
68
|
+
|
|
69
|
+
const card = container.querySelector('.card');
|
|
70
|
+
const paragraph = card.querySelector('p');
|
|
71
|
+
|
|
72
|
+
expect(card).toBeTruthy();
|
|
73
|
+
expect(paragraph.textContent).toBe('Card content here');
|
|
74
|
+
});
|
|
75
|
+
|
|
29
76
|
it('renders with nested components and prop passing', () => {
|
|
30
77
|
component Button(props: PropsWithExtras<{
|
|
31
78
|
variant: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Tracked, Props } from 'ripple';
|
|
2
|
+
import { track, trackSplit, effect, flushSync } from 'ripple';
|
|
2
3
|
|
|
3
4
|
describe('composite > props', () => {
|
|
4
5
|
it('correctly handles default prop values', () => {
|
|
@@ -56,7 +57,7 @@ describe('composite > props', () => {
|
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
it('correctly handles no props #2', () => {
|
|
59
|
-
component Child({ foo }) {
|
|
60
|
+
component Child({ foo }: { foo?: number }) {
|
|
60
61
|
<div>{foo}</div>
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -76,7 +77,7 @@ describe('composite > props', () => {
|
|
|
76
77
|
it('mutating a tracked value prop should work as intended', () => {
|
|
77
78
|
const logs: number[] = [];
|
|
78
79
|
|
|
79
|
-
component Counter({ count }) {
|
|
80
|
+
component Counter({ count }: { count: Tracked<number> }) {
|
|
80
81
|
effect(() => {
|
|
81
82
|
logs.push(@count);
|
|
82
83
|
});
|
|
@@ -120,7 +121,7 @@ describe('composite > props', () => {
|
|
|
120
121
|
</style>
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
component Toggle(props) {
|
|
124
|
+
component Toggle(props: { pressed: Tracked<boolean> }) {
|
|
124
125
|
const [pressed, rest] = trackSplit(props, ['pressed']);
|
|
125
126
|
const onClick = () => (@pressed = !@pressed);
|
|
126
127
|
<Button {...@rest} class={@pressed ? 'on' : 'off'} {onClick}>{'button 1'}</Button>
|
|
@@ -146,4 +147,48 @@ describe('composite > props', () => {
|
|
|
146
147
|
expect(button1.className).toContain('off');
|
|
147
148
|
expect(button2.className).toContain('off');
|
|
148
149
|
});
|
|
150
|
+
|
|
151
|
+
it('correctly renders destructured props', () => {
|
|
152
|
+
interface ProductInfo {
|
|
153
|
+
id: string;
|
|
154
|
+
name: string;
|
|
155
|
+
organizationName: string;
|
|
156
|
+
description: string;
|
|
157
|
+
imageUrl: string;
|
|
158
|
+
price: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
component Product({ id, name, organizationName, description, imageUrl, price }: ProductInfo) {
|
|
162
|
+
<article class="no-padding">
|
|
163
|
+
<img class="responsive small" src={imageUrl} alt={name} />
|
|
164
|
+
<span>{id}</span>
|
|
165
|
+
<h5>{name}</h5>
|
|
166
|
+
<h3>{organizationName}</h3>
|
|
167
|
+
<p>{description}</p>
|
|
168
|
+
<price class="price">{price}</price>
|
|
169
|
+
</article>
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
component App() {
|
|
173
|
+
<Product
|
|
174
|
+
id="1"
|
|
175
|
+
name="Product 1"
|
|
176
|
+
organizationName="Org 1"
|
|
177
|
+
description="Description 1"
|
|
178
|
+
imageUrl="https://picsum.photos/300/200"
|
|
179
|
+
price={15}
|
|
180
|
+
/>
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
render(App);
|
|
184
|
+
|
|
185
|
+
expect(container.querySelector('img').getAttribute('src')).toBe(
|
|
186
|
+
'https://picsum.photos/300/200',
|
|
187
|
+
);
|
|
188
|
+
expect(container.querySelector('span').textContent).toBe('1');
|
|
189
|
+
expect(container.querySelector('h5').textContent).toBe('Product 1');
|
|
190
|
+
expect(container.querySelector('h3').textContent).toBe('Org 1');
|
|
191
|
+
expect(container.querySelector('p').textContent).toBe('Description 1');
|
|
192
|
+
expect(container.querySelector('price').textContent).toBe('15');
|
|
193
|
+
});
|
|
149
194
|
});
|
|
@@ -15,6 +15,50 @@ describe('dynamic DOM elements', () => {
|
|
|
15
15
|
expect(element.textContent).toBe('Hello World');
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
// The ts errors below are due to limitations in our current tsx generation for dynamic elements.
|
|
19
|
+
// They can be ignored for now. But we'll fix them via jsx() vs <jsx>
|
|
20
|
+
it('renders static dynamic element from a plain object with a tracked property', () => {
|
|
21
|
+
component App() {
|
|
22
|
+
let obj = { tag: track('div') };
|
|
23
|
+
|
|
24
|
+
<obj.@tag>{'Hello World'}</obj.@tag>
|
|
25
|
+
}
|
|
26
|
+
render(App);
|
|
27
|
+
|
|
28
|
+
const element = container.querySelector('div');
|
|
29
|
+
expect(element).toBeTruthy();
|
|
30
|
+
expect(element.textContent).toBe('Hello World');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('renders static dynamic element from a tracked object with a tracked property', () => {
|
|
34
|
+
component App() {
|
|
35
|
+
let obj = track({ tag: track('div') });
|
|
36
|
+
|
|
37
|
+
<@obj.@tag>{'Hello World'}</@obj.@tag>
|
|
38
|
+
}
|
|
39
|
+
render(App);
|
|
40
|
+
|
|
41
|
+
const element = container.querySelector('div');
|
|
42
|
+
expect(element).toBeTruthy();
|
|
43
|
+
expect(element.textContent).toBe('Hello World');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it(
|
|
47
|
+
'renders static dynamic element from a tracked object with a computed tracked property',
|
|
48
|
+
() => {
|
|
49
|
+
component App() {
|
|
50
|
+
let obj = track({ tag: track('div') });
|
|
51
|
+
|
|
52
|
+
<@obj.@['tag']>{'Hello World'}</@obj.@['tag']>
|
|
53
|
+
}
|
|
54
|
+
render(App);
|
|
55
|
+
|
|
56
|
+
const element = container.querySelector('div');
|
|
57
|
+
expect(element).toBeTruthy();
|
|
58
|
+
expect(element.textContent).toBe('Hello World');
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
18
62
|
it('renders reactive dynamic element', () => {
|
|
19
63
|
component App() {
|
|
20
64
|
let tag = track('div');
|
|
@@ -316,4 +316,44 @@ describe('switch statements', () => {
|
|
|
316
316
|
expect(container.querySelector('div').querySelectorAll('div')[1].id).toBe('b');
|
|
317
317
|
},
|
|
318
318
|
);
|
|
319
|
+
|
|
320
|
+
it('renders bare text nodes in switch fall-through cases without element wrappers', () => {
|
|
321
|
+
component App() {
|
|
322
|
+
let value = 'a';
|
|
323
|
+
|
|
324
|
+
<div>
|
|
325
|
+
<span>{'before'}</span>
|
|
326
|
+
switch (value) {
|
|
327
|
+
case 'a':
|
|
328
|
+
{'Case A'}
|
|
329
|
+
case 'b':
|
|
330
|
+
{'Case B'}
|
|
331
|
+
}
|
|
332
|
+
</div>
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
render(App);
|
|
336
|
+
expect(container.querySelector('div').textContent).toBe('beforeCase ACase B');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('renders bare text nodes in switch cases without element wrappers', () => {
|
|
340
|
+
component App() {
|
|
341
|
+
let value = 'a';
|
|
342
|
+
|
|
343
|
+
<div>
|
|
344
|
+
<span>{'before'}</span>
|
|
345
|
+
switch (value) {
|
|
346
|
+
case 'a':
|
|
347
|
+
{'Case A'}
|
|
348
|
+
break;
|
|
349
|
+
case 'b':
|
|
350
|
+
{'Case B'}
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
</div>
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
render(App);
|
|
357
|
+
expect(container.querySelector('div').textContent).toBe('beforeCase A');
|
|
358
|
+
});
|
|
319
359
|
});
|
package/tests/client.d.ts
CHANGED
|
@@ -1,25 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
declare var error: string | undefined;
|
|
3
|
-
declare function render(component: () => void): void;
|
|
1
|
+
import type { Component } from '#public';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
declare global {
|
|
4
|
+
function render(component: Component): void;
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
interface HTMLElement {
|
|
11
|
-
// We don't care about checking if it returned an element or null in tests
|
|
12
|
-
// because if it returned null, those tests will fail anyway. This
|
|
13
|
-
// typing drastically simplifies testing: you don't have to check if the
|
|
14
|
-
// query returned null or an actual element, and you don't have to do
|
|
15
|
-
// optional chaining everywhere (elem?.textContent)
|
|
16
|
-
querySelector<K extends keyof TagNameMap>(selectors: K): TagNameMap[K];
|
|
17
|
-
querySelector(selectors: string): HTMLElement;
|
|
18
|
-
querySelectorAll<K extends keyof TagNameMap>(
|
|
19
|
-
selectors: K,
|
|
20
|
-
): NodeListOf<TagNameMap[K]>;
|
|
21
|
-
querySelectorAll(selectors: string): NodeListOf<Element>;
|
|
22
|
-
|
|
23
|
-
// Allow dynamic properties for delegated event handlers
|
|
24
|
-
[key: string]: any;
|
|
6
|
+
var container: HTMLDivElement;
|
|
7
|
+
var error: string | undefined;
|
|
25
8
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// export allows this file to be treated as a module
|
|
2
|
+
export {};
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
// Helper type for test attributes that allows custom data-* and other attributes
|
|
6
|
+
type TestAttributes = Record<string, any>;
|
|
7
|
+
|
|
8
|
+
type TagNameMap = HTMLElementTagNameMap & SVGElementTagNameMap;
|
|
9
|
+
|
|
10
|
+
interface ParentNode {
|
|
11
|
+
// We don't care about checking if it returned an element or null in tests
|
|
12
|
+
// because if it returned null, those tests will fail anyway. This
|
|
13
|
+
// typing drastically simplifies testing: you don't have to check if the
|
|
14
|
+
// query returned null or an actual element, and you don't have to do
|
|
15
|
+
// optional chaining everywhere (elem?.textContent)
|
|
16
|
+
querySelector<K extends keyof TagNameMap>(selectors: K): TagNameMap[K];
|
|
17
|
+
querySelector(selectors: string): HTMLElement;
|
|
18
|
+
querySelectorAll<K extends keyof TagNameMap>(selectors: K): NodeListOf<TagNameMap[K]>;
|
|
19
|
+
querySelectorAll(selectors: string): NodeListOf<Element>;
|
|
20
|
+
|
|
21
|
+
// Allow dynamic properties for delegated event handlers
|
|
22
|
+
[key: string]: any;
|
|
23
|
+
}
|
|
24
|
+
}
|