ripple 0.3.126 → 0.3.128

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/package.json +2 -2
  3. package/src/jsx-runtime.d.ts +1128 -967
  4. package/src/runtime/internal/client/blocks.js +20 -24
  5. package/src/runtime/internal/client/constants.js +5 -5
  6. package/src/runtime/internal/client/expression.js +142 -101
  7. package/src/runtime/internal/client/for.js +217 -145
  8. package/src/runtime/internal/client/index.js +5 -1
  9. package/src/runtime/internal/client/operations.js +24 -10
  10. package/src/runtime/internal/client/render.js +22 -3
  11. package/src/runtime/internal/client/runtime.js +246 -182
  12. package/src/runtime/internal/client/selector.js +74 -0
  13. package/src/runtime/internal/client/types.d.ts +11 -0
  14. package/src/runtime/internal/server/index.js +2 -0
  15. package/tests/client/basic/basic.reactivity.test.tsrx +33 -0
  16. package/tests/client/for-selector.test.tsrx +221 -0
  17. package/tests/hydration/compiled/client/basic.js +44 -44
  18. package/tests/hydration/compiled/client/composite.js +3 -3
  19. package/tests/hydration/compiled/client/events.js +56 -29
  20. package/tests/hydration/compiled/client/for.js +76 -70
  21. package/tests/hydration/compiled/client/head.js +11 -8
  22. package/tests/hydration/compiled/client/hmr.js +4 -4
  23. package/tests/hydration/compiled/client/html-in-template.js +3 -3
  24. package/tests/hydration/compiled/client/html.js +141 -141
  25. package/tests/hydration/compiled/client/if-children.js +29 -29
  26. package/tests/hydration/compiled/client/if-fragment-controlflow.js +16 -16
  27. package/tests/hydration/compiled/client/if.js +10 -10
  28. package/tests/hydration/compiled/client/mixed-control-flow.js +26 -22
  29. package/tests/hydration/compiled/client/nested-control-flow.js +114 -92
  30. package/tests/hydration/compiled/client/portal.js +8 -8
  31. package/tests/hydration/compiled/client/reactivity.js +38 -15
  32. package/tests/hydration/compiled/client/streaming.js +16 -16
  33. package/tests/hydration/compiled/client/switch.js +4 -4
  34. package/tests/hydration/compiled/client/track-async-serialization.js +18 -18
  35. package/tests/hydration/compiled/client/try.js +8 -8
  36. package/tests/hydration/compiled/server/events.js +5 -53
  37. package/tests/hydration/compiled/server/for.js +2 -18
  38. package/tests/hydration/compiled/server/head.js +1 -9
  39. package/tests/hydration/compiled/server/html.js +1 -9
  40. package/tests/hydration/compiled/server/reactivity.js +2 -34
  41. package/tests/utils/jsx-runtime-types.test.js +106 -0
@@ -10,15 +10,7 @@ export function TrackedState() {
10
10
  _$_.regular_block(() => {
11
11
  let __out = '';
12
12
 
13
- __out += '<div class="count">';
14
-
15
- {
16
- _$_.output_push(__out);
17
- __out = '';
18
- _$_.render_expression(lazy.value);
19
- }
20
-
21
- __out += '</div>';
13
+ __out += '<div class="count">' + _$_.escape(lazy.value) + '</div>';
22
14
  _$_.output_push(__out);
23
15
  });
24
16
  });
@@ -90,31 +82,7 @@ export function MultipleTracked() {
90
82
  _$_.regular_block(() => {
91
83
  let __out = '';
92
84
 
93
- __out += '<div class="multiple-tracked"><div class="x">';
94
-
95
- {
96
- _$_.output_push(__out);
97
- __out = '';
98
- _$_.render_expression(lazy_4.value);
99
- }
100
-
101
- __out += '</div><div class="y">';
102
-
103
- {
104
- _$_.output_push(__out);
105
- __out = '';
106
- _$_.render_expression(lazy_5.value);
107
- }
108
-
109
- __out += '</div><div class="z">';
110
-
111
- {
112
- _$_.output_push(__out);
113
- __out = '';
114
- _$_.render_expression(lazy_6.value);
115
- }
116
-
117
- __out += '</div></div>';
85
+ __out += '<div class="multiple-tracked"><div class="x">' + _$_.escape(lazy_4.value) + '</div><div class="y">' + _$_.escape(lazy_5.value) + '</div><div class="z">' + _$_.escape(lazy_6.value) + '</div></div>';
118
86
  _$_.output_push(__out);
119
87
  });
120
88
  });
