ripple 0.4.2 → 0.4.3
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 +107 -0
- package/package.json +9 -3
- package/src/jsx-runtime.d.ts +2 -1
- package/src/runtime/array.js +15 -9
- package/src/runtime/index-client.js +25 -7
- package/src/runtime/index-server.js +1 -0
- package/src/runtime/internal/client/attributes.js +313 -0
- package/src/runtime/internal/client/blocks.js +4 -14
- package/src/runtime/internal/client/component.js +2 -5
- package/src/runtime/internal/client/composite.js +14 -6
- package/src/runtime/internal/client/constants.js +6 -6
- package/src/runtime/internal/client/context.js +3 -2
- package/src/runtime/internal/client/css.js +11 -26
- package/src/runtime/internal/client/errors.js +188 -0
- package/src/runtime/internal/client/events.js +79 -34
- package/src/runtime/internal/client/expression.js +21 -116
- package/src/runtime/internal/client/for.js +108 -232
- package/src/runtime/internal/client/head.js +4 -3
- package/src/runtime/internal/client/html.js +5 -3
- package/src/runtime/internal/client/hydrate.js +484 -0
- package/src/runtime/internal/client/hydration-enabled.js +7 -0
- package/src/runtime/internal/client/hydration.js +16 -81
- package/src/runtime/internal/client/if.js +13 -9
- package/src/runtime/internal/client/index.js +14 -6
- package/src/runtime/internal/client/operations.js +11 -7
- package/src/runtime/internal/client/portal.js +3 -2
- package/src/runtime/internal/client/render.js +42 -325
- package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
- package/src/runtime/internal/client/runtime.js +87 -363
- package/src/runtime/internal/client/template-ns.js +47 -0
- package/src/runtime/internal/client/template.js +63 -140
- package/src/runtime/internal/client/track-async.js +356 -0
- package/src/runtime/internal/client/try.js +133 -319
- package/src/runtime/internal/client/types.d.ts +48 -24
- package/src/runtime/internal/server/index.js +31 -29
- package/src/runtime/internal/server/prerender.js +27 -0
- package/src/server/index.js +1 -0
- package/src/utils/class-name.js +28 -0
- package/tests/client/basic/basic.errors.test.tsrx +3 -3
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.tsrx.snap +0 -2
- package/tests/client/compiler/compiler.basic.test.tsrx +2 -2
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
- package/tests/client/composite/composite.props.test.tsrx +10 -10
- package/tests/client/derived-release.test.tsrx +3 -3
- package/tests/client/for-item.test.tsrx +39 -0
- package/tests/client/for-selector.test.tsrx +95 -0
- package/tests/hydration/compiled/client/basic.js +87 -87
- package/tests/hydration/compiled/client/composite.js +5 -5
- package/tests/hydration/compiled/client/events.js +40 -48
- package/tests/hydration/compiled/client/for.js +162 -181
- package/tests/hydration/compiled/client/fragment-cursor.js +521 -289
- package/tests/hydration/compiled/client/head.js +22 -22
- package/tests/hydration/compiled/client/hmr.js +4 -4
- package/tests/hydration/compiled/client/html-in-template.js +5 -5
- package/tests/hydration/compiled/client/html.js +152 -162
- package/tests/hydration/compiled/client/if-children.js +58 -53
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +39 -39
- package/tests/hydration/compiled/client/if.js +26 -26
- package/tests/hydration/compiled/client/mixed-control-flow.js +56 -68
- package/tests/hydration/compiled/client/nested-control-flow.js +139 -271
- package/tests/hydration/compiled/client/portal.js +10 -10
- package/tests/hydration/compiled/client/reactivity.js +29 -29
- package/tests/hydration/compiled/client/return.js +2 -2
- package/tests/hydration/compiled/client/streaming.js +33 -35
- package/tests/hydration/compiled/client/switch.js +25 -25
- package/tests/hydration/compiled/client/track-async-serialization.js +41 -41
- package/tests/hydration/compiled/client/try.js +15 -15
- package/tests/hydration/compiled/client/typed-text.js +7 -7
- package/tests/hydration/compiled/server/basic.js +6 -6
- package/tests/hydration/compiled/server/events.js +8 -8
- package/tests/hydration/compiled/server/for.js +22 -22
- package/tests/hydration/compiled/server/fragment-cursor.js +188 -52
- package/tests/hydration/compiled/server/head.js +6 -6
- package/tests/hydration/compiled/server/hmr.js +1 -1
- package/tests/hydration/compiled/server/html.js +1 -1
- package/tests/hydration/compiled/server/if-children.js +9 -9
- package/tests/hydration/compiled/server/if.js +5 -5
- package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
- package/tests/hydration/compiled/server/portal.js +1 -1
- package/tests/hydration/compiled/server/reactivity.js +9 -9
- package/tests/hydration/compiled/server/streaming.js +9 -9
- package/tests/hydration/compiled/server/switch.js +4 -4
- package/tests/hydration/compiled/server/track-async-serialization.js +16 -16
- package/tests/hydration/compiled/server/try.js +4 -4
- package/tests/hydration/compiled/server/typed-text.js +1 -1
- package/tests/hydration/components/fragment-cursor.tsrx +60 -0
- package/tests/hydration/components/track-async-serialization.tsrx +2 -2
- package/tests/hydration/fragment-cursor.test.js +34 -1
- package/tests/hydration/track-async-serialization.test.js +1 -1
- package/tests/server/basic.components.test.tsrx +3 -3
- package/tests/server/track-async-serialization.test.tsrx +6 -6
- package/tests/utils/tracked-types.test.js +3 -3
- package/types/index.d.ts +8 -8
- package/types/server.d.ts +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,112 @@
|
|
|
1
1
|
# ripple
|
|
2
2
|
|
|
3
|
+
## 0.4.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1486](https://github.com/Ripple-TS/ripple/pull/1486)
|
|
8
|
+
[`304ba6c`](https://github.com/Ripple-TS/ripple/commit/304ba6c2d9098752396fff6c2545e3ef2546381c)
|
|
9
|
+
Thanks [@leonidaz](https://github.com/leonidaz)! - perf: a static component,
|
|
10
|
+
template `@if`, or template `@for` followed by a static element sibling inserts
|
|
11
|
+
before that element instead of before a `<!>` placeholder of its own: the
|
|
12
|
+
element is part of the template and never moves, so the compiled template holds
|
|
13
|
+
no comment node for it and the client reuses the element's variable where
|
|
14
|
+
hydration steps to the sibling. The `RippleArray` statics (`from`, `of`,
|
|
15
|
+
`fromAsync`) lower to standalone runtime functions, so a bundle that never
|
|
16
|
+
constructs a `RippleArray` drops the array proxy instead of keeping it through
|
|
17
|
+
property assignments on the constructor's compiled form (the server's
|
|
18
|
+
`fromAsync` lowering previously named a function that did not exist).
|
|
19
|
+
|
|
20
|
+
- [#1487](https://github.com/Ripple-TS/ripple/pull/1487)
|
|
21
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1)
|
|
22
|
+
Thanks [@leonidaz](https://github.com/leonidaz)! - perf: smaller production
|
|
23
|
+
bundles without a runtime cost. The compiler lowers a static `onBlur`-style
|
|
24
|
+
handler (an event its name keeps off delegation) to a direct `listen` call with
|
|
25
|
+
the DOM name and phase resolved at compile time, packs the locals a hoisted
|
|
26
|
+
`@if`/`@switch` captures under positional keys, writes template strings without
|
|
27
|
+
attribute quotes or implied end tags where the parser reads them back the same,
|
|
28
|
+
gives a plain function no `_$_.scope()` call when its body never reads the
|
|
29
|
+
block, and omits `set_class`'s default trailing arguments. The runtime keeps its
|
|
30
|
+
hydration paths (template adoption, expression text, append cursor, streamed
|
|
31
|
+
boundaries) in a module `hydrate()` installs and its `trackAsync` machinery in a
|
|
32
|
+
module that registers with the runtime on first use, and the catch routing is
|
|
33
|
+
registered by the first boundary, so a client-only mount without `trackAsync`
|
|
34
|
+
ships neither and the core runtime no longer imports the boundary module; the
|
|
35
|
+
try boundary state, the root event ref and the template cache use short keys,
|
|
36
|
+
the keyed and reference list diffs share one patch helper, and symbol constants
|
|
37
|
+
carry no descriptions. The runtime's errors are thrown as in Svelte: the message
|
|
38
|
+
and a link to `https://ripple-ts.com/e/<code>` in development, the link alone in
|
|
39
|
+
production, so the message text stays out of production bundles; `set_class`
|
|
40
|
+
composes non-string values with its own clsx-style joiner instead of importing
|
|
41
|
+
`clsx`; hoisted render blocks store captured locals under positional keys; a
|
|
42
|
+
`template()` with no flags passes none; and the hash a `track()` call carries
|
|
43
|
+
encodes the same 32 bits in base 36. The Vite plugin leaves Vite's modulepreload
|
|
44
|
+
polyfill out of production builds unless `build.modulePreload` is configured.
|
|
45
|
+
|
|
46
|
+
- [#1487](https://github.com/Ripple-TS/ripple/pull/1487)
|
|
47
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1)
|
|
48
|
+
Thanks [@leonidaz](https://github.com/leonidaz)! - feat: build shapes.
|
|
49
|
+
`ripple({ ssr: false })` builds a client-only app: components compile without
|
|
50
|
+
the hydration cursor or `track()` serialization hashes (`hydration: false` on
|
|
51
|
+
the compiler), the runtime's hydration paths are compiled out through its
|
|
52
|
+
`HYDRATION` build constant, and `hydrate()` throws; `ssr: true` compiles every
|
|
53
|
+
module for the server. `prerender()` on `ripple/server` renders a component to
|
|
54
|
+
static HTML with every boundary settled and the CSS as text, and a `RenderRoute`
|
|
55
|
+
marked `prerender: true` is rendered through the built server entry at build
|
|
56
|
+
time into `<outDir>/client<path>/index.html`, which the node and bun adapters
|
|
57
|
+
now serve for a directory request before the server renders. The client build's
|
|
58
|
+
`index.html` template moves to the server output so a static handler never
|
|
59
|
+
serves its placeholders.
|
|
60
|
+
|
|
61
|
+
- [#1487](https://github.com/Ripple-TS/ripple/pull/1487)
|
|
62
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1)
|
|
63
|
+
Thanks [@leonidaz](https://github.com/leonidaz)! - perf: a `class` expression
|
|
64
|
+
the compiler can prove to be a string writes through a string-only `set_class`,
|
|
65
|
+
so the clsx-style joiner ships only with a bundle that composes classes from
|
|
66
|
+
arrays or objects; templates in the SVG or MathML namespace parse through a
|
|
67
|
+
`template_ns` entry that an HTML-only bundle never loads; and the class joiner
|
|
68
|
+
is one module shared by the client and server renderers, so `ripple` no longer
|
|
69
|
+
depends on `clsx`. `ripple({ rootBoundary: false })` builds an app that renders
|
|
70
|
+
without the default root try/pending/catch boundary and leaves the boundary
|
|
71
|
+
runtime out of the bundle; a `rootBoundary` option on `mount()` or `hydrate()`
|
|
72
|
+
then throws. The `ripple` package declares `sideEffects: false`, and dynamic
|
|
73
|
+
attribute, style and spread helpers live in their own runtime module, so a
|
|
74
|
+
bundle that never sets an attribute dynamically drops them along with the
|
|
75
|
+
attribute tables they consult. The plugin's `ssr: false` and
|
|
76
|
+
`rootBoundary: false` rewrite the runtime's build-constant imports to literals
|
|
77
|
+
at transform time, which every bundler folds while tree-shaking.
|
|
78
|
+
|
|
79
|
+
- [#1484](https://github.com/Ripple-TS/ripple/pull/1484)
|
|
80
|
+
[`1ea71f1`](https://github.com/Ripple-TS/ripple/commit/1ea71f14fcee5a976ea81e2bedf5010251eaa70c)
|
|
81
|
+
Thanks [@leonidaz](https://github.com/leonidaz)! - perf: a template `@if` that
|
|
82
|
+
closes the trailing run of an element's children (nothing rendered follows it)
|
|
83
|
+
never materializes an anchor node: the element's tail is its position for its
|
|
84
|
+
whole life. An `@if (outer === item.key)` in a `@for` body lowers to the loop's
|
|
85
|
+
selector like an attribute comparison, and a comparison against the item's key
|
|
86
|
+
reads the block's key (fixed for its life) rather than the item, so a change of
|
|
87
|
+
the outer value re-runs the ifs of its previous and next key alone and a
|
|
88
|
+
replaced item leaves its ifs untouched; comparisons against the same outer value
|
|
89
|
+
share one selector per loop. Event delegation reads the composed path only for a
|
|
90
|
+
nested root and looks the handler property up by one string, and `checked` is
|
|
91
|
+
compared against the element instead of a cached write.
|
|
92
|
+
|
|
93
|
+
- [#1487](https://github.com/Ripple-TS/ripple/pull/1487)
|
|
94
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1)
|
|
95
|
+
Thanks [@leonidaz](https://github.com/leonidaz)! - `trackReadOnly(value)`
|
|
96
|
+
replaces the `readOnly()` method on tracked and derived values: it returns a
|
|
97
|
+
derived that follows a tracked or writable derived, or a read-only derived as it
|
|
98
|
+
is. The method is removed, so `count.readOnly()` becomes `trackReadOnly(count)`,
|
|
99
|
+
imported from `ripple`. Dropping the prototype methods lets a bundle that never
|
|
100
|
+
takes a read-only view leave the code out.
|
|
101
|
+
- Updated dependencies
|
|
102
|
+
[[`304ba6c`](https://github.com/Ripple-TS/ripple/commit/304ba6c2d9098752396fff6c2545e3ef2546381c),
|
|
103
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1),
|
|
104
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1),
|
|
105
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1),
|
|
106
|
+
[`1ea71f1`](https://github.com/Ripple-TS/ripple/commit/1ea71f14fcee5a976ea81e2bedf5010251eaa70c),
|
|
107
|
+
[`b4abea0`](https://github.com/Ripple-TS/ripple/commit/b4abea0ab4830eed99adf14dc95a0ef214b24af1)]:
|
|
108
|
+
- @tsrx/ripple@0.2.2
|
|
109
|
+
|
|
3
110
|
## 0.4.2
|
|
4
111
|
|
|
5
112
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.4.
|
|
6
|
+
"version": "0.4.3",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
8
9
|
"module": "src/runtime/index-client.js",
|
|
9
10
|
"main": "src/runtime/index-client.js",
|
|
10
11
|
"repository": {
|
|
@@ -54,6 +55,12 @@
|
|
|
54
55
|
"./internal/client": {
|
|
55
56
|
"default": "./src/runtime/internal/client/index.js"
|
|
56
57
|
},
|
|
58
|
+
"./internal/client/hydration-enabled": {
|
|
59
|
+
"default": "./src/runtime/internal/client/hydration-enabled.js"
|
|
60
|
+
},
|
|
61
|
+
"./internal/client/root-boundary-enabled": {
|
|
62
|
+
"default": "./src/runtime/internal/client/root-boundary-enabled.js"
|
|
63
|
+
},
|
|
57
64
|
"./internal/client-types": {
|
|
58
65
|
"types": "./src/runtime/internal/client/types.d.ts"
|
|
59
66
|
},
|
|
@@ -76,12 +83,11 @@
|
|
|
76
83
|
"#public": "./types/index.d.ts"
|
|
77
84
|
},
|
|
78
85
|
"dependencies": {
|
|
79
|
-
"clsx": "^2.1.1",
|
|
80
86
|
"csstype": "^3.2.3",
|
|
81
87
|
"devalue": "^5.9.2",
|
|
82
88
|
"esm-env": "^1.2.2",
|
|
83
89
|
"@tsrx/core": "^0.2.0",
|
|
84
|
-
"@tsrx/ripple": "0.2.
|
|
90
|
+
"@tsrx/ripple": "0.2.2"
|
|
85
91
|
},
|
|
86
92
|
"devDependencies": {
|
|
87
93
|
"@types/estree": "^1.0.8",
|
package/src/jsx-runtime.d.ts
CHANGED
|
@@ -55,7 +55,8 @@ type NullableStyleProperties<T> = { [Property in keyof T]: T[Property] | null };
|
|
|
55
55
|
|
|
56
56
|
/** JSX attributes live under Ripple so editor hovers identify their framework. */
|
|
57
57
|
declare namespace Ripple {
|
|
58
|
-
type ClassValue =
|
|
58
|
+
type ClassValue =
|
|
59
|
+
string | number | boolean | null | undefined | ClassValue[] | { [key: string]: unknown };
|
|
59
60
|
|
|
60
61
|
/** Inline CSS styles. Values keep their units; null and undefined remove a property. */
|
|
61
62
|
interface CSSProperties
|
package/src/runtime/array.js
CHANGED
|
@@ -24,7 +24,7 @@ export class RippleArray {
|
|
|
24
24
|
* @returns {RippleArray<U>}
|
|
25
25
|
*/
|
|
26
26
|
static from(arrayLike, mapFn, thisArg) {
|
|
27
|
-
return
|
|
27
|
+
return ripple_array_from(safe_scope(), arrayLike, mapFn, thisArg);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -33,7 +33,7 @@ export class RippleArray {
|
|
|
33
33
|
* @returns {RippleArray<U>}
|
|
34
34
|
*/
|
|
35
35
|
static of(...items) {
|
|
36
|
-
return
|
|
36
|
+
return ripple_array_of(safe_scope(), ...items);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -44,11 +44,14 @@ export class RippleArray {
|
|
|
44
44
|
* @returns {Promise<RippleArray<U>>}
|
|
45
45
|
*/
|
|
46
46
|
static async fromAsync(arrayLike, mapFn, thisArg) {
|
|
47
|
-
return
|
|
47
|
+
return ripple_array_from_async(safe_scope(), arrayLike, mapFn, thisArg);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
+
* The compiled form of `new RippleArray(...)`. Each static below is its own
|
|
53
|
+
* function so a bundle that never uses one drops it (and the proxy it would
|
|
54
|
+
* create) instead of keeping a function that carries them all.
|
|
52
55
|
* @template T
|
|
53
56
|
* @param {Block} block
|
|
54
57
|
* @param {...T} elements
|
|
@@ -59,6 +62,7 @@ export function ripple_array(block, ...elements) {
|
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
/**
|
|
65
|
+
* `RippleArray.from(...)`
|
|
62
66
|
* @template T
|
|
63
67
|
* @param {Block} block
|
|
64
68
|
* @param {ArrayLike<T> | Iterable<T>} arrayLike
|
|
@@ -66,23 +70,25 @@ export function ripple_array(block, ...elements) {
|
|
|
66
70
|
* @param {*} [thisArg]
|
|
67
71
|
* @returns {RippleArray<T>}
|
|
68
72
|
*/
|
|
69
|
-
|
|
73
|
+
export function ripple_array_from(block, arrayLike, mapFn, thisArg) {
|
|
70
74
|
var elements = mapFn ? Array.from(arrayLike, mapFn, thisArg) : Array.from(arrayLike);
|
|
71
75
|
return array_proxy({ elements, block, from_static: true });
|
|
72
|
-
}
|
|
76
|
+
}
|
|
73
77
|
|
|
74
78
|
/**
|
|
79
|
+
* `RippleArray.of(...)`
|
|
75
80
|
* @template T
|
|
76
81
|
* @param {Block} block
|
|
77
82
|
* @param {...T} items
|
|
78
83
|
* @returns {RippleArray<T>}
|
|
79
84
|
*/
|
|
80
|
-
|
|
85
|
+
export function ripple_array_of(block, ...items) {
|
|
81
86
|
var elements = Array.of(...items);
|
|
82
87
|
return array_proxy({ elements, block, from_static: true });
|
|
83
|
-
}
|
|
88
|
+
}
|
|
84
89
|
|
|
85
90
|
/**
|
|
91
|
+
* `RippleArray.fromAsync(...)`
|
|
86
92
|
* @template T
|
|
87
93
|
* @param {Block} block
|
|
88
94
|
* @param {ArrayLike<T> | Iterable<T>} arrayLike
|
|
@@ -90,9 +96,9 @@ ripple_array.of = function (block, ...items) {
|
|
|
90
96
|
* @param {any} [thisArg]
|
|
91
97
|
* @returns {Promise<RippleArray<T>>}
|
|
92
98
|
*/
|
|
93
|
-
|
|
99
|
+
export async function ripple_array_from_async(block, arrayLike, mapFn, thisArg) {
|
|
94
100
|
var elements = mapFn
|
|
95
101
|
? await Array.fromAsync(arrayLike, mapFn, thisArg)
|
|
96
102
|
: await Array.fromAsync(arrayLike);
|
|
97
103
|
return array_proxy({ elements, block, from_static: true });
|
|
98
|
-
}
|
|
104
|
+
}
|
|
@@ -6,6 +6,9 @@ import { init_operations } from './internal/client/operations.js';
|
|
|
6
6
|
import { render_component } from './internal/client/component.js';
|
|
7
7
|
import { try_block } from './internal/client/try.js';
|
|
8
8
|
import { remove_styles } from './internal/client/css.js';
|
|
9
|
+
import { install_hydration } from './internal/client/hydrate.js';
|
|
10
|
+
import { hydration_disabled, root_boundary_disabled } from './internal/client/errors.js';
|
|
11
|
+
import { ROOT_BOUNDARY } from 'ripple/internal/client/root-boundary-enabled';
|
|
9
12
|
import { normalize_children } from './element.js';
|
|
10
13
|
import {
|
|
11
14
|
hydrate_next,
|
|
@@ -82,8 +85,10 @@ export function mount(component, options) {
|
|
|
82
85
|
// Requesting a frame otherwise makes the browser attach layout to the
|
|
83
86
|
// mounted tree before the app's next update, which slows a teardown that
|
|
84
87
|
// would have preceded the first paint.
|
|
85
|
-
if (
|
|
86
|
-
|
|
88
|
+
if (HYDRATION) {
|
|
89
|
+
if (document.querySelector('style[data-ripple-ssr]') !== null) {
|
|
90
|
+
requestAnimationFrame(remove_styles);
|
|
91
|
+
}
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
let props = options.props ?? {};
|
|
@@ -100,15 +105,18 @@ export function mount(component, options) {
|
|
|
100
105
|
// The app appends into the emptied target; a root that keeps inserting
|
|
101
106
|
// relative to its anchor materializes one (see `resolve_anchor`).
|
|
102
107
|
/** @type {AppendIntoAnchor} */
|
|
103
|
-
const anchor = { parent: target, into: true };
|
|
108
|
+
const anchor = { parent: target, into: true, tail: false };
|
|
104
109
|
|
|
105
110
|
/** @type {import('./internal/client/events.js').RootTargetRef | null} */
|
|
106
111
|
let events_ref = handle_root_events(target);
|
|
107
112
|
|
|
108
113
|
const root_boundary = options.rootBoundary;
|
|
114
|
+
if (!ROOT_BOUNDARY && root_boundary != null && root_boundary !== false) {
|
|
115
|
+
root_boundary_disabled();
|
|
116
|
+
}
|
|
109
117
|
|
|
110
118
|
const _root = root(() => {
|
|
111
|
-
if (root_boundary === false) {
|
|
119
|
+
if (!ROOT_BOUNDARY || root_boundary === false) {
|
|
112
120
|
render_component(component, anchor, props);
|
|
113
121
|
return;
|
|
114
122
|
}
|
|
@@ -144,7 +152,11 @@ export function mount(component, options) {
|
|
|
144
152
|
* @returns {() => void}
|
|
145
153
|
*/
|
|
146
154
|
export function hydrate(component, options) {
|
|
155
|
+
if (!HYDRATION) {
|
|
156
|
+
hydration_disabled();
|
|
157
|
+
}
|
|
147
158
|
init_operations();
|
|
159
|
+
install_hydration();
|
|
148
160
|
requestAnimationFrame(remove_styles);
|
|
149
161
|
|
|
150
162
|
let props = options.props ?? {};
|
|
@@ -152,7 +164,7 @@ export function hydrate(component, options) {
|
|
|
152
164
|
props = normalize_props(props);
|
|
153
165
|
}
|
|
154
166
|
const target = options.target;
|
|
155
|
-
const was_hydrating = hydrating;
|
|
167
|
+
const was_hydrating = HYDRATION && hydrating;
|
|
156
168
|
const previous_hydrate_node = hydrate_node;
|
|
157
169
|
let anchor = target.firstChild;
|
|
158
170
|
|
|
@@ -176,9 +188,12 @@ export function hydrate(component, options) {
|
|
|
176
188
|
|
|
177
189
|
const root_boundary = options.rootBoundary;
|
|
178
190
|
const marker = /** @type {Comment} */ (anchor);
|
|
191
|
+
if (!ROOT_BOUNDARY && root_boundary != null && root_boundary !== false) {
|
|
192
|
+
root_boundary_disabled();
|
|
193
|
+
}
|
|
179
194
|
|
|
180
195
|
_root = root(() => {
|
|
181
|
-
if (root_boundary === false && marker.data === HYDRATION_START) {
|
|
196
|
+
if ((!ROOT_BOUNDARY || root_boundary === false) && marker.data === HYDRATION_START) {
|
|
182
197
|
// The root boundary's own hydration walk: consume the `<!--[-->`
|
|
183
198
|
// marker and render against it, as `try_block` does for the root.
|
|
184
199
|
hydrate_next();
|
|
@@ -215,13 +230,15 @@ export { Context } from './internal/client/context.js';
|
|
|
215
230
|
export {
|
|
216
231
|
flush_sync as flushSync,
|
|
217
232
|
track,
|
|
218
|
-
|
|
233
|
+
track_read_only as trackReadOnly,
|
|
219
234
|
untrack,
|
|
220
235
|
tick,
|
|
221
236
|
is_tracked_pending as trackPending,
|
|
222
237
|
peek_tracked as peek,
|
|
223
238
|
} from './internal/client/runtime.js';
|
|
224
239
|
|
|
240
|
+
export { track_async as trackAsync } from './internal/client/track-async.js';
|
|
241
|
+
|
|
225
242
|
export { snapshot } from './proxy.js';
|
|
226
243
|
|
|
227
244
|
export { RippleArray } from './array.js';
|
|
@@ -271,3 +288,4 @@ export {
|
|
|
271
288
|
bindOffsetWidth,
|
|
272
289
|
bindOffsetHeight,
|
|
273
290
|
} from './internal/client/bindings.js';
|
|
291
|
+
import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/** @import { Block } from '#client' */
|
|
2
|
+
|
|
3
|
+
import { block, branch, destroy_block, ref } from './blocks.js';
|
|
4
|
+
import { DESTROYED, REF_PROP, RENDER_BLOCK } from './constants.js';
|
|
5
|
+
import { isRefProp as is_ref_prop } from '@tsrx/core/runtime/ref';
|
|
6
|
+
import { is_ripple_object } from './utils.js';
|
|
7
|
+
import {
|
|
8
|
+
get_descriptors,
|
|
9
|
+
get_own_property_symbols,
|
|
10
|
+
get_prototype_of,
|
|
11
|
+
} from '@tsrx/core/runtime/language-helpers';
|
|
12
|
+
import { event_listener } from './events.js';
|
|
13
|
+
import { get_attribute_event_name, is_event_attribute } from '@tsrx/core/runtime/events';
|
|
14
|
+
import { get } from './runtime.js';
|
|
15
|
+
import { normalize_css_property_name } from '@tsrx/core/runtime/html';
|
|
16
|
+
import { set_class_value } from './render.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Dynamic attributes, styles and spreads. Apart from `render.js`, so that an
|
|
20
|
+
* app whose attributes are all static or class/text/value writes never loads
|
|
21
|
+
* the attribute tables these helpers consult.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** @type {Map<string, string[]>} */
|
|
25
|
+
var setters_cache = new Map();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {Element} element
|
|
29
|
+
* @returns {string[]}
|
|
30
|
+
*/
|
|
31
|
+
function get_setters(element) {
|
|
32
|
+
var setters = setters_cache.get(element.nodeName);
|
|
33
|
+
if (setters) return setters;
|
|
34
|
+
setters_cache.set(element.nodeName, (setters = []));
|
|
35
|
+
|
|
36
|
+
var descriptors;
|
|
37
|
+
var proto = element; // In the case of custom elements there might be setters on the instance
|
|
38
|
+
var element_proto = Element.prototype;
|
|
39
|
+
|
|
40
|
+
// Stop at Element, from there on there's only unnecessary setters we're not interested in
|
|
41
|
+
// Do not use constructor.name here as that's unreliable in some browser environments
|
|
42
|
+
while (element_proto !== proto) {
|
|
43
|
+
descriptors = get_descriptors(proto);
|
|
44
|
+
|
|
45
|
+
for (var key in descriptors) {
|
|
46
|
+
if (descriptors[key].set) {
|
|
47
|
+
setters.push(key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
proto = get_prototype_of(proto);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return setters;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {Element} element
|
|
59
|
+
* @param {any} value
|
|
60
|
+
* @param {Record<string, string | number | null | undefined> | undefined} prev
|
|
61
|
+
* @returns {void}
|
|
62
|
+
*/
|
|
63
|
+
export function set_style(element, value, prev = {}) {
|
|
64
|
+
if (value == null) {
|
|
65
|
+
element.removeAttribute('style');
|
|
66
|
+
} else if (typeof value !== 'string') {
|
|
67
|
+
apply_styles(/** @type {HTMLElement} */ (element), value, prev);
|
|
68
|
+
} else {
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
element.style.cssText = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {Element} element
|
|
76
|
+
* @param {string} attribute
|
|
77
|
+
* @param {any} value
|
|
78
|
+
* @returns {void}
|
|
79
|
+
*/
|
|
80
|
+
export function set_attribute(element, attribute, value) {
|
|
81
|
+
if (value == null) {
|
|
82
|
+
element.removeAttribute(attribute);
|
|
83
|
+
} else if (typeof value !== 'string' && get_setters(element).includes(attribute)) {
|
|
84
|
+
/** @type {any} */ (element)[attribute] = value;
|
|
85
|
+
} else {
|
|
86
|
+
element.setAttribute(attribute, value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {HTMLElement} element
|
|
92
|
+
* @param {Record<string, string | number | null | undefined>} new_styles
|
|
93
|
+
* @param {Record<string, string | number | null | undefined>} prev
|
|
94
|
+
*/
|
|
95
|
+
function apply_styles(element, new_styles, prev) {
|
|
96
|
+
const style = element.style;
|
|
97
|
+
|
|
98
|
+
// Apply new styles
|
|
99
|
+
for (const key in new_styles) {
|
|
100
|
+
const css_prop = normalize_css_property_name(key);
|
|
101
|
+
const raw_value = new_styles[key];
|
|
102
|
+
const value = raw_value == null ? null : String(raw_value);
|
|
103
|
+
|
|
104
|
+
if (!(key in prev) || prev[key] !== value) {
|
|
105
|
+
style.setProperty(css_prop, value);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Remove properties that were in prev but not in new_styles
|
|
110
|
+
for (const key in prev) {
|
|
111
|
+
if (!(key in new_styles)) {
|
|
112
|
+
const css_prop = normalize_css_property_name(key);
|
|
113
|
+
style.removeProperty(css_prop);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Helper function to set a single attribute
|
|
120
|
+
* @param {Element} element
|
|
121
|
+
* @param {string} key
|
|
122
|
+
* @param {any} value
|
|
123
|
+
* @param {Record<string, (() => void) | undefined>} remove_listeners
|
|
124
|
+
* @param {Record<string | symbol, any>} prev
|
|
125
|
+
*/
|
|
126
|
+
function set_attribute_helper(element, key, value, remove_listeners, prev) {
|
|
127
|
+
if (key === 'class') {
|
|
128
|
+
const is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
|
|
129
|
+
set_class_value(/** @type {HTMLElement} */ (element), value, undefined, is_html);
|
|
130
|
+
} else if (key === 'innerHTML') {
|
|
131
|
+
element.removeAttribute('innerhtml');
|
|
132
|
+
/** @type {HTMLElement} */ (element).innerHTML = value == null ? '' : String(value);
|
|
133
|
+
} else if (key === 'style') {
|
|
134
|
+
set_style(element, value, prev.style);
|
|
135
|
+
} else if (key === '#class') {
|
|
136
|
+
// Special case for the scope classes of an element that spreads props:
|
|
137
|
+
// one token per scope hash and applied theme.
|
|
138
|
+
element.classList.add(...String(value).split(' ').filter(Boolean));
|
|
139
|
+
} else if (typeof key === 'string' && is_event_attribute(key)) {
|
|
140
|
+
// Handle event handlers in spread props
|
|
141
|
+
if (remove_listeners[key]) {
|
|
142
|
+
remove_listeners[key]();
|
|
143
|
+
remove_listeners[key] = undefined;
|
|
144
|
+
}
|
|
145
|
+
if (value != null) {
|
|
146
|
+
const event_name = get_attribute_event_name(key, value);
|
|
147
|
+
remove_listeners[key] = event_listener(event_name, element, value);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
set_attribute(element, key, value);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {Element} element
|
|
156
|
+
* @param {() => Record<string | symbol, any>} fn
|
|
157
|
+
* @returns {() => void}
|
|
158
|
+
*/
|
|
159
|
+
export function apply_element_spread(element, fn) {
|
|
160
|
+
/** @type {Record<string | symbol, any>} */
|
|
161
|
+
var prev = {};
|
|
162
|
+
/** @type {Record<string | symbol, Block | undefined>} */
|
|
163
|
+
var effects = {};
|
|
164
|
+
/** @type {Record<string | symbol, (() => void) | undefined>} */
|
|
165
|
+
var remove_listeners = {};
|
|
166
|
+
|
|
167
|
+
/** @type {Record<symbol, any>} */
|
|
168
|
+
var prev_symbols = {};
|
|
169
|
+
/** @type {Record<string, any>} */
|
|
170
|
+
var prev_ref_props = {};
|
|
171
|
+
|
|
172
|
+
return () => {
|
|
173
|
+
var next = fn();
|
|
174
|
+
var current_symbols = /** @type {Record<symbol, any>} */ ({});
|
|
175
|
+
|
|
176
|
+
for (const symbol of get_own_property_symbols(next)) {
|
|
177
|
+
if (symbol.description !== REF_PROP) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const ref_fn = next[symbol];
|
|
181
|
+
current_symbols[symbol] = ref_fn;
|
|
182
|
+
|
|
183
|
+
if (
|
|
184
|
+
!(symbol in prev_symbols) ||
|
|
185
|
+
ref_fn !== prev_symbols[symbol] ||
|
|
186
|
+
(effects[symbol] && (effects[symbol].f & DESTROYED) !== 0)
|
|
187
|
+
) {
|
|
188
|
+
if (effects[symbol] && (effects[symbol].f & DESTROYED) === 0) {
|
|
189
|
+
destroy_block(effects[symbol]);
|
|
190
|
+
}
|
|
191
|
+
effects[symbol] = create_spread_ref_effect(element, ref_fn);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const symbol of get_own_property_symbols(prev_symbols)) {
|
|
196
|
+
if (!(symbol in current_symbols) && effects[symbol]) {
|
|
197
|
+
destroy_block(/** @type {Block} */ (effects[symbol]));
|
|
198
|
+
effects[symbol] = undefined;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
prev_symbols = current_symbols;
|
|
203
|
+
|
|
204
|
+
/** @type {Record<string, any>} */
|
|
205
|
+
var current_ref_props = {};
|
|
206
|
+
var keys = spread_keys(next);
|
|
207
|
+
|
|
208
|
+
for (const key of keys) {
|
|
209
|
+
const ref_fn = next[key];
|
|
210
|
+
if (!is_ref_prop(ref_fn)) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
current_ref_props[key] = ref_fn;
|
|
215
|
+
|
|
216
|
+
if (
|
|
217
|
+
!(key in prev_ref_props) ||
|
|
218
|
+
ref_fn !== prev_ref_props[key] ||
|
|
219
|
+
(effects[key] && (effects[key].f & DESTROYED) !== 0)
|
|
220
|
+
) {
|
|
221
|
+
if (effects[key] && (effects[key].f & DESTROYED) === 0) {
|
|
222
|
+
destroy_block(effects[key]);
|
|
223
|
+
}
|
|
224
|
+
effects[key] = create_spread_ref_effect(element, ref_fn);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
for (const key in prev_ref_props) {
|
|
229
|
+
if (!(key in current_ref_props) && effects[key]) {
|
|
230
|
+
destroy_block(/** @type {Block} */ (effects[key]));
|
|
231
|
+
effects[key] = undefined;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
prev_ref_props = current_ref_props;
|
|
236
|
+
|
|
237
|
+
for (let key in remove_listeners) {
|
|
238
|
+
// Remove event listeners that are no longer present
|
|
239
|
+
if ((!keys.includes(key) || is_ref_prop(next[key])) && remove_listeners[key]) {
|
|
240
|
+
remove_listeners[key]();
|
|
241
|
+
remove_listeners[key] = undefined;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
for (const key in prev) {
|
|
246
|
+
if (!keys.includes(key) || is_ref_prop(next[key])) {
|
|
247
|
+
if (key === '#class') {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
set_attribute_helper(element, key, null, remove_listeners, prev);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** @type {typeof prev} */
|
|
255
|
+
const current = {};
|
|
256
|
+
for (const key of keys) {
|
|
257
|
+
if (key === 'children') continue;
|
|
258
|
+
|
|
259
|
+
let value = next[key];
|
|
260
|
+
if (is_ref_prop(value)) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (is_ripple_object(value)) {
|
|
264
|
+
value = get(value);
|
|
265
|
+
}
|
|
266
|
+
current[key] = value;
|
|
267
|
+
|
|
268
|
+
if (key in prev && prev[key] === value && key !== '#class') {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
set_attribute_helper(element, key, value, remove_listeners, prev);
|
|
273
|
+
}
|
|
274
|
+
prev = current;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* The keys an element spread applies: every prop of a props instance, or the
|
|
280
|
+
* enumerable keys of a plain object.
|
|
281
|
+
* @param {Record<string | symbol, any>} next
|
|
282
|
+
* @returns {string[]}
|
|
283
|
+
*/
|
|
284
|
+
function spread_keys(next) {
|
|
285
|
+
/** @type {string[]} */
|
|
286
|
+
var keys = [];
|
|
287
|
+
for (var key in next) keys.push(key);
|
|
288
|
+
return keys;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Keep spread refs in a branch block so ordinary spread updates do not destroy
|
|
293
|
+
* and recreate the ref block before `apply_element_spread` can compare the
|
|
294
|
+
* previous and current ref values.
|
|
295
|
+
*
|
|
296
|
+
* @param {Element} element
|
|
297
|
+
* @param {any} ref_fn
|
|
298
|
+
* @returns {Block}
|
|
299
|
+
*/
|
|
300
|
+
function create_spread_ref_effect(element, ref_fn) {
|
|
301
|
+
return branch(() => {
|
|
302
|
+
ref(element, () => ref_fn);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* A render block that applies a spread of attributes to an element.
|
|
307
|
+
* @param {any} element
|
|
308
|
+
* @param {any} fn
|
|
309
|
+
* @param {number} [flags]
|
|
310
|
+
*/
|
|
311
|
+
export function render_spread(element, fn, flags = 0) {
|
|
312
|
+
return block(RENDER_BLOCK | flags, apply_element_spread(element, fn));
|
|
313
|
+
}
|