ripple 0.3.11 → 0.3.13
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/CHANGELOG.md +43 -0
- package/package.json +8 -2
- package/src/compiler/phases/1-parse/index.js +73 -30
- package/src/compiler/phases/2-analyze/index.js +28 -58
- package/src/compiler/phases/3-transform/client/index.js +127 -164
- package/src/compiler/phases/3-transform/segments.js +4 -8
- package/src/compiler/phases/3-transform/server/index.js +210 -360
- package/src/compiler/types/import.d.ts +0 -12
- package/src/compiler/types/index.d.ts +12 -5
- package/src/compiler/types/parse.d.ts +2 -0
- package/src/compiler/utils.js +39 -44
- package/src/helpers.d.ts +2 -0
- package/src/runtime/index-client.js +15 -13
- package/src/runtime/index-server.js +18 -11
- package/src/runtime/internal/client/blocks.js +19 -23
- package/src/runtime/internal/client/constants.js +20 -9
- package/src/runtime/internal/client/index.js +14 -4
- package/src/runtime/internal/client/runtime.js +435 -173
- package/src/runtime/internal/client/try.js +334 -156
- package/src/runtime/internal/client/types.d.ts +26 -0
- package/src/runtime/internal/server/blocks.js +183 -0
- package/src/runtime/internal/server/constants.js +7 -0
- package/src/runtime/internal/server/index.js +780 -148
- package/src/runtime/internal/server/types.d.ts +35 -0
- package/src/server/index.js +1 -1
- package/src/utils/async.js +35 -0
- package/src/utils/builders.js +3 -1
- package/tests/client/__snapshots__/computed-properties.test.rsrx.snap +49 -0
- package/tests/client/__snapshots__/for.test.rsrx.snap +319 -0
- package/tests/client/__snapshots__/html.test.rsrx.snap +40 -0
- package/tests/client/_etc.test.rsrx +7 -0
- package/tests/client/array/{array.static.test.ripple → array.static.test.rsrx} +18 -20
- package/tests/client/async-suspend.test.rsrx +662 -0
- package/tests/client/basic/__snapshots__/basic.attributes.test.rsrx.snap +60 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.rsrx.snap +59 -0
- package/tests/client/basic/{basic.errors.test.ripple → basic.errors.test.rsrx} +2 -2
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +12 -0
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +46 -0
- package/tests/client/compiler/{compiler.try-in-function.test.ripple → compiler.try-in-function.test.rsrx} +8 -6
- package/tests/client/composite/__snapshots__/composite.render.test.rsrx.snap +37 -0
- package/tests/client/{function-overload.test.ripple → function-overload.test.rsrx} +1 -1
- package/tests/client/try.test.rsrx +1702 -0
- package/tests/hydration/build-components.js +5 -3
- package/tests/hydration/compiled/client/head.js +11 -11
- package/tests/hydration/compiled/client/mixed-control-flow.js +55 -70
- package/tests/hydration/compiled/client/nested-control-flow.js +72 -88
- package/tests/hydration/compiled/client/try.js +42 -54
- package/tests/hydration/compiled/server/basic.js +491 -369
- package/tests/hydration/compiled/server/composite.js +153 -128
- package/tests/hydration/compiled/server/events.js +166 -145
- package/tests/hydration/compiled/server/for.js +821 -677
- package/tests/hydration/compiled/server/head.js +200 -165
- package/tests/hydration/compiled/server/hmr.js +62 -54
- package/tests/hydration/compiled/server/html-in-template.js +64 -55
- package/tests/hydration/compiled/server/html.js +1477 -1360
- package/tests/hydration/compiled/server/if-children.js +448 -408
- package/tests/hydration/compiled/server/if.js +204 -171
- package/tests/hydration/compiled/server/mixed-control-flow.js +237 -195
- package/tests/hydration/compiled/server/nested-control-flow.js +533 -467
- package/tests/hydration/compiled/server/portal.js +94 -107
- package/tests/hydration/compiled/server/reactivity.js +87 -64
- package/tests/hydration/compiled/server/return.js +1424 -1174
- package/tests/hydration/compiled/server/switch.js +268 -238
- package/tests/hydration/compiled/server/try.js +98 -87
- package/tests/hydration/components/{mixed-control-flow.ripple → mixed-control-flow.rsrx} +2 -2
- package/tests/hydration/components/{try.ripple → try.rsrx} +4 -2
- package/tests/hydration/mixed-control-flow.test.js +14 -0
- package/tests/hydration/nested-control-flow.test.js +50 -48
- package/tests/hydration/try.test.js +25 -0
- package/tests/server/__snapshots__/compiler.test.ripple.snap +0 -32
- package/tests/server/__snapshots__/compiler.test.rsrx.snap +95 -0
- package/tests/server/{compiler.test.ripple → compiler.test.rsrx} +0 -17
- package/tests/server/{html-nesting-validation.test.ripple → html-nesting-validation.test.rsrx} +3 -3
- package/tests/server/streaming-ssr.test.rsrx +115 -0
- package/tests/server/try.test.rsrx +503 -0
- package/tests/utils/compiler-compat-config.test.js +3 -3
- package/tests/utils/vite-plugin-config.test.js +1 -1
- package/tests/utils/vite-plugin-hmr.test.js +5 -5
- package/tsconfig.json +4 -0
- package/types/index.d.ts +13 -23
- package/types/server.d.ts +43 -16
- package/tests/client/_etc.test.ripple +0 -5
- package/tests/client/async-suspend.test.ripple +0 -94
- package/tests/client/try.test.ripple +0 -196
- package/tests/server/streaming-ssr.test.ripple +0 -68
- package/tests/server/try.test.ripple +0 -82
- /package/tests/client/array/{array.copy-within.test.ripple → array.copy-within.test.rsrx} +0 -0
- /package/tests/client/array/{array.derived.test.ripple → array.derived.test.rsrx} +0 -0
- /package/tests/client/array/{array.iteration.test.ripple → array.iteration.test.rsrx} +0 -0
- /package/tests/client/array/{array.mutations.test.ripple → array.mutations.test.rsrx} +0 -0
- /package/tests/client/array/{array.to-methods.test.ripple → array.to-methods.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.collections.test.ripple → basic.collections.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.events.test.ripple → basic.events.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.get-set.test.ripple → basic.get-set.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.hmr.test.ripple → basic.hmr.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.reactivity.test.ripple → basic.reactivity.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.rendering.test.ripple → basic.rendering.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.styling.test.ripple → basic.styling.test.rsrx} +0 -0
- /package/tests/client/basic/{basic.utilities.test.ripple → basic.utilities.test.rsrx} +0 -0
- /package/tests/client/{boundaries.test.ripple → boundaries.test.rsrx} +0 -0
- /package/tests/client/compiler/{compiler.assignments.test.ripple → compiler.assignments.test.rsrx} +0 -0
- /package/tests/client/compiler/{compiler.attributes.test.ripple → compiler.attributes.test.rsrx} +0 -0
- /package/tests/client/compiler/{compiler.basic.test.ripple → compiler.basic.test.rsrx} +0 -0
- /package/tests/client/compiler/{compiler.regex.test.ripple → compiler.regex.test.rsrx} +0 -0
- /package/tests/client/compiler/{compiler.tracked-access.test.ripple → compiler.tracked-access.test.rsrx} +0 -0
- /package/tests/client/compiler/{compiler.typescript.test.ripple → compiler.typescript.test.rsrx} +0 -0
- /package/tests/client/composite/{composite.dynamic-components.test.ripple → composite.dynamic-components.test.rsrx} +0 -0
- /package/tests/client/composite/{composite.generics.test.ripple → composite.generics.test.rsrx} +0 -0
- /package/tests/client/composite/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
- /package/tests/client/composite/{composite.reactivity.test.ripple → composite.reactivity.test.rsrx} +0 -0
- /package/tests/client/composite/{composite.render.test.ripple → composite.render.test.rsrx} +0 -0
- /package/tests/client/{computed-properties.test.ripple → computed-properties.test.rsrx} +0 -0
- /package/tests/client/{context.test.ripple → context.test.rsrx} +0 -0
- /package/tests/client/css/{global-additional-cases.test.ripple → global-additional-cases.test.rsrx} +0 -0
- /package/tests/client/css/{global-advanced-selectors.test.ripple → global-advanced-selectors.test.rsrx} +0 -0
- /package/tests/client/css/{global-at-rules.test.ripple → global-at-rules.test.rsrx} +0 -0
- /package/tests/client/css/{global-basic.test.ripple → global-basic.test.rsrx} +0 -0
- /package/tests/client/css/{global-classes-ids.test.ripple → global-classes-ids.test.rsrx} +0 -0
- /package/tests/client/css/{global-combinators.test.ripple → global-combinators.test.rsrx} +0 -0
- /package/tests/client/css/{global-complex-nesting.test.ripple → global-complex-nesting.test.rsrx} +0 -0
- /package/tests/client/css/{global-edge-cases.test.ripple → global-edge-cases.test.rsrx} +0 -0
- /package/tests/client/css/{global-keyframes.test.ripple → global-keyframes.test.rsrx} +0 -0
- /package/tests/client/css/{global-nested.test.ripple → global-nested.test.rsrx} +0 -0
- /package/tests/client/css/{global-pseudo.test.ripple → global-pseudo.test.rsrx} +0 -0
- /package/tests/client/css/{global-scoping.test.ripple → global-scoping.test.rsrx} +0 -0
- /package/tests/client/css/{style-identifier.test.ripple → style-identifier.test.rsrx} +0 -0
- /package/tests/client/{date.test.ripple → date.test.rsrx} +0 -0
- /package/tests/client/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
- /package/tests/client/{events.test.ripple → events.test.rsrx} +0 -0
- /package/tests/client/{for.test.ripple → for.test.rsrx} +0 -0
- /package/tests/client/{function-overload-import.ripple → function-overload-import.rsrx} +0 -0
- /package/tests/client/{head.test.ripple → head.test.rsrx} +0 -0
- /package/tests/client/{html.test.ripple → html.test.rsrx} +0 -0
- /package/tests/client/{input-value.test.ripple → input-value.test.rsrx} +0 -0
- /package/tests/client/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
- /package/tests/client/{map.test.ripple → map.test.rsrx} +0 -0
- /package/tests/client/{media-query.test.ripple → media-query.test.rsrx} +0 -0
- /package/tests/client/{object.test.ripple → object.test.rsrx} +0 -0
- /package/tests/client/{portal.test.ripple → portal.test.rsrx} +0 -0
- /package/tests/client/{ref.test.ripple → ref.test.rsrx} +0 -0
- /package/tests/client/{return.test.ripple → return.test.rsrx} +0 -0
- /package/tests/client/{set.test.ripple → set.test.rsrx} +0 -0
- /package/tests/client/{svg.test.ripple → svg.test.rsrx} +0 -0
- /package/tests/client/{switch.test.ripple → switch.test.rsrx} +0 -0
- /package/tests/client/{tsx.test.ripple → tsx.test.rsrx} +0 -0
- /package/tests/client/{typescript-generics.test.ripple → typescript-generics.test.rsrx} +0 -0
- /package/tests/client/url/{url.derived.test.ripple → url.derived.test.rsrx} +0 -0
- /package/tests/client/url/{url.parsing.test.ripple → url.parsing.test.rsrx} +0 -0
- /package/tests/client/url/{url.partial-removal.test.ripple → url.partial-removal.test.rsrx} +0 -0
- /package/tests/client/url/{url.reactivity.test.ripple → url.reactivity.test.rsrx} +0 -0
- /package/tests/client/url/{url.serialization.test.ripple → url.serialization.test.rsrx} +0 -0
- /package/tests/client/url-search-params/{url-search-params.derived.test.ripple → url-search-params.derived.test.rsrx} +0 -0
- /package/tests/client/url-search-params/{url-search-params.initialization.test.ripple → url-search-params.initialization.test.rsrx} +0 -0
- /package/tests/client/url-search-params/{url-search-params.iteration.test.ripple → url-search-params.iteration.test.rsrx} +0 -0
- /package/tests/client/url-search-params/{url-search-params.mutation.test.ripple → url-search-params.mutation.test.rsrx} +0 -0
- /package/tests/client/url-search-params/{url-search-params.retrieval.test.ripple → url-search-params.retrieval.test.rsrx} +0 -0
- /package/tests/client/url-search-params/{url-search-params.serialization.test.ripple → url-search-params.serialization.test.rsrx} +0 -0
- /package/tests/client/url-search-params/{url-search-params.tracked-url.test.ripple → url-search-params.tracked-url.test.rsrx} +0 -0
- /package/tests/hydration/components/{basic.ripple → basic.rsrx} +0 -0
- /package/tests/hydration/components/{composite.ripple → composite.rsrx} +0 -0
- /package/tests/hydration/components/{events.ripple → events.rsrx} +0 -0
- /package/tests/hydration/components/{for.ripple → for.rsrx} +0 -0
- /package/tests/hydration/components/{head.ripple → head.rsrx} +0 -0
- /package/tests/hydration/components/{hmr.ripple → hmr.rsrx} +0 -0
- /package/tests/hydration/components/{html-in-template.ripple → html-in-template.rsrx} +0 -0
- /package/tests/hydration/components/{html.ripple → html.rsrx} +0 -0
- /package/tests/hydration/components/{if-children.ripple → if-children.rsrx} +0 -0
- /package/tests/hydration/components/{if.ripple → if.rsrx} +0 -0
- /package/tests/hydration/components/{nested-control-flow.ripple → nested-control-flow.rsrx} +0 -0
- /package/tests/hydration/components/{portal.ripple → portal.rsrx} +0 -0
- /package/tests/hydration/components/{reactivity.ripple → reactivity.rsrx} +0 -0
- /package/tests/hydration/components/{return.ripple → return.rsrx} +0 -0
- /package/tests/hydration/components/{switch.ripple → switch.rsrx} +0 -0
- /package/tests/server/{await.test.ripple → await.test.rsrx} +0 -0
- /package/tests/server/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
- /package/tests/server/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
- /package/tests/server/{basic.test.ripple → basic.test.rsrx} +0 -0
- /package/tests/server/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
- /package/tests/server/{composite.test.ripple → composite.test.rsrx} +0 -0
- /package/tests/server/{context.test.ripple → context.test.rsrx} +0 -0
- /package/tests/server/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
- /package/tests/server/{for.test.ripple → for.test.rsrx} +0 -0
- /package/tests/server/{head.test.ripple → head.test.rsrx} +0 -0
- /package/tests/server/{if.test.ripple → if.test.rsrx} +0 -0
- /package/tests/server/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
- /package/tests/server/{return.test.ripple → return.test.rsrx} +0 -0
- /package/tests/server/{style-identifier.test.ripple → style-identifier.test.rsrx} +0 -0
- /package/tests/server/{switch.test.ripple → switch.test.rsrx} +0 -0
package/types/index.d.ts
CHANGED
|
@@ -141,18 +141,21 @@ declare global {
|
|
|
141
141
|
|
|
142
142
|
export function createRefKey(): symbol;
|
|
143
143
|
|
|
144
|
+
export const UNINITIALIZED: unique symbol;
|
|
145
|
+
export const DERIVED_UPDATED: unique symbol;
|
|
146
|
+
export const SUSPENSE_PENDING: unique symbol;
|
|
147
|
+
export const SUSPENSE_REJECTED: unique symbol;
|
|
148
|
+
|
|
144
149
|
// Base Tracked interface - all tracked values have a '#v' property containing the actual value
|
|
145
150
|
interface TrackedBase<V> {
|
|
146
151
|
'#v': V;
|
|
147
152
|
value: V;
|
|
148
153
|
}
|
|
149
|
-
|
|
150
154
|
// Augment Tracked to be callable when V is a Component
|
|
151
155
|
// This allows <@Something /> to work in JSX when Something is Tracked<Component>
|
|
152
156
|
interface TrackedCallable<V> {
|
|
153
157
|
(props: V extends Component<infer P> ? P : never): V extends Component ? void : never;
|
|
154
158
|
}
|
|
155
|
-
|
|
156
159
|
// Supports indexed access: track(0)[0] → value, track(0)[1] → Tracked<V>
|
|
157
160
|
// And destructuring `const [one, two] = track(0);`
|
|
158
161
|
export type Tracked<V> = [V, Tracked<V>] & TrackedBase<V> & TrackedCallable<V>;
|
|
@@ -188,6 +191,14 @@ export function track<V>(
|
|
|
188
191
|
// Overload for non-function values
|
|
189
192
|
export function track<V>(value?: V, get?: (v: V) => V, set?: (next: V, prev: V) => V): Tracked<V>;
|
|
190
193
|
|
|
194
|
+
export function trackAsync<V>(
|
|
195
|
+
value: () => PromiseLike<V> | { promise: PromiseLike<V>; abortController: AbortController },
|
|
196
|
+
): Tracked<V>;
|
|
197
|
+
|
|
198
|
+
export function trackPending<V>(value: Tracked<V> | (() => any)): boolean;
|
|
199
|
+
|
|
200
|
+
export function peek<V>(tracked: Tracked<V>): V;
|
|
201
|
+
|
|
191
202
|
export interface AddEventOptions extends ExtendedEventOptions {
|
|
192
203
|
customName?: string;
|
|
193
204
|
}
|
|
@@ -531,24 +542,3 @@ export function bindFiles<V extends FileList>(
|
|
|
531
542
|
tracked: Tracked<V | null | undefined> | GetFunction<V | null | undefined>,
|
|
532
543
|
setter?: SetFunction<V>,
|
|
533
544
|
): (node: HTMLInputElement) => void;
|
|
534
|
-
|
|
535
|
-
type ServerBlock = {};
|
|
536
|
-
|
|
537
|
-
export interface RippleNamespace {
|
|
538
|
-
array: RippleArrayCallable;
|
|
539
|
-
object: RippleObjectCallable;
|
|
540
|
-
context: ContextCallable;
|
|
541
|
-
date: RippleDateCallable;
|
|
542
|
-
effect: typeof effect;
|
|
543
|
-
map: RippleMapCallable;
|
|
544
|
-
mediaQuery: MediaQueryCallable;
|
|
545
|
-
set: RippleSetCallable;
|
|
546
|
-
url: RippleURLCallable;
|
|
547
|
-
urlSearchParams: RippleURLSearchParamsCallable;
|
|
548
|
-
untrack: typeof untrack;
|
|
549
|
-
track: typeof track;
|
|
550
|
-
style: Record<string, string>;
|
|
551
|
-
server: ServerBlock;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
export declare const ripple_namespace: RippleNamespace;
|
package/types/server.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Readable } from 'node:stream';
|
|
1
|
+
import type { Component } from '#public';
|
|
3
2
|
|
|
4
3
|
// Re-export runtime types for server-compiled components
|
|
5
4
|
export {
|
|
@@ -17,28 +16,56 @@ export {
|
|
|
17
16
|
RippleURLSearchParams,
|
|
18
17
|
} from './index.js';
|
|
19
18
|
|
|
20
|
-
export interface
|
|
19
|
+
export interface RenderResult {
|
|
21
20
|
head: string;
|
|
22
21
|
body: string;
|
|
23
22
|
css: Set<string>;
|
|
23
|
+
topLevelError?: Error | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RenderStreamResult {
|
|
27
|
+
stream: StreamSink;
|
|
28
|
+
topLevelError?: Error | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface StreamSink {
|
|
24
32
|
push(chunk: string): void;
|
|
25
|
-
|
|
33
|
+
close(): void;
|
|
34
|
+
error(reason: unknown): void;
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
export type WebStream = ReadableStream<Uint8Array>;
|
|
38
|
+
|
|
39
|
+
export interface Stream {
|
|
40
|
+
controller: ReadableStreamDefaultController<Uint8Array>;
|
|
41
|
+
textEncoder: TextEncoder;
|
|
42
|
+
stream: WebStream;
|
|
43
|
+
sink: StreamSink;
|
|
31
44
|
}
|
|
32
45
|
|
|
33
|
-
export interface
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
export interface BaseRenderOptions {
|
|
47
|
+
stream?: StreamSink;
|
|
48
|
+
// defaults to true
|
|
49
|
+
// set to false to add more content
|
|
50
|
+
closeStream?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface StreamingRenderOptions extends BaseRenderOptions {
|
|
54
|
+
stream: StreamSink;
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
57
|
+
export interface RenderOptions extends BaseRenderOptions {
|
|
58
|
+
stream?: undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export declare function create_ssr_stream(): Stream;
|
|
62
|
+
|
|
63
|
+
export declare function render(
|
|
64
|
+
component: Component,
|
|
65
|
+
options?: RenderOptions,
|
|
66
|
+
): Promise<RenderResult>;
|
|
42
67
|
|
|
43
|
-
export
|
|
44
|
-
|
|
68
|
+
export declare function render(
|
|
69
|
+
component: Component,
|
|
70
|
+
options: StreamingRenderOptions,
|
|
71
|
+
): Promise<RenderStreamResult>;
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { flushSync, track } from 'ripple';
|
|
2
|
-
|
|
3
|
-
describe('async suspense', () => {
|
|
4
|
-
it('hides child content during re-suspension when tracked dependency changes', async () => {
|
|
5
|
-
let resolve_fn: (() => void) | null = null;
|
|
6
|
-
|
|
7
|
-
component Child({ countTracked }: { countTracked: any }) {
|
|
8
|
-
await track(() => {
|
|
9
|
-
countTracked.value;
|
|
10
|
-
return new Promise<void>((resolve) => {
|
|
11
|
-
resolve_fn = resolve;
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
<div class="child-content">{'child content'}</div>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
component App() {
|
|
19
|
-
let &[count, countTracked] = track(0);
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
<Child {countTracked} />
|
|
23
|
-
} pending {
|
|
24
|
-
<div class="pending">{'pending...'}</div>
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
<button onClick={() => count++}>{'Increment'}</button>
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
render(App);
|
|
31
|
-
|
|
32
|
-
// Initial state: should show pending
|
|
33
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
34
|
-
flushSync();
|
|
35
|
-
expect(container.innerHTML).toContain('pending...');
|
|
36
|
-
expect(container.innerHTML).not.toContain('child content');
|
|
37
|
-
|
|
38
|
-
// Resolve the first promise
|
|
39
|
-
(resolve_fn as () => void)?.();
|
|
40
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
41
|
-
flushSync();
|
|
42
|
-
|
|
43
|
-
// After resolution: should show child content, not pending
|
|
44
|
-
expect(container.innerHTML).toContain('child content');
|
|
45
|
-
expect(container.innerHTML).not.toContain('pending...');
|
|
46
|
-
|
|
47
|
-
// Now trigger re-suspension by changing count
|
|
48
|
-
const button = container.querySelector('button');
|
|
49
|
-
button?.click();
|
|
50
|
-
flushSync();
|
|
51
|
-
|
|
52
|
-
// Wait for microtask to process
|
|
53
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
54
|
-
flushSync();
|
|
55
|
-
|
|
56
|
-
// BUG: During re-suspension, we should only see pending, not child content
|
|
57
|
-
// The child content should be hidden when the new promise is pending
|
|
58
|
-
expect(container.innerHTML).toContain('pending...');
|
|
59
|
-
expect(container.innerHTML).not.toContain('child content');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('shows pending UI immediately when child suspends', async () => {
|
|
63
|
-
component Child() {
|
|
64
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
65
|
-
<div class="child">{'loaded'}</div>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
component App() {
|
|
69
|
-
try {
|
|
70
|
-
<Child />
|
|
71
|
-
} pending {
|
|
72
|
-
<div class="pending">{'loading...'}</div>
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
render(App);
|
|
77
|
-
|
|
78
|
-
// Wait for microtask
|
|
79
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
80
|
-
flushSync();
|
|
81
|
-
|
|
82
|
-
// Should show pending, not child content
|
|
83
|
-
expect(container.innerHTML).toContain('loading...');
|
|
84
|
-
expect(container.innerHTML).not.toContain('loaded');
|
|
85
|
-
|
|
86
|
-
// Wait for child to resolve
|
|
87
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
88
|
-
flushSync();
|
|
89
|
-
|
|
90
|
-
// Should show child content, not pending
|
|
91
|
-
expect(container.innerHTML).toContain('loaded');
|
|
92
|
-
expect(container.innerHTML).not.toContain('loading...');
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { RippleArray, bindValue, flushSync, track } from 'ripple';
|
|
2
|
-
|
|
3
|
-
describe('try block with catch and pending', () => {
|
|
4
|
-
it('catch block works when component throws before await with pending block', async () => {
|
|
5
|
-
component App() {
|
|
6
|
-
try {
|
|
7
|
-
<ThrowingChild />
|
|
8
|
-
} pending {
|
|
9
|
-
<p>{'loading...'}</p>
|
|
10
|
-
} catch (err) {
|
|
11
|
-
<p>{'caught error'}</p>
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
component ThrowingChild() {
|
|
16
|
-
throw new Error('sync error');
|
|
17
|
-
let data = await Promise.resolve('hello');
|
|
18
|
-
<p>{data}</p>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
render(App);
|
|
22
|
-
|
|
23
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
24
|
-
flushSync();
|
|
25
|
-
|
|
26
|
-
expect(container.innerHTML).toContain('caught error');
|
|
27
|
-
expect(container.innerHTML).not.toContain('loading...');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('catch block works when component throws after await with pending block', async () => {
|
|
31
|
-
component App() {
|
|
32
|
-
try {
|
|
33
|
-
<ThrowingAfterAwait />
|
|
34
|
-
} pending {
|
|
35
|
-
<p>{'loading...'}</p>
|
|
36
|
-
} catch (err) {
|
|
37
|
-
<p>{'caught error'}</p>
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
component ThrowingAfterAwait() {
|
|
42
|
-
let data = await Promise.resolve('hello');
|
|
43
|
-
throw new Error('error after await');
|
|
44
|
-
<p>{data}</p>
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
render(App);
|
|
48
|
-
|
|
49
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
50
|
-
flushSync();
|
|
51
|
-
|
|
52
|
-
expect(container.innerHTML).toContain('caught error');
|
|
53
|
-
expect(container.innerHTML).not.toContain('loading...');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('catch block works with try/catch/pending when async body rejects', async () => {
|
|
57
|
-
component App() {
|
|
58
|
-
try {
|
|
59
|
-
let data = await Promise.reject(new Error('rejected'));
|
|
60
|
-
<p>{data}</p>
|
|
61
|
-
} pending {
|
|
62
|
-
<p>{'loading...'}</p>
|
|
63
|
-
} catch (err) {
|
|
64
|
-
<p>{'caught rejection'}</p>
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
render(App);
|
|
69
|
-
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
71
|
-
flushSync();
|
|
72
|
-
|
|
73
|
-
expect(container.innerHTML).toContain('caught rejection');
|
|
74
|
-
expect(container.innerHTML).not.toContain('loading...');
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('try block', () => {
|
|
79
|
-
it(
|
|
80
|
-
'does not compile ref binds as async callbacks inside try/pending async branches',
|
|
81
|
-
async () => {
|
|
82
|
-
component App() {
|
|
83
|
-
try {
|
|
84
|
-
<Child />
|
|
85
|
-
} pending {
|
|
86
|
-
<p>{'loading...'}</p>
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
component Child() {
|
|
91
|
-
let &[value, valueTracked] = track(1);
|
|
92
|
-
await Promise.resolve(value + 1);
|
|
93
|
-
|
|
94
|
-
<input type="number" {ref bindValue(valueTracked)} />
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
render(App);
|
|
98
|
-
|
|
99
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
100
|
-
flushSync();
|
|
101
|
-
|
|
102
|
-
const input = container.querySelector('input') as HTMLInputElement | null;
|
|
103
|
-
expect(input?.value).toBe('1');
|
|
104
|
-
expect(container.innerHTML).not.toContain('loading...');
|
|
105
|
-
},
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
it('does not crash when async component is used inside try/pending', async () => {
|
|
109
|
-
component App() {
|
|
110
|
-
try {
|
|
111
|
-
<AsyncChild />
|
|
112
|
-
} pending {
|
|
113
|
-
<p>{'loading...'}</p>
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
component AsyncChild() {
|
|
118
|
-
let data = await Promise.resolve(['a', 'b', 'c']);
|
|
119
|
-
|
|
120
|
-
<ul>
|
|
121
|
-
for (let item of data) {
|
|
122
|
-
<li>{item}</li>
|
|
123
|
-
}
|
|
124
|
-
</ul>
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
render(App);
|
|
128
|
-
|
|
129
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
130
|
-
flushSync();
|
|
131
|
-
|
|
132
|
-
const items = container.querySelectorAll('li');
|
|
133
|
-
expect(items.length).toBe(3);
|
|
134
|
-
expect(items[0].textContent).toBe('a');
|
|
135
|
-
expect(items[1].textContent).toBe('b');
|
|
136
|
-
expect(items[2].textContent).toBe('c');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it(
|
|
140
|
-
'does not crash when async component with tracked state is used inside try/pending',
|
|
141
|
-
async () => {
|
|
142
|
-
component App() {
|
|
143
|
-
let query = track('');
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
<FilteredList {query} />
|
|
147
|
-
} pending {
|
|
148
|
-
<p>{'loading...'}</p>
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
component FilteredList({ query }: { query: any }) {
|
|
153
|
-
let items = await Promise.resolve(['apple', 'banana', 'cherry']);
|
|
154
|
-
let list = RippleArray.from(items);
|
|
155
|
-
let &[filtered] = track(() => list.filter((item: string) => item.includes(query.value)));
|
|
156
|
-
|
|
157
|
-
<ul>
|
|
158
|
-
for (let item of filtered) {
|
|
159
|
-
<li>{item}</li>
|
|
160
|
-
}
|
|
161
|
-
</ul>
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
render(App);
|
|
165
|
-
|
|
166
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
167
|
-
flushSync();
|
|
168
|
-
|
|
169
|
-
const listItems = container.querySelectorAll('li');
|
|
170
|
-
expect(listItems.length).toBe(3);
|
|
171
|
-
},
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
it('if test condition does not become async within try/pending', async () => {
|
|
175
|
-
component App() {
|
|
176
|
-
try {
|
|
177
|
-
let items = await Promise.resolve(['apple', 'banana', 'cherry']);
|
|
178
|
-
|
|
179
|
-
if (items.includes('not-in-list')) {
|
|
180
|
-
<p>{'not-in-list is in the list!'}</p>
|
|
181
|
-
} else {
|
|
182
|
-
<p>{'not-in-list is not in the list.'}</p>
|
|
183
|
-
}
|
|
184
|
-
} pending {
|
|
185
|
-
<p>{'loading...'}</p>
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
render(App);
|
|
190
|
-
|
|
191
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
192
|
-
flushSync();
|
|
193
|
-
|
|
194
|
-
expect(container.innerHTML).toContain('not-in-list is not in the list.');
|
|
195
|
-
});
|
|
196
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { renderToStream } from 'ripple/server';
|
|
2
|
-
|
|
3
|
-
test('renderToStream renders a simple component', async ({ expect }) => {
|
|
4
|
-
component Basic() {
|
|
5
|
-
<div>{'Hello, streaming SSR!'}</div>
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const stream = renderToStream(Basic);
|
|
9
|
-
|
|
10
|
-
let result = '';
|
|
11
|
-
await new Promise((resolve) => {
|
|
12
|
-
stream.on('data', (chunk) => {
|
|
13
|
-
result += chunk.toString();
|
|
14
|
-
});
|
|
15
|
-
stream.on('end', resolve);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
expect(result).toBe('<div>Hello, streaming SSR!</div>');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test('renderToStream handles async components', async ({ expect }) => {
|
|
22
|
-
component AsyncComponent() {
|
|
23
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
24
|
-
<p>{'Async content loaded.'}</p>
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const stream = renderToStream(AsyncComponent);
|
|
28
|
-
|
|
29
|
-
let result = '';
|
|
30
|
-
await new Promise((resolve) => {
|
|
31
|
-
stream.on('data', (chunk) => {
|
|
32
|
-
result += chunk.toString();
|
|
33
|
-
});
|
|
34
|
-
stream.on('end', resolve);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
expect(result).toBe('<p>Async content loaded.</p>');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('renderToStream handles await blocks with pending state', async ({ expect }) => {
|
|
41
|
-
component AwaitComponent() {
|
|
42
|
-
let data = 'initial';
|
|
43
|
-
await new Promise((resolve) => setTimeout(() => {
|
|
44
|
-
data = 'resolved';
|
|
45
|
-
resolve('');
|
|
46
|
-
}, 20));
|
|
47
|
-
try {
|
|
48
|
-
<div>
|
|
49
|
-
{'Data: '}
|
|
50
|
-
{data}
|
|
51
|
-
</div>
|
|
52
|
-
} pending {
|
|
53
|
-
<div>{'Loading...'}</div>
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const stream = renderToStream(AwaitComponent);
|
|
58
|
-
|
|
59
|
-
let result = '';
|
|
60
|
-
await new Promise((resolve) => {
|
|
61
|
-
stream.on('data', (chunk) => {
|
|
62
|
-
result += chunk.toString();
|
|
63
|
-
});
|
|
64
|
-
stream.on('end', resolve);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
expect(result).toBe('<!--[--><div>Loading...</div><div>Data: resolved</div><!--]-->');
|
|
68
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
describe('try block with catch and pending (server)', () => {
|
|
2
|
-
it('catch block works when component throws before await with pending block', async () => {
|
|
3
|
-
component ThrowingChild() {
|
|
4
|
-
throw new Error('sync error');
|
|
5
|
-
let data = await Promise.resolve('hello');
|
|
6
|
-
<p>{data}</p>
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
component App() {
|
|
10
|
-
try {
|
|
11
|
-
<ThrowingChild />
|
|
12
|
-
} pending {
|
|
13
|
-
<p>{'loading...'}</p>
|
|
14
|
-
} catch (err) {
|
|
15
|
-
<p>{'caught error'}</p>
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const { body } = await render(App);
|
|
20
|
-
expect(body).toContain('caught error');
|
|
21
|
-
expect(body).not.toContain('loading...');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('catch block works when component throws after await with pending block', async () => {
|
|
25
|
-
component ThrowingAfterAwait() {
|
|
26
|
-
let data = await Promise.resolve('hello');
|
|
27
|
-
throw new Error('error after await');
|
|
28
|
-
<p>{data}</p>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
component App() {
|
|
32
|
-
try {
|
|
33
|
-
<ThrowingAfterAwait />
|
|
34
|
-
} pending {
|
|
35
|
-
<p>{'loading...'}</p>
|
|
36
|
-
} catch (err) {
|
|
37
|
-
<p>{'caught error'}</p>
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const { body } = await render(App);
|
|
42
|
-
expect(body).toContain('caught error');
|
|
43
|
-
expect(body).not.toContain('loading...');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('catch block works with try/catch/pending when async body rejects', async () => {
|
|
47
|
-
component App() {
|
|
48
|
-
try {
|
|
49
|
-
let data = await Promise.reject(new Error('rejected'));
|
|
50
|
-
<p>{data}</p>
|
|
51
|
-
} pending {
|
|
52
|
-
<p>{'loading...'}</p>
|
|
53
|
-
} catch (err) {
|
|
54
|
-
<p>{'caught rejection'}</p>
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const { body } = await render(App);
|
|
59
|
-
expect(body).toContain('caught rejection');
|
|
60
|
-
expect(body).not.toContain('loading...');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('removes pending content for nested try/pending blocks', async () => {
|
|
64
|
-
component App() {
|
|
65
|
-
try {
|
|
66
|
-
try {
|
|
67
|
-
let data = await Promise.resolve('resolved');
|
|
68
|
-
<p>{data}</p>
|
|
69
|
-
} pending {
|
|
70
|
-
<p>{'inner loading...'}</p>
|
|
71
|
-
}
|
|
72
|
-
} pending {
|
|
73
|
-
<p>{'outer loading...'}</p>
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const { body } = await render(App);
|
|
78
|
-
expect(body).toContain('resolved');
|
|
79
|
-
expect(body).not.toContain('outer loading...');
|
|
80
|
-
expect(body).not.toContain('inner loading...');
|
|
81
|
-
});
|
|
82
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/tests/client/compiler/{compiler.assignments.test.ripple → compiler.assignments.test.rsrx}
RENAMED
|
File without changes
|
/package/tests/client/compiler/{compiler.attributes.test.ripple → compiler.attributes.test.rsrx}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/tests/client/compiler/{compiler.typescript.test.ripple → compiler.typescript.test.rsrx}
RENAMED
|
File without changes
|
|
File without changes
|
/package/tests/client/composite/{composite.generics.test.ripple → composite.generics.test.rsrx}
RENAMED
|
File without changes
|
|
File without changes
|
/package/tests/client/composite/{composite.reactivity.test.ripple → composite.reactivity.test.rsrx}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/tests/client/css/{global-additional-cases.test.ripple → global-additional-cases.test.rsrx}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/tests/client/css/{global-complex-nesting.test.ripple → global-complex-nesting.test.rsrx}
RENAMED
|
File without changes
|
|
File without changes
|