@@ -0,0 +1,106 @@
1
+ import ts from 'typescript';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ /**
5
+ * @param {string} source
6
+ * @param {boolean} automatic
7
+ */
8
+ function create_service(source, automatic) {
9
+ const root = process.cwd();
10
+ const file_name = `${root}/packages/ripple/jsx-types-test.tsx`;
11
+ const options = {
12
+ strict: true,
13
+ target: ts.ScriptTarget.ESNext,
14
+ module: ts.ModuleKind.ESNext,
15
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
16
+ jsx: ts.JsxEmit.Preserve,
17
+ ...(automatic ? { jsxImportSource: 'ripple' } : {}),
18
+ skipLibCheck: true,
19
+ types: [],
20
+ paths: {
21
+ '#public': [`${root}/packages/ripple/types/index.d.ts`],
22
+ ripple: [`${root}/packages/ripple/types/index.d.ts`],
23
+ 'ripple/jsx-runtime': [`${root}/packages/ripple/src/jsx-runtime.d.ts`],
24
+ },
25
+ };
26
+ const service = ts.createLanguageService({
27
+ getCompilationSettings: () => options,
28
+ getScriptFileNames: () => [file_name],
29
+ getScriptVersion: () => '0',
30
+ getScriptSnapshot: (name) => {
31
+ const text = name === file_name ? source : ts.sys.readFile(name);
32
+ return text === undefined ? undefined : ts.ScriptSnapshot.fromString(text);
33
+ },
34
+ getCurrentDirectory: () => root,
35
+ getDefaultLibFileName: ts.getDefaultLibFilePath,
36
+ realpath: ts.sys.realpath,
37
+ directoryExists: ts.sys.directoryExists,
38
+ getDirectories: ts.sys.getDirectories,
39
+ fileExists: (name) => name === file_name || ts.sys.fileExists(name),
40
+ readFile: (name) => (name === file_name ? source : ts.sys.readFile(name)),
41
+ });
42
+ return { service, file_name };
43
+ }
44
+
45
+ describe('Ripple JSX types', () => {
46
+ it.each([true, false])('qualifies HTML tag hovers (automatic runtime: %s)', (automatic) => {
47
+ const source = `
48
+ import 'ripple/jsx-runtime';
49
+ const paragraph = <p />;
50
+ const input = <input />;
51
+ `;
52
+ const { service, file_name } = create_service(source, automatic);
53
+ try {
54
+ expect(service.getSemanticDiagnostics(file_name)).toEqual([]);
55
+ for (const [tag, attributes, element] of [
56
+ ['p', 'HTMLAttributes', 'HTMLParagraphElement'],
57
+ ['input', 'InputHTMLAttributes', 'HTMLInputElement'],
58
+ ]) {
59
+ const info = service.getQuickInfoAtPosition(file_name, source.indexOf(`<${tag} `) + 1);
60
+ expect(ts.displayPartsToString(info?.displayParts)).toBe(
61
+ `(property) Ripple.JSX.IntrinsicElements.${tag}: Ripple.DetailedHTMLProps<Ripple.${attributes}<${element}>, ${element}>`,
62
+ );
63
+ }
64
+ } finally {
65
+ service.dispose();
66
+ }
67
+ });
68
+
69
+ it('preserves JSX imports, global types, native events, and DOM refs', () => {
70
+ const source = `
71
+ import type { ClassValue, JSX as RuntimeJSX, Ripple } from 'ripple/jsx-runtime';
72
+ import { createRefKey, type RefKey } from 'ripple';
73
+
74
+ const ref_key: RefKey = createRefKey();
75
+ const props: Ripple.DetailedHTMLProps<Ripple.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> = {
76
+ value: 'hello',
77
+ ref: (node) => { node.select(); },
78
+ [ref_key]: (node) => { node.select(); },
79
+ onInput(event) { const input: HTMLInputElement = event.currentTarget; input.select(); },
80
+ onClick: { handleEvent(event) { event.currentTarget.select(); } },
81
+ };
82
+ const global_props: JSX.IntrinsicElements['input'] = props;
83
+ const runtime_props: RuntimeJSX.IntrinsicElements['input'] = global_props;
84
+ const classes: ClassValue = ['one', { two: true }];
85
+ const input = <input {...runtime_props} class={classes} />;
86
+ const svg = <svg><circle cx={10} ref={(node) => { const circle: SVGCircleElement = node; }} /></svg>;
87
+ // @ts-expect-error Input values do not accept objects.
88
+ const bad_value = <input value={{}} />;
89
+ // @ts-expect-error Refs remain specific to the element.
90
+ const bad_ref = <p ref={(node: HTMLInputElement) => { node.select(); }} />;
91
+ // @ts-expect-error Event currentTarget remains specific to the element.
92
+ const bad_event = <p onClick={(event) => { event.currentTarget.select(); }} />;
93
+ `;
94
+ const { service, file_name } = create_service(source, true);
95
+ try {
96
+ const diagnostics = service.getSemanticDiagnostics(file_name);
97
+ expect(
98
+ diagnostics.map((diagnostic) =>
99
+ ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
100
+ ),
101
+ ).toEqual([]);
102
+ } finally {
103
+ service.dispose();
104
+ }
105
+ });
106
+ });