ripple 0.3.128 → 0.4.1
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 +294 -0
- package/package.json +11 -4
- package/src/jsx-runtime.d.ts +12 -2
- package/src/runtime/element.js +23 -6
- package/src/runtime/index-client.js +81 -41
- package/src/runtime/index-server.js +1 -0
- package/src/runtime/internal/client/blocks.js +173 -91
- package/src/runtime/internal/client/component.js +4 -30
- package/src/runtime/internal/client/composite.js +12 -13
- package/src/runtime/internal/client/constants.js +9 -0
- package/src/runtime/internal/client/context.js +12 -19
- package/src/runtime/internal/client/css.js +7 -9
- package/src/runtime/internal/client/events.js +178 -90
- package/src/runtime/internal/client/expression.js +45 -25
- package/src/runtime/internal/client/for.js +409 -129
- package/src/runtime/internal/client/head.js +10 -5
- package/src/runtime/internal/client/html.js +1 -0
- package/src/runtime/internal/client/hydration.js +152 -10
- package/src/runtime/internal/client/if.js +239 -50
- package/src/runtime/internal/client/index.js +4 -12
- package/src/runtime/internal/client/operations.js +40 -61
- package/src/runtime/internal/client/portal.js +163 -85
- package/src/runtime/internal/client/render.js +44 -43
- package/src/runtime/internal/client/rpc.js +3 -3
- package/src/runtime/internal/client/runtime.js +592 -433
- package/src/runtime/internal/client/selector.js +5 -2
- package/src/runtime/internal/client/template.js +133 -158
- package/src/runtime/internal/client/transport.js +38 -0
- package/src/runtime/internal/client/try.js +465 -426
- package/src/runtime/internal/client/types.d.ts +56 -23
- package/src/runtime/internal/client/utils.js +1 -3
- package/src/runtime/internal/server/dynamic.js +20 -21
- package/src/runtime/internal/server/index.js +329 -277
- package/src/runtime/internal/server/rpc.js +3 -2
- package/src/runtime/internal/server/transport.js +16 -0
- package/src/runtime/transport.js +21 -0
- package/tests/client/__snapshots__/for.test.tsrx.snap +0 -5
- package/tests/client/array/array.copy-within.test.tsrx +12 -12
- package/tests/client/array/array.derived.test.tsrx +46 -46
- package/tests/client/array/array.iteration.test.tsrx +10 -10
- package/tests/client/array/array.mutations.test.tsrx +20 -20
- package/tests/client/array/array.static.test.tsrx +11 -11
- package/tests/client/array/array.to-methods.test.tsrx +6 -6
- package/tests/client/async-suspend.test.tsrx +74 -75
- package/tests/client/basic/basic.attributes.test.tsrx +123 -83
- package/tests/client/basic/basic.collections.test.tsrx +12 -12
- package/tests/client/basic/basic.components.test.tsrx +99 -25
- package/tests/client/basic/basic.errors.test.tsrx +18 -18
- package/tests/client/basic/basic.events.test.tsrx +42 -42
- package/tests/client/basic/basic.get-set.test.tsrx +8 -8
- package/tests/client/basic/basic.reactivity.test.tsrx +73 -38
- package/tests/client/basic/basic.rendering.test.tsrx +25 -25
- package/tests/client/basic/basic.utilities.test.tsrx +3 -3
- package/tests/client/boundaries.test.tsrx +8 -8
- package/tests/client/boxed-locals.test.tsrx +452 -0
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +6 -6
- package/tests/client/compiler/compiler.assignments.test.tsrx +9 -73
- package/tests/client/compiler/compiler.attributes.test.tsrx +23 -23
- package/tests/client/compiler/compiler.basic.test.tsrx +15 -60
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +18 -43
- package/tests/client/compiler/compiler.try-in-function.test.tsrx +2 -2
- package/tests/client/composite/__snapshots__/composite.render.test.tsrx.snap +0 -1
- package/tests/client/composite/composite.dynamic-components.test.tsrx +77 -6
- package/tests/client/composite/composite.props.test.tsrx +149 -12
- package/tests/client/composite/composite.reactivity.test.tsrx +51 -52
- package/tests/client/composite/composite.render.test.tsrx +26 -9
- package/tests/client/css/scoped-styles.test.tsrx +8 -8
- package/tests/client/date.test.tsrx +42 -42
- package/tests/client/derived-release.test.tsrx +370 -0
- package/tests/client/dynamic-elements.test.tsrx +45 -45
- package/tests/client/events.test.tsrx +97 -71
- package/tests/client/for-selector.test.tsrx +14 -14
- package/tests/client/for-single-root-items.test.tsrx +118 -3
- package/tests/client/for.test.tsrx +332 -31
- package/tests/client/head.test.tsrx +19 -19
- package/tests/client/html.test.tsrx +11 -9
- package/tests/client/if-block.test.tsrx +80 -0
- package/tests/client/map.test.tsrx +16 -16
- package/tests/client/media-query.test.tsrx +7 -7
- package/tests/client/portal.test.tsrx +224 -26
- package/tests/client/primitive-text.test.tsrx +50 -0
- package/tests/client/prop-kind-switch.test.tsrx +141 -0
- package/tests/client/ref.test.tsrx +17 -17
- package/tests/client/return.test.tsrx +26 -3
- package/tests/client/root-boundary.test.tsrx +42 -0
- package/tests/client/scoped-flush.test.tsrx +8 -8
- package/tests/client/set.test.tsrx +6 -6
- package/tests/client/svg.test.tsrx +11 -11
- package/tests/client/switch.test.tsrx +44 -44
- package/tests/client/track-async-hydration.test.tsrx +4 -4
- package/tests/client/try.test.tsrx +151 -83
- package/tests/client/tsx.test.tsrx +43 -40
- package/tests/client/url/url.derived.test.tsrx +6 -6
- package/tests/client/url-search-params/url-search-params.derived.test.tsrx +8 -8
- package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +10 -10
- package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +10 -10
- package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +18 -18
- package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +2 -2
- package/tests/hydration/basic.test.js +74 -1
- package/tests/hydration/build-components.js +22 -1
- package/tests/hydration/compiled/client/basic.js +1142 -713
- package/tests/hydration/compiled/client/composite.js +151 -100
- package/tests/hydration/compiled/client/events.js +184 -171
- package/tests/hydration/compiled/client/for.js +1472 -1132
- package/tests/hydration/compiled/client/fragment-cursor.js +2341 -0
- package/tests/hydration/compiled/client/head.js +254 -201
- package/tests/hydration/compiled/client/hmr.js +54 -41
- package/tests/hydration/compiled/client/html-in-template.js +54 -39
- package/tests/hydration/compiled/client/html.js +1633 -1263
- package/tests/hydration/compiled/client/if-children.js +420 -270
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +363 -317
- package/tests/hydration/compiled/client/if.js +229 -219
- package/tests/hydration/compiled/client/mixed-control-flow.js +420 -404
- package/tests/hydration/compiled/client/nested-control-flow.js +1326 -1277
- package/tests/hydration/compiled/client/portal.js +87 -95
- package/tests/hydration/compiled/client/reactivity.js +135 -101
- package/tests/hydration/compiled/client/return.js +28 -19
- package/tests/hydration/compiled/client/streaming.js +390 -306
- package/tests/hydration/compiled/client/switch.js +258 -266
- package/tests/hydration/compiled/client/track-async-serialization.js +448 -280
- package/tests/hydration/compiled/client/try.js +173 -129
- package/tests/hydration/compiled/client/typed-text.js +71 -0
- package/tests/hydration/compiled/fixtures/money.js +10 -0
- package/tests/hydration/compiled/server/basic.js +123 -6
- package/tests/hydration/compiled/server/composite.js +16 -30
- package/tests/hydration/compiled/server/events.js +66 -18
- package/tests/hydration/compiled/server/for.js +117 -41
- package/tests/hydration/compiled/server/fragment-cursor.js +1687 -0
- package/tests/hydration/compiled/server/head.js +20 -20
- package/tests/hydration/compiled/server/hmr.js +2 -2
- package/tests/hydration/compiled/server/html.js +70 -238
- package/tests/hydration/compiled/server/if-children.js +107 -15
- package/tests/hydration/compiled/server/if.js +11 -15
- package/tests/hydration/compiled/server/mixed-control-flow.js +8 -8
- package/tests/hydration/compiled/server/portal.js +14 -22
- package/tests/hydration/compiled/server/reactivity.js +14 -14
- package/tests/hydration/compiled/server/streaming.js +19 -19
- package/tests/hydration/compiled/server/switch.js +8 -8
- package/tests/hydration/compiled/server/track-async-serialization.js +150 -23
- package/tests/hydration/compiled/server/try.js +8 -8
- package/tests/hydration/compiled/server/typed-text.js +17 -0
- package/tests/hydration/components/basic.tsrx +44 -8
- package/tests/hydration/components/composite.tsrx +2 -2
- package/tests/hydration/components/events.tsrx +25 -25
- package/tests/hydration/components/for.tsrx +115 -67
- package/tests/hydration/components/fragment-cursor.tsrx +650 -0
- package/tests/hydration/components/head.tsrx +16 -16
- package/tests/hydration/components/hmr.tsrx +2 -2
- package/tests/hydration/components/html.tsrx +7 -7
- package/tests/hydration/components/if-children.tsrx +72 -25
- package/tests/hydration/components/if.tsrx +18 -18
- package/tests/hydration/components/mixed-control-flow.tsrx +11 -11
- package/tests/hydration/components/portal.tsrx +3 -3
- package/tests/hydration/components/reactivity.tsrx +16 -16
- package/tests/hydration/components/streaming.tsrx +20 -20
- package/tests/hydration/components/switch.tsrx +20 -20
- package/tests/hydration/components/text-types.ts +4 -0
- package/tests/hydration/components/track-async-serialization.tsrx +80 -26
- package/tests/hydration/components/try.tsrx +8 -8
- package/tests/hydration/components/typed-text.tsrx +22 -0
- package/tests/hydration/fixtures/money.js +10 -0
- package/tests/hydration/for.test.js +36 -0
- package/tests/hydration/fragment-cursor.test.js +111 -0
- package/tests/hydration/if-children.test.js +24 -0
- package/tests/hydration/streaming.test.js +19 -0
- package/tests/hydration/track-async-serialization.test.js +87 -2
- package/tests/hydration/tsconfig.text-types.json +10 -0
- package/tests/hydration/typed-text.test.js +34 -0
- package/tests/server/await.test.tsrx +3 -3
- package/tests/server/basic.attributes.test.tsrx +70 -42
- package/tests/server/basic.components.test.tsrx +64 -8
- package/tests/server/basic.test.tsrx +34 -32
- package/tests/server/compiler.test.tsrx +1 -70
- package/tests/server/composite.props.test.tsrx +6 -6
- package/tests/server/dynamic-elements.test.tsrx +31 -8
- package/tests/server/for.test.tsrx +94 -0
- package/tests/server/head.test.tsrx +11 -11
- package/tests/server/scoped-styles.test.tsrx +2 -2
- package/tests/server/streaming-ssr.test.tsrx +48 -43
- package/tests/server/track-async-serialization.test.tsrx +204 -21
- package/tests/server/try.test.tsrx +48 -48
- package/tests/types/transport.ts +40 -0
- package/tests/utils/jsx-runtime-types.test.js +67 -2
- package/tests/utils/tracked-types.test.js +117 -0
- package/tests/utils/vite-plugin-config.test.js +23 -0
- package/types/index.d.ts +49 -9
- package/types/server.d.ts +3 -0
- package/types/transport.d.ts +15 -0
- package/src/runtime/internal/client/switch.js +0 -113
- package/src/utils/errors.js +0 -13
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +0 -12
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +0 -46
- package/tests/client/lazy-array.test.tsrx +0 -31
- package/tests/client/lazy-destructuring.test.tsrx +0 -438
- package/tests/client/tracked-index-access.test.tsrx +0 -113
- package/tests/server/lazy-destructuring.test.tsrx +0 -595
- package/tests/server/tracked-index-access.test.tsrx +0 -76
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} source
|
|
6
|
+
*/
|
|
7
|
+
function create_service(source) {
|
|
8
|
+
const root = process.cwd();
|
|
9
|
+
const file_name = `${root}/packages/ripple/tracked-types-test.tsx`;
|
|
10
|
+
const options = {
|
|
11
|
+
strict: true,
|
|
12
|
+
target: ts.ScriptTarget.ESNext,
|
|
13
|
+
module: ts.ModuleKind.ESNext,
|
|
14
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
15
|
+
jsx: ts.JsxEmit.Preserve,
|
|
16
|
+
jsxImportSource: 'ripple',
|
|
17
|
+
skipLibCheck: true,
|
|
18
|
+
types: [],
|
|
19
|
+
paths: {
|
|
20
|
+
'#public': [`${root}/packages/ripple/types/index.d.ts`],
|
|
21
|
+
ripple: [`${root}/packages/ripple/types/index.d.ts`],
|
|
22
|
+
'ripple/jsx-runtime': [`${root}/packages/ripple/src/jsx-runtime.d.ts`],
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
const service = ts.createLanguageService({
|
|
26
|
+
getCompilationSettings: () => options,
|
|
27
|
+
getScriptFileNames: () => [file_name],
|
|
28
|
+
getScriptVersion: () => '0',
|
|
29
|
+
getScriptSnapshot: (name) => {
|
|
30
|
+
const text = name === file_name ? source : ts.sys.readFile(name);
|
|
31
|
+
return text === undefined ? undefined : ts.ScriptSnapshot.fromString(text);
|
|
32
|
+
},
|
|
33
|
+
getCurrentDirectory: () => root,
|
|
34
|
+
getDefaultLibFileName: ts.getDefaultLibFilePath,
|
|
35
|
+
realpath: ts.sys.realpath,
|
|
36
|
+
directoryExists: ts.sys.directoryExists,
|
|
37
|
+
getDirectories: ts.sys.getDirectories,
|
|
38
|
+
fileExists: (name) => name === file_name || ts.sys.fileExists(name),
|
|
39
|
+
readFile: (name) => (name === file_name ? source : ts.sys.readFile(name)),
|
|
40
|
+
});
|
|
41
|
+
return { service, file_name };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe('Ripple tracked types', () => {
|
|
45
|
+
it('shows public names in value property hovers', () => {
|
|
46
|
+
const source = `
|
|
47
|
+
import { track } from 'ripple';
|
|
48
|
+
const count = track(0);
|
|
49
|
+
const derived = track(() => count.value * 2);
|
|
50
|
+
const writable = track(() => count.value, undefined, true);
|
|
51
|
+
count.value;
|
|
52
|
+
derived.value;
|
|
53
|
+
writable.value;
|
|
54
|
+
`;
|
|
55
|
+
const { service, file_name } = create_service(source);
|
|
56
|
+
try {
|
|
57
|
+
expect(service.getSemanticDiagnostics(file_name)).toEqual([]);
|
|
58
|
+
for (const [name, type] of [
|
|
59
|
+
['count', 'Tracked'],
|
|
60
|
+
['derived', 'Derived'],
|
|
61
|
+
['writable', 'WritableDerived'],
|
|
62
|
+
]) {
|
|
63
|
+
const info = service.getQuickInfoAtPosition(
|
|
64
|
+
file_name,
|
|
65
|
+
source.lastIndexOf(`${name}.value`) + name.length + 1,
|
|
66
|
+
);
|
|
67
|
+
expect(ts.displayPartsToString(info?.displayParts)).toBe(
|
|
68
|
+
`(property) ${type}<number>.value: number`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
} finally {
|
|
72
|
+
service.dispose();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('preserves writes, read-only views, and tracked component props', () => {
|
|
77
|
+
const source = `
|
|
78
|
+
import { track, type Component, type Derived, type Tracked } from 'ripple';
|
|
79
|
+
const count = track(0);
|
|
80
|
+
count.value = 1;
|
|
81
|
+
const tracked: Tracked<number> = track(count);
|
|
82
|
+
const derived: Derived<number> = tracked;
|
|
83
|
+
const view = count.readOnly();
|
|
84
|
+
// @ts-expect-error Read-only views reject writes.
|
|
85
|
+
view.value = 2;
|
|
86
|
+
const computed = track(() => count.value * 2);
|
|
87
|
+
// @ts-expect-error Computed values reject writes.
|
|
88
|
+
computed.value = 2;
|
|
89
|
+
const writable = track(() => count.value, undefined, true);
|
|
90
|
+
writable.value = 2;
|
|
91
|
+
const with_setter = track(() => count.value, undefined, (next) => next);
|
|
92
|
+
with_setter.value = 3;
|
|
93
|
+
// @ts-expect-error Tracked values preserve their value type.
|
|
94
|
+
count.value = 'wrong';
|
|
95
|
+
// @ts-expect-error Non-component tracked values are not callable.
|
|
96
|
+
count({});
|
|
97
|
+
declare const TrackedComponent: Tracked<Component<{ name: string }>>;
|
|
98
|
+
declare const DerivedComponent: Derived<Component<{ name: string }>>;
|
|
99
|
+
const valid = <TrackedComponent name="Ripple" />;
|
|
100
|
+
const valid_derived = <DerivedComponent name="Ripple" />;
|
|
101
|
+
// @ts-expect-error Tracked components preserve required props.
|
|
102
|
+
const missing = <TrackedComponent />;
|
|
103
|
+
// @ts-expect-error Derived components preserve prop types.
|
|
104
|
+
const invalid = <DerivedComponent name={123} />;
|
|
105
|
+
`;
|
|
106
|
+
const { service, file_name } = create_service(source);
|
|
107
|
+
try {
|
|
108
|
+
expect(
|
|
109
|
+
service
|
|
110
|
+
.getSemanticDiagnostics(file_name)
|
|
111
|
+
.map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')),
|
|
112
|
+
).toEqual([]);
|
|
113
|
+
} finally {
|
|
114
|
+
service.dispose();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -18,6 +18,7 @@ describe('vite-plugin-ripple config resolution', () => {
|
|
|
18
18
|
expect(config.platform.env).toEqual({});
|
|
19
19
|
expect(config.server.trustProxy).toBe(false);
|
|
20
20
|
expect(config.rootBoundary).toEqual({});
|
|
21
|
+
expect(config.transport).toEqual({});
|
|
21
22
|
expect(config.build.outDir).toBe('dist');
|
|
22
23
|
});
|
|
23
24
|
|
|
@@ -77,4 +78,26 @@ describe('vite-plugin-ripple config resolution', () => {
|
|
|
77
78
|
}),
|
|
78
79
|
).toThrow('router.routes must be an array');
|
|
79
80
|
});
|
|
81
|
+
|
|
82
|
+
it('preserves transport functions when resolving a config again', () => {
|
|
83
|
+
const transport = {
|
|
84
|
+
Money: { encode: () => false, decode: (data) => data },
|
|
85
|
+
};
|
|
86
|
+
const config = resolveRippleConfig({ transport });
|
|
87
|
+
expect(config.transport).toBe(transport);
|
|
88
|
+
expect(resolveRippleConfig(config).transport).toBe(transport);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it.each([null, false, [], 'Money', () => {}])('rejects invalid transport %s', (transport) => {
|
|
92
|
+
expect(() => resolveRippleConfig({ transport })).toThrow('transport must be an object');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it.each([null, [], false, {}, { encode() {} }, { decode() {} }, { encode: true, decode() {} }])(
|
|
96
|
+
'rejects invalid transport handler %s',
|
|
97
|
+
(Money) => {
|
|
98
|
+
expect(() => resolveRippleConfig({ transport: { Money } })).toThrow(
|
|
99
|
+
'transport.Money must be an object with encode and decode functions',
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
);
|
|
80
103
|
});
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { ExtendedEventOptions } from '@tsrx/core/types';
|
|
2
|
+
export type { CSSProperties, JSX, Ripple } from '../src/jsx-runtime.js';
|
|
3
|
+
export { setTransport } from './transport.js';
|
|
4
|
+
export type { Transport, Transporter } from './transport.js';
|
|
2
5
|
export type { RefValue } from '@tsrx/core/runtime/ref';
|
|
3
6
|
export type { AddEventOptions, AddEventObject, ExtendedEventOptions } from '@tsrx/core/types';
|
|
4
7
|
|
|
@@ -61,12 +64,26 @@ export function Dynamic<T>(
|
|
|
61
64
|
|
|
62
65
|
export function mount(
|
|
63
66
|
component: Component,
|
|
64
|
-
options: {
|
|
67
|
+
options: {
|
|
68
|
+
target: HTMLElement;
|
|
69
|
+
props?: Record<string, any>;
|
|
70
|
+
/**
|
|
71
|
+
* The default `try`/`pending`/`catch` boundary the app renders under.
|
|
72
|
+
* `false` renders without one: `trackAsync()` must then sit inside a
|
|
73
|
+
* user `@try` block, and errors that escape one propagate out of the
|
|
74
|
+
* flush.
|
|
75
|
+
*/
|
|
76
|
+
rootBoundary?: RootBoundaryOptions | false;
|
|
77
|
+
},
|
|
65
78
|
): () => void;
|
|
66
79
|
|
|
67
80
|
export function hydrate(
|
|
68
81
|
component: Component,
|
|
69
|
-
options: {
|
|
82
|
+
options: {
|
|
83
|
+
target: HTMLElement;
|
|
84
|
+
props?: Record<string, any>;
|
|
85
|
+
rootBoundary?: RootBoundaryOptions | false;
|
|
86
|
+
},
|
|
70
87
|
): () => void;
|
|
71
88
|
|
|
72
89
|
export interface RootBoundaryOptions {
|
|
@@ -204,18 +221,36 @@ export const TRACKED_UPDATED: unique symbol;
|
|
|
204
221
|
export const SUSPENSE_PENDING: unique symbol;
|
|
205
222
|
export const SUSPENSE_REJECTED: unique symbol;
|
|
206
223
|
|
|
207
|
-
//
|
|
208
|
-
interface
|
|
224
|
+
// A tracked value: `track(0)`. Read and write it through `.value`.
|
|
225
|
+
export interface Tracked<V> extends TrackedCallable<V> {
|
|
209
226
|
'#v': V;
|
|
210
227
|
value: V;
|
|
228
|
+
/**
|
|
229
|
+
* A read-only view of this value: a `Derived<V>` that follows it, for a
|
|
230
|
+
* receiver that should read but not write it. Equivalent to
|
|
231
|
+
* `track(() => tracked.value)`.
|
|
232
|
+
*/
|
|
233
|
+
readOnly(): Derived<V>;
|
|
211
234
|
}
|
|
212
235
|
// Augment Tracked to be callable when V is a Component.
|
|
213
236
|
// This allows tracked component values to continue flowing through JSX checks.
|
|
214
237
|
interface TrackedCallable<V> {
|
|
215
238
|
(props: V extends Component<infer P> ? P : never): V extends Component ? void : never;
|
|
216
239
|
}
|
|
217
|
-
//
|
|
218
|
-
|
|
240
|
+
// A computed value: `track(() => ...)`. Its `value` is read-only unless the
|
|
241
|
+
// call opted into writes (see `WritableDerived`). A `Tracked` satisfies it.
|
|
242
|
+
export interface Derived<V> extends TrackedCallable<V> {
|
|
243
|
+
'#v': V;
|
|
244
|
+
readonly value: V;
|
|
245
|
+
/** A derived is already read-only: returns itself. */
|
|
246
|
+
readOnly(): Derived<V>;
|
|
247
|
+
}
|
|
248
|
+
// A computed value created with a setter (`track(fn, get, set)`) or with
|
|
249
|
+
// `true` in the setter position: writes land as a temporary value until the
|
|
250
|
+
// next recompute.
|
|
251
|
+
export interface WritableDerived<V> extends Tracked<V> {
|
|
252
|
+
value: V;
|
|
253
|
+
}
|
|
219
254
|
|
|
220
255
|
// Helper type to infer component type from a function that returns a component
|
|
221
256
|
// If T is a function returning a Component, extract the Component type itself, not the return type (void)
|
|
@@ -235,12 +270,17 @@ type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
|
|
235
270
|
|
|
236
271
|
// Overload for tracked values - returns the original tracked value type
|
|
237
272
|
export function track<V>(value: Tracked<V>): Tracked<V>;
|
|
238
|
-
// Overload for function values -
|
|
273
|
+
// Overload for function values - a read-only derived
|
|
239
274
|
export function track<V>(
|
|
240
275
|
value: () => V,
|
|
241
276
|
get?: (v: InferComponent<V>) => InferComponent<V>,
|
|
242
|
-
|
|
243
|
-
)
|
|
277
|
+
): Derived<InferComponent<V>>;
|
|
278
|
+
// Overload for function values with a setter (or `true`) - a writable derived
|
|
279
|
+
export function track<V>(
|
|
280
|
+
value: () => V,
|
|
281
|
+
get: ((v: InferComponent<V>) => InferComponent<V>) | undefined,
|
|
282
|
+
set: ((next: InferComponent<V>, prev: InferComponent<V>) => InferComponent<V>) | true,
|
|
283
|
+
): WritableDerived<InferComponent<V>>;
|
|
244
284
|
// Overload for non-function values
|
|
245
285
|
export function track<V>(value?: V, get?: (v: V) => V, set?: (next: V, prev: V) => V): Tracked<V>;
|
|
246
286
|
|
package/types/server.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Component, RootBoundaryOptions } from '#public';
|
|
|
2
2
|
|
|
3
3
|
// Re-export runtime types for server-compiled components
|
|
4
4
|
export {
|
|
5
|
+
setTransport,
|
|
5
6
|
track,
|
|
6
7
|
untrack,
|
|
7
8
|
flushSync,
|
|
@@ -16,6 +17,8 @@ export {
|
|
|
16
17
|
RippleURLSearchParams,
|
|
17
18
|
} from './index.js';
|
|
18
19
|
|
|
20
|
+
export type { Transport, Transporter } from './index.js';
|
|
21
|
+
|
|
19
22
|
export interface RenderResult {
|
|
20
23
|
head: string;
|
|
21
24
|
body: string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** A custom type's synchronous serialization pair for hydration and RPC. */
|
|
2
|
+
export interface Transporter<T = any, Encoded = any> {
|
|
3
|
+
/** Return truthy serializable data, or false/undefined for values not handled. */
|
|
4
|
+
encode: (value: unknown) => Encoded | false | undefined;
|
|
5
|
+
decode: (data: Encoded) => T;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type Transport = Record<string, Transporter>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Register the app's transport once, before rendering, hydration, mounting or
|
|
12
|
+
* RPC. The Vite plugin calls this automatically for ripple.config.ts transport.
|
|
13
|
+
* Omit the argument or pass an empty object to restore built-in serialization.
|
|
14
|
+
*/
|
|
15
|
+
export function setTransport(transport?: Transport): void;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/** @import { Block } from '#client' */
|
|
2
|
-
|
|
3
|
-
import { branch, destroy_block, get_first_node, get_last_node, render } from './blocks.js';
|
|
4
|
-
import { SWITCH_BLOCK } from './constants.js';
|
|
5
|
-
import { hydrate_next, hydrate_node, hydrating } from './hydration.js';
|
|
6
|
-
import { next_sibling } from './operations.js';
|
|
7
|
-
import { append } from './template.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Moves a block's DOM nodes to before an anchor node
|
|
11
|
-
* @param {Block} block
|
|
12
|
-
* @param {ChildNode} anchor
|
|
13
|
-
* @returns {void}
|
|
14
|
-
*/
|
|
15
|
-
function move(block, anchor) {
|
|
16
|
-
// Fast path: a normal case records its own range. Only an optimized single
|
|
17
|
-
// control-flow / component root case (DOM rendered through a descendant
|
|
18
|
-
// block, `s.start` null) needs the descent via `get_first_node`/`get_last_node`.
|
|
19
|
-
var s = block.s;
|
|
20
|
-
var node = s !== null ? s.start : null;
|
|
21
|
-
var end;
|
|
22
|
-
|
|
23
|
-
if (node === null) {
|
|
24
|
-
node = get_first_node(block);
|
|
25
|
-
if (node === null) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
end = get_last_node(block);
|
|
29
|
-
} else {
|
|
30
|
-
end = s.end;
|
|
31
|
-
}
|
|
32
|
-
/** @type {Node | null} */
|
|
33
|
-
var sibling;
|
|
34
|
-
|
|
35
|
-
while (node !== null) {
|
|
36
|
-
if (node === end) {
|
|
37
|
-
append(anchor, node);
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
sibling = next_sibling(node);
|
|
41
|
-
append(anchor, node);
|
|
42
|
-
node = sibling;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @param {ChildNode} anchor
|
|
48
|
-
* @param {() => ((anchor: ChildNode) => void)[] | null} fn
|
|
49
|
-
* @param {boolean} [root_controlled] When true the block renders before the
|
|
50
|
-
* component's `__anchor` (no `<!>` wrapper); during hydration the SSR boundary
|
|
51
|
-
* marker is handed to `append()` afterwards for the context-aware cursor
|
|
52
|
-
* advance the eliminated wrapper used to perform.
|
|
53
|
-
* @returns {void}
|
|
54
|
-
*/
|
|
55
|
-
export function switch_block(anchor, fn, root_controlled) {
|
|
56
|
-
/** @type {Node | undefined} */
|
|
57
|
-
var boundary;
|
|
58
|
-
|
|
59
|
-
if (hydrating) {
|
|
60
|
-
if (root_controlled) {
|
|
61
|
-
boundary = /** @type {Node} */ (hydrate_node);
|
|
62
|
-
}
|
|
63
|
-
hydrate_next();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** @type {((anchor: ChildNode) => void)[]} */
|
|
67
|
-
var prev = [];
|
|
68
|
-
/** @type {Map<(anchor: ChildNode) => void, Block>} */
|
|
69
|
-
var blocks = new Map();
|
|
70
|
-
|
|
71
|
-
render(
|
|
72
|
-
() => {
|
|
73
|
-
var funcs = fn();
|
|
74
|
-
let same = prev.length === funcs?.length || false;
|
|
75
|
-
|
|
76
|
-
for (var i = 0; i < prev.length; i++) {
|
|
77
|
-
var p = prev[i];
|
|
78
|
-
|
|
79
|
-
if (!funcs || funcs.indexOf(p) === -1) {
|
|
80
|
-
same = false;
|
|
81
|
-
destroy_block(/** @type {Block} */ (blocks.get(p)));
|
|
82
|
-
blocks.delete(p);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
prev = funcs ?? [];
|
|
87
|
-
|
|
88
|
-
if (same || !funcs) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
for (var i = 0; i < funcs.length; i++) {
|
|
93
|
-
var n = funcs[i];
|
|
94
|
-
var b = blocks.get(n);
|
|
95
|
-
if (b) {
|
|
96
|
-
move(b, anchor);
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
blocks.set(
|
|
101
|
-
n,
|
|
102
|
-
branch(() => n(anchor)),
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
null,
|
|
107
|
-
SWITCH_BLOCK,
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
if (hydrating && root_controlled) {
|
|
111
|
-
append(anchor, /** @type {Node} */ (boundary));
|
|
112
|
-
}
|
|
113
|
-
}
|
package/src/utils/errors.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** @returns {never} */
|
|
2
|
-
export function throw_tracked_index_value_error() {
|
|
3
|
-
throw new Error(
|
|
4
|
-
'Do not access tracked values with [0]. Use .value or &[] lazy destructuring instead. Numeric tracked access leads to degraded performance.',
|
|
5
|
-
);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/** @returns {never} */
|
|
9
|
-
export function throw_tracked_index_reference_error() {
|
|
10
|
-
throw new Error(
|
|
11
|
-
'Do not access tracked values with [1]. Use the tracked value directly instead. Numeric tracked access leads to degraded performance.',
|
|
12
|
-
);
|
|
13
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`compiler > assignments > compiles tracked values in effect with assignment expression 1`] = `"state.count = lazy.value;"`;
|
|
4
|
-
|
|
5
|
-
exports[`compiler > assignments > compiles tracked values in effect with update expressions 1`] = `
|
|
6
|
-
"_$_.untrack(() => {
|
|
7
|
-
state.preIncrement = _$_.update_pre(lazy);
|
|
8
|
-
state.postIncrement = _$_.update(lazy);
|
|
9
|
-
state.preDecrement = _$_.update_pre(lazy, -1);
|
|
10
|
-
state.postDecrement = _$_.update(lazy, -1);
|
|
11
|
-
});"
|
|
12
|
-
`;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`compiler > typescript > compiles TSInstantiationExpression 1`] = `
|
|
4
|
-
"import * as _$_ from 'ripple/internal/client';
|
|
5
|
-
|
|
6
|
-
function makeBox(value) {
|
|
7
|
-
return { value };
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const makeStringBox = (makeBox);
|
|
11
|
-
const stringBox = makeStringBox('abc');
|
|
12
|
-
const ErrorMap = (Map);
|
|
13
|
-
const errorMap = new ErrorMap();"
|
|
14
|
-
`;
|
|
15
|
-
|
|
16
|
-
exports[`compiler > typescript > removes class TypeScript syntax from JS output 1`] = `
|
|
17
|
-
"import * as _$_ from 'ripple/internal/client';
|
|
18
|
-
|
|
19
|
-
class PrintEvent {
|
|
20
|
-
text;
|
|
21
|
-
|
|
22
|
-
constructor(text) {
|
|
23
|
-
this.text = text;
|
|
24
|
-
}
|
|
25
|
-
}"
|
|
26
|
-
`;
|
|
27
|
-
|
|
28
|
-
exports[`compiler > typescript > removes class extends type arguments from JS output 1`] = `
|
|
29
|
-
"import * as _$_ from 'ripple/internal/client';
|
|
30
|
-
|
|
31
|
-
class StringMap extends Map {
|
|
32
|
-
constructor() {
|
|
33
|
-
var __block = _$_.scope();
|
|
34
|
-
|
|
35
|
-
super();
|
|
36
|
-
}
|
|
37
|
-
}"
|
|
38
|
-
`;
|
|
39
|
-
|
|
40
|
-
exports[`compiler > typescript > removes type assertions from function parameters and leaves default values 1`] = `
|
|
41
|
-
"import * as _$_ from 'ripple/internal/client';
|
|
42
|
-
|
|
43
|
-
function getString(e = 'test') {
|
|
44
|
-
return e;
|
|
45
|
-
}"
|
|
46
|
-
`;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { flushSync, track } from 'ripple';
|
|
3
|
-
|
|
4
|
-
describe('lazy array destructuring', () => {
|
|
5
|
-
it('updates unknown lazy array bindings through tracked values', () => {
|
|
6
|
-
function Child({ tr: &[count, tr] }) @{
|
|
7
|
-
<button
|
|
8
|
-
onClick={() => {
|
|
9
|
-
count++;
|
|
10
|
-
tr[0]++;
|
|
11
|
-
}}
|
|
12
|
-
>{count}</button>
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function App() @{
|
|
16
|
-
let tracked = track(0);
|
|
17
|
-
<Child tr={tracked} />
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
render(App);
|
|
21
|
-
|
|
22
|
-
const button = container.querySelector('button');
|
|
23
|
-
expect(button?.textContent).toBe('0');
|
|
24
|
-
|
|
25
|
-
flushSync(() => {
|
|
26
|
-
button?.click();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
expect(button?.textContent).toBe('2');
|
|
30
|
-
});
|
|
31
|
-
});
|