@symbiote-native/svelte 0.2.1 → 0.3.0
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/build/bootstrap.js +1 -1
- package/build/class-value.js +1 -1
- package/build/components/RefreshControl.svelte +4 -2
- package/build/components/Text.svelte +11 -2
- package/build/components/View.svelte +3 -1
- package/build/components/activity-indicator/index.svelte +8 -2
- package/build/components/button.svelte +8 -3
- package/build/components/flat-list/index.svelte +46 -11
- package/build/components/image/image-logic.js +13 -3
- package/build/components/image-background/index.svelte +8 -2
- package/build/components/index.d.ts +2 -2
- package/build/components/input-accessory-view/index.svelte +4 -1
- package/build/components/keyboard-avoiding-view/index.svelte +34 -11
- package/build/components/modal/index.svelte +4 -1
- package/build/components/pressable/index.svelte +10 -3
- package/build/components/pressable/pressable-props.d.ts +1 -1
- package/build/components/scroll-view/index.svelte +54 -21
- package/build/components/scroll-view/sticky-header.svelte +49 -16
- package/build/components/section-list/index.svelte +6 -1
- package/build/components/switch/index.svelte +14 -3
- package/build/components/text-input/index.svelte +22 -6
- package/build/components/touchable-highlight/index.svelte +114 -15
- package/build/components/touchable-highlight/touchable-highlight-props.d.ts +2 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback.svelte +7 -2
- package/build/components/touchable-opacity/index.svelte +112 -56
- package/build/components/touchable-without-feedback/index.svelte +59 -4
- package/build/components/virtualized-list/index.svelte +233 -70
- package/build/components/virtualized-list/virtualized-list-props.js +8 -4
- package/build/components/virtualized-section-list/index.svelte +56 -9
- package/build/components/virtualized-section-list/index.svelte.d.ts +1 -1
- package/build/components/virtualized-section-list/virtualized-section-list-props.d.ts +5 -0
- package/build/create-portal/index.d.ts +22 -0
- package/build/create-portal/index.js +160 -0
- package/build/descriptor-to-svelte.js +15 -21
- package/build/dom-shim/index.d.ts +1 -0
- package/build/dom-shim/index.js +3 -0
- package/build/dom-shim/text.js +38 -1
- package/build/host-instance.js +7 -2
- package/build/index.d.ts +5 -3
- package/build/index.js +16 -11
- package/build/modules/animated/create-animated-component.d.ts +5 -0
- package/build/modules/animated/create-animated-component.js +131 -0
- package/build/modules/animated/index.d.ts +24 -14
- package/build/modules/animated/index.js +30 -30
- package/build/modules/status-bar/index.js +1 -1
- package/build/modules/status-bar/index.svelte +4 -1
- package/build/native-view-bridge.d.ts +1 -1
- package/build/native-view-bridge.js +1 -1
- package/build/preprocessor/collapse-text-whitespace.js +20 -7
- package/build/preprocessor/forbid-web-only-constructs.js +8 -2
- package/build/preprocessor/scoped-styles.js +141 -104
- package/build/render.js +48 -3
- package/build/scope-token.d.ts +3 -2
- package/build/scope-token.js +6 -17
- package/build/style-scope.d.ts +2 -1
- package/build/style-scope.js +10 -8
- package/metro-svelte-transformer.cjs +1 -1
- package/package.json +7 -7
- package/build/modules/animated/AnimatedImage.svelte +0 -115
- package/build/modules/animated/AnimatedImage.svelte.d.ts +0 -5
- package/build/modules/animated/AnimatedScrollView.svelte +0 -92
- package/build/modules/animated/AnimatedScrollView.svelte.d.ts +0 -17
- package/build/modules/animated/AnimatedText.svelte +0 -58
- package/build/modules/animated/AnimatedText.svelte.d.ts +0 -5
- package/build/modules/animated/AnimatedView.svelte +0 -95
- package/build/modules/animated/AnimatedView.svelte.d.ts +0 -5
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// createAnimatedComponent: the Svelte twin of the React/Vue/Solid wrapper. Wraps ANY base
|
|
2
|
+
// component so it accepts AnimatedNodes in its props. The framework-agnostic half
|
|
3
|
+
// (reduceProps / readPassthroughStyle / resolveHostNode / the AnimatedProps leaf lifecycle)
|
|
4
|
+
// comes from @symbiote-native/engine, shared verbatim with every other adapter; only the
|
|
5
|
+
// lifecycle below is Svelte's.
|
|
6
|
+
//
|
|
7
|
+
// WHY IT IS PLAIN TS AND NOT A PARAMETRIZED `.svelte` FILE. The generic wrap expresses fine in
|
|
8
|
+
// Svelte — `<Component {...reduced} bind:this={inner} />` over a `component` prop compiles clean
|
|
9
|
+
// — but a `.ts` module that imports a `.svelte` file is unparseable by any tool without the
|
|
10
|
+
// Svelte plugin, which includes this repo's vitest and every consumer smoke test that builds an
|
|
11
|
+
// Animated namespace by hand (packages/navigation's drawer suite). Writing the component body
|
|
12
|
+
// against `svelte/internal/client` keeps `createAnimatedComponent` importable from ordinary TS
|
|
13
|
+
// everywhere, and leaves `modules/animated/` with no `.svelte` file at all. The internals used
|
|
14
|
+
// here are the exact calls the compiler itself emits for the same source — see this package's
|
|
15
|
+
// `svelte-internal-client.d.ts`, and §8 of the svelte-adapter-dom-shim skill for the bump
|
|
16
|
+
// checklist that covers them.
|
|
17
|
+
//
|
|
18
|
+
// TWO WAYS TO THE HOST NODE, because Svelte has no ref fall-through. `bind:this` on a COMPONENT
|
|
19
|
+
// resolves to that component's exports, never to its element:
|
|
20
|
+
// * a scroll container (ScrollView / FlatList / SectionList) exports an imperative handle, so
|
|
21
|
+
// resolveHostNode() unwraps it through getScrollNode() — the same call React and Vue make.
|
|
22
|
+
// Binding the leaf to the handle itself type-checks and then silently animates nothing.
|
|
23
|
+
// * a plain host primitive (View / Text / Image) exports nothing, so the node arrives through
|
|
24
|
+
// an `{@attach}` of our own: every Symbiote component forwards attachments onto its host tag
|
|
25
|
+
// (runes/attachments.ts), which is the adapter's documented seam to a committed node.
|
|
26
|
+
// A base this adapter does not own works if it does either.
|
|
27
|
+
//
|
|
28
|
+
// EVERY OTHER PROP IS FORWARDED UNTOUCHED, including snippets. Svelte snippets — `children` and
|
|
29
|
+
// named scoped ones such as a list's `item` / `sectionHeader` — are ordinary props, so naming any
|
|
30
|
+
// of them here would drop the rest: Vue's wrapper forwarded only its default slot and
|
|
31
|
+
// Animated.FlatList committed empty cells with no error. The only prop this wrapper consumes is
|
|
32
|
+
// `passthroughAnimatedPropExplicitValues`.
|
|
33
|
+
import { createAttachmentKey } from 'svelte/attachments';
|
|
34
|
+
import { derived, get, pop, push, set, spread_props, state, user_effect, } from 'svelte/internal/client';
|
|
35
|
+
import { createAnimatedLeafLifecycle, isNativeAnimatedAvailable, isSymbioteNode, readPassthroughStyle, reduceProps, resolveHostNode, } from '@symbiote-native/engine';
|
|
36
|
+
import { ShimElement } from '../../dom-shim/index.js';
|
|
37
|
+
// RN's prop carrying explicit (already-rasterized) values that override the animated prop in the
|
|
38
|
+
// COMMITTED props (sticky-header passthrough). Named once so reduce and reconcile agree.
|
|
39
|
+
const PASSTHROUGH_PROP = 'passthroughAnimatedPropExplicitValues';
|
|
40
|
+
// The three keys Svelte's own compiled `rest_props` always drops, plus the prop we consume.
|
|
41
|
+
const BASE_EXCLUDES = new Set([
|
|
42
|
+
'$$slots',
|
|
43
|
+
'$$events',
|
|
44
|
+
'$$legacy',
|
|
45
|
+
PASSTHROUGH_PROP,
|
|
46
|
+
]);
|
|
47
|
+
// The leaf additionally never sees `children`: an AnimatedProps leaf rasterizes its props into
|
|
48
|
+
// setNativeProps on every frame, and a snippet is not a Fabric prop. It still reaches the BASE.
|
|
49
|
+
const LEAF_EXCLUDES = new Set([...BASE_EXCLUDES, 'children']);
|
|
50
|
+
// A fresh plain object each call, never the live props proxy: the leaf lifecycle stores its
|
|
51
|
+
// argument as the previous-props SNAPSHOT and compares the next call against it, so handing it
|
|
52
|
+
// one live object twice compares that object with itself and skips every reconcile forever.
|
|
53
|
+
function collect(props, excludes) {
|
|
54
|
+
const out = {};
|
|
55
|
+
for (const key of Object.keys(props)) {
|
|
56
|
+
if (excludes.has(key))
|
|
57
|
+
continue;
|
|
58
|
+
out[key] = Reflect.get(props, key);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
// Reads the base's display name for the dlog label, without a cast, so a reconcile line in a log
|
|
63
|
+
// dump says which Animated.* it came from — the per-component labels the six hand-authored files
|
|
64
|
+
// used to hardcode.
|
|
65
|
+
function baseName(base) {
|
|
66
|
+
if (typeof base !== 'function' && typeof base !== 'object')
|
|
67
|
+
return 'Anonymous';
|
|
68
|
+
if (base === null)
|
|
69
|
+
return 'Anonymous';
|
|
70
|
+
const name = Reflect.get(base, 'name');
|
|
71
|
+
return typeof name === 'string' && name.length > 0 ? name : 'Anonymous';
|
|
72
|
+
}
|
|
73
|
+
function hostNode(instance, shim) {
|
|
74
|
+
const unwrapped = resolveHostNode(instance);
|
|
75
|
+
if (isSymbioteNode(unwrapped))
|
|
76
|
+
return unwrapped;
|
|
77
|
+
return shim?.engineNode ?? null;
|
|
78
|
+
}
|
|
79
|
+
export function createAnimatedComponent(Base) {
|
|
80
|
+
const label = `Animated(${baseName(Base)})`;
|
|
81
|
+
return function AnimatedComponent(internals, props) {
|
|
82
|
+
// The compiled twin of a component's own `$props()` scope: without it the `$effect`s below
|
|
83
|
+
// are created immediately instead of being deferred to mount, and would first run before
|
|
84
|
+
// the base has rendered anything.
|
|
85
|
+
push(props, true);
|
|
86
|
+
const lifecycle = createAnimatedLeafLifecycle(label);
|
|
87
|
+
// The `$state.raw` twin: the shim is held by IDENTITY (a deep proxy would miss the engine's
|
|
88
|
+
// WeakMap mirror) and read inside the reconcile effect, so the leaf rebinds when it lands.
|
|
89
|
+
const hostShim = state(null);
|
|
90
|
+
// Rasterized props for the declarative bag: every animated node replaced by its current
|
|
91
|
+
// value, so the first paint already carries concrete values. The per-FRAME path is separate
|
|
92
|
+
// (AnimatedProps.update -> setNativeProps) and never goes through this.
|
|
93
|
+
const reduced = derived(() => {
|
|
94
|
+
const out = reduceProps(collect(props, BASE_EXCLUDES));
|
|
95
|
+
const passthroughStyle = readPassthroughStyle(Reflect.get(props, PASSTHROUGH_PROP));
|
|
96
|
+
if (passthroughStyle !== undefined) {
|
|
97
|
+
out.style =
|
|
98
|
+
out.style === undefined
|
|
99
|
+
? passthroughStyle
|
|
100
|
+
: [out.style, passthroughStyle];
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
});
|
|
104
|
+
// `reduceProps` rebuilds a plain string-keyed Record, so `{@attach}`'s SYMBOL keys do not
|
|
105
|
+
// survive it. This one is ours and rides as a second spread source; a user's own attachments
|
|
106
|
+
// are re-collected the same way.
|
|
107
|
+
const attachments = {
|
|
108
|
+
[createAttachmentKey()]: (node) => {
|
|
109
|
+
set(hostShim, node instanceof ShimElement ? node : null);
|
|
110
|
+
return () => set(hostShim, null);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
for (const key of Object.getOwnPropertySymbols(props)) {
|
|
114
|
+
attachments[key] = Reflect.get(props, key);
|
|
115
|
+
}
|
|
116
|
+
const instance = Base(internals, spread_props(() => get(reduced), attachments));
|
|
117
|
+
// `props` and the node are read unconditionally before any branch — a guarded read would
|
|
118
|
+
// drop that dependency from later re-runs. reconcile() itself tolerates a null node.
|
|
119
|
+
user_effect(() => {
|
|
120
|
+
const leafProps = collect(props, LEAF_EXCLUDES);
|
|
121
|
+
const node = hostNode(instance, get(hostShim));
|
|
122
|
+
const passthrough = Reflect.get(props, PASSTHROUGH_PROP);
|
|
123
|
+
lifecycle.reconcile(leafProps, node, passthrough != null && isNativeAnimatedAvailable());
|
|
124
|
+
});
|
|
125
|
+
user_effect(() => () => lifecycle.teardown());
|
|
126
|
+
// The base's own exports ARE the wrapper's: a parent's `bind:this` on Animated.FlatList gets
|
|
127
|
+
// the real scrollToOffset/scrollToIndex/getScrollNode surface, and a method added to the base
|
|
128
|
+
// later cannot go missing here the way a hand-listed delegation would.
|
|
129
|
+
return pop(instance);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnimatedValue, AnimatedValueXY, AnimatedColor, timing, spring, decay, parallel, sequence, stagger, loop, delay, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent } from '@symbiote-native/engine';
|
|
2
|
+
export { createAnimatedComponent } from './create-animated-component';
|
|
2
3
|
export type { IAnimatedComponentProps } from './animated-component-props';
|
|
3
4
|
export declare const Animated: {
|
|
4
5
|
Value: typeof AnimatedValue;
|
|
@@ -40,19 +41,28 @@ export declare const Animated: {
|
|
|
40
41
|
event: typeof event;
|
|
41
42
|
forkEvent: typeof forkEvent;
|
|
42
43
|
unforkEvent: typeof unforkEvent;
|
|
43
|
-
View: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
View: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
|
|
45
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
46
|
+
$set?(props: Partial<Record<string, any>>): void;
|
|
47
|
+
} & Record<string, any>, string>;
|
|
48
|
+
Text: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
|
|
49
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
50
|
+
$set?(props: Partial<Record<string, any>>): void;
|
|
51
|
+
} & Record<string, any>, string>;
|
|
52
|
+
Image: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
|
|
53
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
54
|
+
$set?(props: Partial<Record<string, any>>): void;
|
|
55
|
+
} & Record<string, any>, string>;
|
|
46
56
|
ScrollView: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
},
|
|
57
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
58
|
+
$set?(props: Partial<Record<string, any>>): void;
|
|
59
|
+
} & Record<string, any>, string>;
|
|
60
|
+
FlatList: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
|
|
61
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
62
|
+
$set?(props: Partial<Record<string, any>>): void;
|
|
63
|
+
} & Record<string, any>, string>;
|
|
64
|
+
SectionList: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
|
|
65
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
66
|
+
$set?(props: Partial<Record<string, any>>): void;
|
|
67
|
+
} & Record<string, any>, string>;
|
|
58
68
|
};
|
|
@@ -4,32 +4,26 @@
|
|
|
4
4
|
// interpolation and drivers come from @symbiote-native/engine (framework-agnostic, JS-driven),
|
|
5
5
|
// spread in verbatim.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// authored `.svelte` file sharing only the non-visual reconcile logic
|
|
15
|
-
// (animated-props-runtime.ts). `Animated.createAnimatedComponent` is a genuine, documented scope
|
|
16
|
-
// boundary, not an oversight — see this module's own report for the full reasoning. A consumer
|
|
17
|
-
// wanting to animate their OWN custom component should follow the same four-file pattern (rebuild
|
|
18
|
-
// an AnimatedProps leaf from its props, reconcile on every render into the component's own host
|
|
19
|
-
// node/handle, expose that host node via bind:this) rather than reach for a generic wrapper that
|
|
20
|
-
// does not exist on this adapter.
|
|
7
|
+
// All six components come from ONE generic `createAnimatedComponent(Base)` — the same shape
|
|
8
|
+
// React, Vue and Solid have — applied to the ordinary components. There is no hand-authored
|
|
9
|
+
// `.svelte` file per animated component any more: an earlier revision of this header claimed a
|
|
10
|
+
// generic wrap was impossible on Svelte, and that was simply wrong. See
|
|
11
|
+
// create-animated-component.ts for how the wrap reaches the host node behind a base that exports
|
|
12
|
+
// an imperative handle (ScrollView/FlatList/SectionList) versus one that exports nothing
|
|
13
|
+
// (View/Text/Image), and for why it is plain TS rather than a parametrized `.svelte` file.
|
|
21
14
|
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// the module's own report). Wrapping them would follow the exact same
|
|
26
|
-
// wrap-the-real-component-via-its-exported-handle shape as AnimatedScrollView, once each exposes
|
|
27
|
-
// (or is confirmed to need) an equivalent handle.
|
|
15
|
+
// A consumer animating their OWN component wraps it the same way: `createAnimatedComponent(Mine)`
|
|
16
|
+
// works as long as Mine either exports `getScrollNode()` or forwards `{@attach}` onto its host
|
|
17
|
+
// tag, which every component in this adapter does.
|
|
28
18
|
import { AnimatedValue, AnimatedValueXY, AnimatedColor, AnimatedMock, Easing, Platform, timing, spring, decay, parallel, sequence, stagger, loop, delay, add, subtract, multiply, divide, modulo, diffClamp, event, forkEvent, unforkEvent, } from '@symbiote-native/engine';
|
|
29
|
-
import
|
|
30
|
-
import
|
|
31
|
-
import
|
|
32
|
-
import
|
|
19
|
+
import View from '../../components/View.svelte';
|
|
20
|
+
import Text from '../../components/Text.svelte';
|
|
21
|
+
import Image from '../../components/image/index.svelte';
|
|
22
|
+
import ScrollView from '../../components/scroll-view/index.svelte';
|
|
23
|
+
import FlatList from '../../components/flat-list/index.svelte';
|
|
24
|
+
import SectionList from '../../components/section-list/index.svelte';
|
|
25
|
+
import { createAnimatedComponent } from './create-animated-component.js';
|
|
26
|
+
export { createAnimatedComponent } from './create-animated-component.js';
|
|
33
27
|
// The live, JS-driven driver namespace (real frames). RN's AnimatedImplementation.
|
|
34
28
|
const liveDrivers = {
|
|
35
29
|
Value: AnimatedValue,
|
|
@@ -56,14 +50,20 @@ const liveDrivers = {
|
|
|
56
50
|
};
|
|
57
51
|
// RN swaps the WHOLE driver namespace for the mock when the host reports isDisableAnimations
|
|
58
52
|
// (reduced motion / test env): the mock keeps the same surface but jumps each animation to its
|
|
59
|
-
// final value synchronously, no frames. The animated COMPONENTS (View/Text/Image/ScrollView
|
|
60
|
-
// live in both branches; only the drivers/value/operators/events half
|
|
53
|
+
// final value synchronously, no frames. The animated COMPONENTS (View/Text/Image/ScrollView/
|
|
54
|
+
// FlatList/SectionList) are live in both branches; only the drivers/value/operators/events half
|
|
55
|
+
// is swapped, exactly like
|
|
61
56
|
// RN spreading `...Animated` (impl or mock) over the same component references.
|
|
62
57
|
const drivers = Platform.isDisableAnimations ? AnimatedMock : liveDrivers;
|
|
58
|
+
// Wrapped eagerly, unlike Vue's lazy getters: nothing in the scroll/list chain imports THIS
|
|
59
|
+
// module back (sticky-header builds its own Animated.View straight from the wrap), so there is
|
|
60
|
+
// no half-evaluated cycle to dodge.
|
|
63
61
|
export const Animated = {
|
|
64
|
-
View:
|
|
65
|
-
Text:
|
|
66
|
-
Image:
|
|
67
|
-
ScrollView:
|
|
62
|
+
View: createAnimatedComponent(View),
|
|
63
|
+
Text: createAnimatedComponent(Text),
|
|
64
|
+
Image: createAnimatedComponent(Image),
|
|
65
|
+
ScrollView: createAnimatedComponent(ScrollView),
|
|
66
|
+
FlatList: createAnimatedComponent(FlatList),
|
|
67
|
+
SectionList: createAnimatedComponent(SectionList),
|
|
68
68
|
...drivers,
|
|
69
69
|
};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// in a plain sibling `.ts` file rather than inside `index.svelte` itself — same pattern as
|
|
8
8
|
// components/image/index.ts.
|
|
9
9
|
import StatusBarComponent from './index.svelte';
|
|
10
|
-
import { statusBarImperative, statusBarCurrentHeight } from '@symbiote-native/engine';
|
|
10
|
+
import { statusBarImperative, statusBarCurrentHeight, } from '@symbiote-native/engine';
|
|
11
11
|
const StatusBarWithStatics = Object.assign(StatusBarComponent, statusBarImperative);
|
|
12
12
|
// Android exposes the bar height as a native constant; undefined on iOS / when absent. Read
|
|
13
13
|
// lazily (getter) so nothing touches native at import time; the Android engine impl resolves
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
// Unlike Vue's twin (status-bar.ts), no untyped-attrs guard layer is needed: Svelte's
|
|
11
11
|
// `$props()` already destructures real typed fields straight from the compiler, so there is
|
|
12
12
|
// no kebab-case-attrs normalization step to run first.
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
applyStatusBarProps,
|
|
15
|
+
type IStatusBarProps,
|
|
16
|
+
} from '@symbiote-native/engine';
|
|
14
17
|
|
|
15
18
|
let props: IStatusBarProps = $props();
|
|
16
19
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { mount, unmount } from './render';
|
|
2
|
-
export { mountDescriptorChildren, createDescriptorChildrenSync } from './descriptor-to-svelte';
|
|
2
|
+
export { mountDescriptorChildren, createDescriptorChildrenSync, } from './descriptor-to-svelte';
|
|
3
3
|
export type { IDescriptorChildrenMount } from './descriptor-to-svelte';
|
|
4
4
|
export type { ShimElement } from './dom-shim';
|
|
@@ -24,4 +24,4 @@
|
|
|
24
24
|
// 'RNCSlider') that cannot be written as a literal Svelte template tag — see
|
|
25
25
|
// packages/slider/src/svelte/slider/index.svelte's header for the full reasoning.
|
|
26
26
|
export { mount, unmount } from './render.js';
|
|
27
|
-
export { mountDescriptorChildren, createDescriptorChildrenSync } from './descriptor-to-svelte.js';
|
|
27
|
+
export { mountDescriptorChildren, createDescriptorChildrenSync, } from './descriptor-to-svelte.js';
|
|
@@ -23,12 +23,18 @@
|
|
|
23
23
|
// a whitespace-only node with a newline gets deleted; a same-line run of 2+ spaces collapses to
|
|
24
24
|
// one and stays, matching HTML's own collapse of an intentional double space.
|
|
25
25
|
//
|
|
26
|
-
//
|
|
27
|
-
// (`<View><A/> <B/></View>`)
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// this
|
|
26
|
+
// Hazard 2 is no longer THIS file's guarantee. A whitespace-only run between two siblings on
|
|
27
|
+
// ONE line (`<View><A/> <B/></View>`) is indistinguishable here from an intentional space -
|
|
28
|
+
// the missing fact is whether the parent renders raw text, which source alone cannot say.
|
|
29
|
+
// `dom-shim/text.ts` knows it: by the time a text node goes live its parent's engine node is
|
|
30
|
+
// bound, so it drops whitespace-only text under any non-text parent. That is exact, and it
|
|
31
|
+
// holds even for a consumer who never registers this preprocessor.
|
|
32
|
+
//
|
|
33
|
+
// So deleting the cross-line gap here is now an OPTIMIZATION - the node is never built at all -
|
|
34
|
+
// plus source hygiene for svelte-check. Hazard 1 (a wrapped sentence INSIDE one text node) does
|
|
35
|
+
// still rely on this file: it needs collapsing rather than dropping, and only source can tell an
|
|
36
|
+
// authored newline from one a runtime value carries. `scripts/audit-svelte-stray-whitespace.mjs`
|
|
37
|
+
// still walks compiled output independently of both.
|
|
32
38
|
//
|
|
33
39
|
// Registered in svelte.config.js's `preprocess`, same as forbid-web-only-constructs.ts and
|
|
34
40
|
// scoped-styles.ts, so it covers both svelte-check/the language server and Metro's build.
|
|
@@ -72,6 +78,11 @@ function nestedNodes(node) {
|
|
|
72
78
|
}
|
|
73
79
|
return nested;
|
|
74
80
|
}
|
|
81
|
+
// Narrower than the shim's WHITESPACE_ONLY, on purpose - do NOT "unify" them. This class drives
|
|
82
|
+
// COLLAPSING runs inside real text content, where ` ` / ` ` are a deliberate character
|
|
83
|
+
// an author typed, not formatting; folding them into a plain space would change what renders.
|
|
84
|
+
// The shim can be wider because it only ever DROPS, and only under a parent that cannot paint
|
|
85
|
+
// text at all. Same reason Svelte's own regex_not_whitespace excludes nbsp.
|
|
75
86
|
const WHITESPACE_RUN = /[ \t\r\n]+/g;
|
|
76
87
|
function collectEdits(nodes, edits) {
|
|
77
88
|
for (const node of nodes) {
|
|
@@ -99,7 +110,9 @@ function textEdit(start, end, data) {
|
|
|
99
110
|
if (collapsed.trim() === '') {
|
|
100
111
|
// Whitespace-only node: delete it only if the original spanned a line break, else leave it
|
|
101
112
|
// as the single collapsed space.
|
|
102
|
-
return data.includes('\n')
|
|
113
|
+
return data.includes('\n')
|
|
114
|
+
? { start, end, text: '' }
|
|
115
|
+
: { start, end, text: collapsed };
|
|
103
116
|
}
|
|
104
117
|
if (collapsed === data)
|
|
105
118
|
return undefined;
|
|
@@ -33,8 +33,14 @@ const SPECIAL_ELEMENT_ADVICE = 'has no meaning under React Native and is silentl
|
|
|
33
33
|
'(Dimensions / AppState / StatusBar / …).';
|
|
34
34
|
const FORBIDDEN = new Map([
|
|
35
35
|
['SvelteHead', { label: '<svelte:head>', advice: SPECIAL_ELEMENT_ADVICE }],
|
|
36
|
-
[
|
|
37
|
-
|
|
36
|
+
[
|
|
37
|
+
'SvelteWindow',
|
|
38
|
+
{ label: '<svelte:window>', advice: SPECIAL_ELEMENT_ADVICE },
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
'SvelteDocument',
|
|
42
|
+
{ label: '<svelte:document>', advice: SPECIAL_ELEMENT_ADVICE },
|
|
43
|
+
],
|
|
38
44
|
['SvelteBody', { label: '<svelte:body>', advice: SPECIAL_ELEMENT_ADVICE }],
|
|
39
45
|
[
|
|
40
46
|
'HtmlTag',
|