@symbiote-native/svelte 0.3.0 → 2.0.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.
Files changed (107) hide show
  1. package/README.md +8 -10
  2. package/build/components/{activity-indicator/activity-indicator-props.d.ts → activity-indicator-props.d.ts} +1 -1
  3. package/build/components/flat-list/index.svelte +4 -4
  4. package/build/components/{image-background/image-background-props.d.ts → image-background-props.d.ts} +1 -1
  5. package/build/components/index.d.ts +6 -24
  6. package/build/components/index.js +0 -18
  7. package/build/components/keyboard-avoiding-view/index.svelte +6 -6
  8. package/build/components/modal/index.svelte +7 -7
  9. package/build/components/pressable/pressable-props.d.ts +1 -0
  10. package/build/components/refresh-control-props.d.ts +1 -0
  11. package/build/components/safe-area-view-props.d.ts +1 -0
  12. package/build/components/text-props.d.ts +1 -0
  13. package/build/components/virtualized-list/index.svelte +71 -250
  14. package/build/dom-shim/canonical-prop-names.d.ts +2 -0
  15. package/build/dom-shim/canonical-prop-names.js +320 -0
  16. package/build/dom-shim/document.js +1 -1
  17. package/build/dom-shim/element.d.ts +17 -5
  18. package/build/dom-shim/element.js +258 -40
  19. package/build/dom-shim/fold-host-bag.d.ts +1 -0
  20. package/build/dom-shim/fold-host-bag.js +7 -0
  21. package/build/dom-shim/patch-globals.js +35 -7
  22. package/build/dom-shim/shim-node.js +11 -0
  23. package/build/dom-shim/style-cache.d.ts +1 -0
  24. package/build/dom-shim/style-cache.js +27 -0
  25. package/build/dom-shim/text.js +2 -2
  26. package/build/host-instance.d.ts +2 -3
  27. package/build/host-instance.js +9 -4
  28. package/build/index.d.ts +8 -4
  29. package/build/index.js +21 -5
  30. package/build/intrinsic-elements.d.ts +63 -0
  31. package/build/modules/animated/index.d.ts +7 -27
  32. package/build/modules/animated/index.js +24 -34
  33. package/build/modules/image/index.d.ts +3 -0
  34. package/build/modules/image/index.js +15 -0
  35. package/build/preprocessor/scoped-styles.js +1 -1
  36. package/build/register.d.ts +1 -0
  37. package/build/register.js +15 -0
  38. package/build/root-element.js +1 -1
  39. package/metro-css-parser.cjs +2 -1
  40. package/metro-svelte-transformer.cjs +30 -12
  41. package/package.json +19 -9
  42. package/build/components/RefreshControl.svelte +0 -40
  43. package/build/components/RefreshControl.svelte.d.ts +0 -5
  44. package/build/components/SafeAreaView.svelte +0 -34
  45. package/build/components/SafeAreaView.svelte.d.ts +0 -5
  46. package/build/components/Text.svelte +0 -34
  47. package/build/components/Text.svelte.d.ts +0 -5
  48. package/build/components/View.svelte +0 -43
  49. package/build/components/View.svelte.d.ts +0 -5
  50. package/build/components/activity-indicator/activity-indicator-platform.android.d.ts +0 -3
  51. package/build/components/activity-indicator/activity-indicator-platform.android.js +0 -5
  52. package/build/components/activity-indicator/activity-indicator-platform.d.ts +0 -1
  53. package/build/components/activity-indicator/activity-indicator-platform.ios.d.ts +0 -3
  54. package/build/components/activity-indicator/activity-indicator-platform.ios.js +0 -6
  55. package/build/components/activity-indicator/activity-indicator-platform.js +0 -5
  56. package/build/components/activity-indicator/index.svelte +0 -63
  57. package/build/components/activity-indicator/index.svelte.d.ts +0 -5
  58. package/build/components/button.svelte +0 -76
  59. package/build/components/button.svelte.d.ts +0 -5
  60. package/build/components/image/image-logic.d.ts +0 -2
  61. package/build/components/image/image-logic.js +0 -135
  62. package/build/components/image/index.d.ts +0 -3
  63. package/build/components/image/index.js +0 -10
  64. package/build/components/image/index.svelte +0 -66
  65. package/build/components/image/index.svelte.d.ts +0 -5
  66. package/build/components/image-background/index.svelte +0 -87
  67. package/build/components/image-background/index.svelte.d.ts +0 -5
  68. package/build/components/input-accessory-view/index.svelte +0 -51
  69. package/build/components/input-accessory-view/index.svelte.d.ts +0 -5
  70. package/build/components/pressable/index.svelte +0 -167
  71. package/build/components/pressable/index.svelte.d.ts +0 -5
  72. package/build/components/scroll-view/index.svelte +0 -331
  73. package/build/components/scroll-view/index.svelte.d.ts +0 -18
  74. package/build/components/scroll-view/scroll-view-platform-types.d.ts +0 -3
  75. package/build/components/scroll-view/scroll-view-platform.android.d.ts +0 -3
  76. package/build/components/scroll-view/scroll-view-platform.android.js +0 -3
  77. package/build/components/scroll-view/scroll-view-platform.d.ts +0 -1
  78. package/build/components/scroll-view/scroll-view-platform.ios.d.ts +0 -3
  79. package/build/components/scroll-view/scroll-view-platform.ios.js +0 -3
  80. package/build/components/scroll-view/scroll-view-platform.js +0 -4
  81. package/build/components/scroll-view/scroll-view-sticky-context.d.ts +0 -7
  82. package/build/components/scroll-view/scroll-view-sticky-context.js +0 -1
  83. package/build/components/scroll-view/sticky-header-props.d.ts +0 -10
  84. package/build/components/scroll-view/sticky-header-props.js +0 -1
  85. package/build/components/scroll-view/sticky-header.svelte +0 -287
  86. package/build/components/scroll-view/sticky-header.svelte.d.ts +0 -5
  87. package/build/components/switch/index.svelte +0 -134
  88. package/build/components/switch/index.svelte.d.ts +0 -5
  89. package/build/components/text-input/index.svelte +0 -316
  90. package/build/components/text-input/index.svelte.d.ts +0 -12
  91. package/build/components/touchable-highlight/index.svelte +0 -138
  92. package/build/components/touchable-highlight/index.svelte.d.ts +0 -5
  93. package/build/components/touchable-native-feedback/index.d.ts +0 -9
  94. package/build/components/touchable-native-feedback/index.js +0 -14
  95. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte +0 -49
  96. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte.d.ts +0 -5
  97. package/build/components/touchable-opacity/index.svelte +0 -180
  98. package/build/components/touchable-opacity/index.svelte.d.ts +0 -5
  99. package/build/components/touchable-without-feedback/index.svelte +0 -74
  100. package/build/components/touchable-without-feedback/index.svelte.d.ts +0 -5
  101. package/build/modules/animated/animated-component-props.d.ts +0 -7
  102. package/build/modules/animated/animated-component-props.js +0 -1
  103. package/build/modules/animated/create-animated-component.d.ts +0 -5
  104. package/build/modules/animated/create-animated-component.js +0 -131
  105. /package/build/components/{activity-indicator/activity-indicator-props.js → activity-indicator-props.js} +0 -0
  106. /package/build/components/{image-background/image-background-props.js → image-background-props.js} +0 -0
  107. /package/build/{components/scroll-view/scroll-view-platform-types.js → intrinsic-elements.js} +0 -0
package/build/index.js CHANGED
@@ -9,9 +9,18 @@
9
9
  // `descriptorToSvelte` bridge was ever needed — each component below is hand-authored Svelte
10
10
  // markup mirroring its render-*.ts, reusing only the pure state/render logic. See skill §17/§18
11
11
  // for the one open async-exception repro and the honest cross-cutting gap list.
12
+ // Bare side-effect import, deliberately NOT a re-export and deliberately not beside one of the
13
+ // same specifier: it registers the press behavior with the engine's host-behavior registry. Any
14
+ // other shape is dropped by Metro's inlineRequires in a release build — see register.ts.
15
+ import './register.js';
12
16
  export { mount, unmount } from './render.js';
13
17
  export { AppRegistry, setHostRegistrar } from './modules/app-registry/index.js';
14
- export { View, Text, ActivityIndicator, Image, ImageBackground, InputAccessoryView, KeyboardAvoidingView, Switch, TextInput, Modal, SafeAreaView, RefreshControl, Pressable, TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, TouchableNativeFeedback, Button, ScrollView, ScrollViewStickyHeader, VirtualizedList, FlatList, VirtualizedSectionList, SectionList, } from './components/index.js';
18
+ // Nine names are deliberately absent and there is nothing to import in their place: `view`, `text`,
19
+ // `safe-area-view`, `pressable`, `text-input`, `switch`, `image`, `input-accessory-view` and
20
+ // `activity-indicator` are INTRINSIC TAGS an app writes directly. What survives of `Image` and
21
+ // `TouchableNativeFeedback` is a STATICS namespace each, exported with the other imperative
22
+ // namespaces below. See ./components/index.ts for where each deleted wrapper's body went.
23
+ export { KeyboardAvoidingView, Modal, VirtualizedList, FlatList, VirtualizedSectionList, SectionList, } from './components/index.js';
15
24
  // Component-scoped agnostic types that live in @symbiote-native/components but weren't yet
16
25
  // threaded through this barrel (parity fix, see React's index.ts lines 55-58, 89, 245-250).
17
26
  export { setImageSourceResolver } from '@symbiote-native/components';
@@ -20,6 +29,13 @@ export { setImageSourceResolver } from '@symbiote-native/components';
20
29
  // React/Vue's own barrels) until 2026-08-12 — a pure engine re-export, no framework glue, so
21
30
  // adding it is a one-line parity fix, not new adapter work.
22
31
  export { Platform, StyleSheet, PlatformColor, DynamicColorIOS, processColor, setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, PixelRatio, PanResponder, dlog, isDebug, } from '@symbiote-native/engine';
32
+ // The two namespaces left behind by primitives that became tags, here rather than in the component
33
+ // block above for one reason: they carry no view. `Image` gets an adapter file only because it
34
+ // renames the shared `imageStatics`; TNF's four are pure dict producers and come straight from the
35
+ // shared package. RN points NEW code at `<pressable p={{ android_ripple: {…} }}>`, but they are not
36
+ // redundant — the `touchable-native-feedback` tag folds `background`, and nothing else builds it.
37
+ export { Image } from './modules/image/index.js';
38
+ export { TouchableNativeFeedback } from '@symbiote-native/components';
23
39
  // Imperative runtime modules: the SAME module every adapter shares, re-exported straight from
24
40
  // @symbiote-native/engine so app code names only @symbiote-native/svelte (RN's single import
25
41
  // root). Nothing here has a visual or a lifecycle, so there is no Svelte half to write.
@@ -76,7 +92,7 @@ export { orientation, createWidthQuery } from './runes/media-query.js';
76
92
  // IClassNameValue — plus the normalizer, for a consumer building a class value in its own
77
93
  // helper. See class-value.ts's header for the object-is-a-style vs object-is-a-clsx-map rule.
78
94
  export { normalizeSvelteClass, resolveSvelteClass, } from './class-value.js';
79
- // Animated: the Svelte twin of adapters/vue/src/modules/animated. All six RN animated components
80
- // ship (View/Text/Image/ScrollView/FlatList/SectionList), all of them from the same generic
81
- // createAnimatedComponent() wrap React, Vue and Solid also export.
82
- export { Animated, createAnimatedComponent } from './modules/animated/index.js';
95
+ // Animated: the drivers, plus FlatList/SectionList as ALIASES of the plain components. There is no
96
+ // `createAnimatedComponent` — the engine resolves an AnimatedNode in any prop of any host node, so
97
+ // nothing needs wrapping (modules/animated/index.ts).
98
+ export { Animated } from './modules/animated/index.js';
@@ -0,0 +1,63 @@
1
+ import type { ISymbioteIntrinsic, ICrossTypedIntrinsics } from '@symbiote-native/components';
2
+ import type { IShimPropBag } from './dom-shim/element';
3
+ import type { IViewProps } from './components/view-props';
4
+ import type { ITextProps } from './components/text-props';
5
+ import type { IImageProps } from './components/image/image-props';
6
+ import type { IImageBackgroundProps } from './components/image-background-props';
7
+ import type { IPressableProps } from './components/pressable/pressable-props';
8
+ import type { IButtonProps } from './components/button-props';
9
+ import type { IActivityIndicatorProps } from './components/activity-indicator-props';
10
+ import type { ISafeAreaViewProps } from './components/safe-area-view-props';
11
+ import type { IRefreshControlProps } from './components/refresh-control-props';
12
+ import type { ITouchableOpacityProps } from './components/touchable-opacity/touchable-opacity-props';
13
+ import type { ITouchableWithoutFeedbackProps } from './components/touchable-without-feedback/touchable-without-feedback-props';
14
+ import type { ITouchableHighlightProps } from './components/touchable-highlight/touchable-highlight-props';
15
+ import type { ITouchableNativeFeedbackProps } from './components/touchable-native-feedback/touchable-native-feedback-props';
16
+ import type { IScrollViewProps } from './components/scroll-view/scroll-view-props';
17
+ import type { ISwitchProps } from './components/switch/switch-props';
18
+ import type { ITextInputProps } from './components/text-input/text-input-props';
19
+ import type { IModalProps } from './components/modal/modal-props';
20
+ import type { IInputAccessoryViewProps } from './components/input-accessory-view/input-accessory-view-props';
21
+ export interface ISymbioteHostAttributes {
22
+ p?: IShimPropBag;
23
+ [attribute: string]: unknown;
24
+ }
25
+ export interface ICrossedPrimitiveProps {
26
+ view: IViewProps;
27
+ text: ITextProps;
28
+ pressable: IPressableProps;
29
+ button: IButtonProps;
30
+ image: IImageProps;
31
+ 'image-background': IImageBackgroundProps;
32
+ 'input-accessory-view': IInputAccessoryViewProps;
33
+ 'refresh-control': IRefreshControlProps;
34
+ 'safe-area-view': ISafeAreaViewProps;
35
+ 'touchable-native-feedback': ITouchableNativeFeedbackProps;
36
+ 'touchable-opacity': ITouchableOpacityProps;
37
+ 'touchable-highlight': ITouchableHighlightProps;
38
+ 'touchable-without-feedback': ITouchableWithoutFeedbackProps;
39
+ 'scroll-view': IScrollViewProps;
40
+ switch: ISwitchProps;
41
+ 'text-input': ITextInputProps;
42
+ modal: IModalProps;
43
+ 'activity-indicator': IActivityIndicatorProps;
44
+ }
45
+ type IWithSvelteAttributeRealities<Props> = Partial<Props> & {
46
+ p?: IShimPropBag;
47
+ } & {
48
+ [K in keyof Props as K extends string ? Lowercase<K> : never]?: Props[K];
49
+ };
50
+ declare global {
51
+ namespace svelteHTML {
52
+ interface IntrinsicElements extends ICrossTypedIntrinsics<ISymbioteHostAttributes, {
53
+ [K in keyof ICrossedPrimitiveProps]: IWithSvelteAttributeRealities<ICrossedPrimitiveProps[K]>;
54
+ }> {
55
+ }
56
+ interface SVGAttributes extends ISymbioteHostAttributes {
57
+ }
58
+ interface HTMLAttributes extends ISymbioteHostAttributes {
59
+ }
60
+ }
61
+ }
62
+ export type ISymbioteIntrinsicTag = ISymbioteIntrinsic;
63
+ export {};
@@ -1,7 +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';
3
- export type { IAnimatedComponentProps } from './animated-component-props';
4
- export declare const Animated: {
2
+ declare const drivers: {
5
3
  Value: typeof AnimatedValue;
6
4
  ValueXY: typeof AnimatedValueXY;
7
5
  Color: typeof AnimatedColor;
@@ -41,28 +39,10 @@ export declare const Animated: {
41
39
  event: typeof event;
42
40
  forkEvent: typeof forkEvent;
43
41
  unforkEvent: typeof unforkEvent;
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>;
56
- ScrollView: import("svelte").Component<import("./animated-component-props").IAnimatedComponentProps, {
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>;
68
42
  };
43
+ interface IAnimatedNamespace {
44
+ FlatList: typeof import('../../components/flat-list/index.svelte').default;
45
+ SectionList: typeof import('../../components/section-list/index.svelte').default;
46
+ }
47
+ export declare const Animated: IAnimatedNamespace & typeof drivers;
48
+ export {};
@@ -1,29 +1,27 @@
1
- // The Animated namespace for @symbiote-native/svelte: the Svelte twin of
2
- // adapters/vue/src/modules/animated/index.ts (the primary reference — Svelte 5's runes are much
3
- // closer to Vue's reactivity model than to React's hooks). The value graph, easing,
4
- // interpolation and drivers come from @symbiote-native/engine (framework-agnostic, JS-driven),
5
- // spread in verbatim.
1
+ // The Animated namespace for @symbiote-native/svelte. The value graph, easing, interpolation and
2
+ // drivers come from @symbiote-native/engine (framework-agnostic, JS-driven), spread in verbatim.
6
3
  //
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.
4
+ // There is no `Animated.View` / `Animated.Text` / `Animated.Image` any more, and nothing replaced
5
+ // them: `<view style={{ opacity: someAnimatedValue }}>` is the whole API. The engine resolves an
6
+ // AnimatedNode written into any prop of any host node (`core/engine/src/animated/host-binding.ts`):
7
+ // it publishes the current value, subscribes the leaf, writes each frame through its own targeted
8
+ // setNativeProps commit, and releases at the commit sweep. `createAnimatedComponent` brokered
9
+ // between the value graph and the node's Fabric tag, and both of those were already engine-side.
14
10
  //
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.
11
+ // Those three were aliases of the wrappers, and the wrappers are gone — a primitive is a tag now,
12
+ // which is not a value `<Animated.X>` can call. `Animated.ScrollView` left the same way on
13
+ // 2026-09-10: `bindAnimatedEvent` (node.ts, from `setEventListener`) attaches a native-driven event
14
+ // on ANY host node, which was the one job a scroll wrapper still looked like it had. The whole API
15
+ // is `<scroll-view style={{ opacity: v }} p={{ onScroll: Animated.event(…) }}>`.
16
+ //
17
+ // The two below REMAIN because the LIST family is tier 3: a render prop decides their output shape
18
+ // in JS, so there is no tag for `<Animated.X>` to be an alias of.
19
+ //
20
+ // A consumer animating their OWN component wraps nothing either: any component that forwards
21
+ // `style` down to a Symbiote primitive accepts an AnimatedNode by construction.
18
22
  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';
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
23
  import FlatList from '../../components/flat-list/index.svelte';
24
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';
27
25
  // The live, JS-driven driver namespace (real frames). RN's AnimatedImplementation.
28
26
  const liveDrivers = {
29
27
  Value: AnimatedValue,
@@ -50,20 +48,12 @@ const liveDrivers = {
50
48
  };
51
49
  // RN swaps the WHOLE driver namespace for the mock when the host reports isDisableAnimations
52
50
  // (reduced motion / test env): the mock keeps the same surface but jumps each animation to its
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
56
- // RN spreading `...Animated` (impl or mock) over the same component references.
51
+ // final value synchronously, no frames. Only this half is swapped — the component names below are
52
+ // the same references either way, exactly like RN spreading `...Animated` (impl or mock) over one
53
+ // set of components.
57
54
  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.
61
55
  export const Animated = {
62
- View: createAnimatedComponent(View),
63
- Text: createAnimatedComponent(Text),
64
- Image: createAnimatedComponent(Image),
65
- ScrollView: createAnimatedComponent(ScrollView),
66
- FlatList: createAnimatedComponent(FlatList),
67
- SectionList: createAnimatedComponent(SectionList),
56
+ FlatList,
57
+ SectionList,
68
58
  ...drivers,
69
59
  };
@@ -0,0 +1,3 @@
1
+ import { type IImageStatics } from '@symbiote-native/components';
2
+ export type { IImageStatics };
3
+ export declare const Image: IImageStatics;
@@ -0,0 +1,15 @@
1
+ // RN's Image STATICS — `getSize`, `prefetch`, `queryCache`, `abortPrefetch`, `resolveAssetSource`.
2
+ //
3
+ // IN `modules/`, NOT `components/`. The primitive is the `<image>` tag and its fold runs in the
4
+ // engine (`behaviors/image.ts`); what is left under the name `Image` is an imperative API with no
5
+ // view, which is what this bucket is for — `<adapter_src_follows_framework_idioms>` names it.
6
+ // Sitting in `components/` was what made a statics object read as a component.
7
+ //
8
+ // A NAMESPACE OBJECT rather than five bare functions, deliberately. `Alert`, `Share`, `Linking`,
9
+ // `Vibration` and `AppState` are all `export const X = {…}`, an app porting from RN writes
10
+ // `Image.getSize(...)` verbatim (P0, `<adapters_reach_full_feature_parity>`), and the bundle-size
11
+ // argument for splitting them is void: Metro does no tree shaking (facebook/metro#227).
12
+ //
13
+ // Shared verbatim via `imageStatics`, the same source React's and Vue's Image use.
14
+ import { imageStatics } from '@symbiote-native/components';
15
+ export const Image = imageStatics;
@@ -5,7 +5,7 @@
5
5
  // (`generate:'client', fragments:'tree', css:'external'`), a `<style>` block behaves one of two
6
6
  // ways:
7
7
  //
8
- // <symbiote-view class="card"> -> result.css: `.card.svelte-4psua6 { … }`
8
+ // <view class="card"> -> result.css: `.card.svelte-4psua6 { … }`
9
9
  // result.js: `$.set_class(node, 1, 'card svelte-4psua6')`
10
10
  // <View class="card"> -> warning: css_unused_selector
11
11
  // result.css: `/* (unused) .card { … }*/`
@@ -0,0 +1 @@
1
+ import '@symbiote-native/components/register';
@@ -0,0 +1,15 @@
1
+ // Side-effect ONLY. Exports nothing, and must never be re-exported from the barrel.
2
+ //
3
+ // The fourteen calls live once, in `@symbiote-native/components/register` — they were identical in
4
+ // all five adapters, and each copy carried a migration ledger justifying a call against a wrapper
5
+ // that no longer exists. What is per-adapter is only the SPELLING of the import below: every
6
+ // `*-tag.test.*` here reaches the registry through `import './register';`, and this adapter's own
7
+ // barrel guard pins that line.
8
+ //
9
+ // WHY A SIDE-EFFECT SUBPATH AND NOT A BARREL EXPORT. Metro turns on `inlineRequires` for PRODUCTION
10
+ // only: it moves a `require` down to the first place its binding is used as a VALUE, and
11
+ // `export { X } from './x'` compiles to a lazy getter. A registration reached that way never
12
+ // evaluates in a Release build — invisible to tsc, to vitest, and to grepping the bundle. A bare
13
+ // `import` placed NEXT TO a re-export of the same specifier does not help either: Babel merges the
14
+ // two into one dependency and the merged dependency stays lazy.
15
+ import '@symbiote-native/components/register';
@@ -9,7 +9,7 @@
9
9
  // normally an ancestor's `insertOne` does that. For the root there is no ancestor shim node;
10
10
  // the surface plays that role, so this file makes the one `surface.appendChild` call directly.
11
11
  import { ShimElement } from './dom-shim/index.js';
12
- const ROOT_INTRINSIC = 'symbiote-view';
12
+ const ROOT_INTRINSIC = 'view';
13
13
  // Unlike Vue/React, whose app root mounts directly onto the surface (its own flex:1
14
14
  // class/style reaches the engine's synthetic flex:1 AppContainer with nothing in between),
15
15
  // Svelte's compiled output needs a real DOM-like `target` to call appendChild on — this
@@ -6,4 +6,5 @@
6
6
  // already handles standalone style files itself (its isStyleFile branch), so this subpath exists
7
7
  // only for parity with the other adapters and for a future consumer that wants CSS-only handling
8
8
  // without the .svelte transform (e.g. importing a plain .css from non-.svelte source).
9
- module.exports = require('@symbiote-native/css-parser').createCssMetroTransformer();
9
+ module.exports =
10
+ require('@symbiote-native/css-parser').createCssMetroTransformer();
@@ -35,7 +35,11 @@ const upstreamTransformer = resolveUpstreamTransformer();
35
35
  // svelte-check, the language server, Vite), while this file is `require()`d by Metro directly
36
36
  // and must stay pure CommonJS with no build step of its own, exactly like
37
37
  // metro-vue-transformer.cjs.
38
- const COMPILER_OPTIONS = { fragments: 'tree', css: 'external', generate: 'client' };
38
+ const COMPILER_OPTIONS = {
39
+ fragments: 'tree',
40
+ css: 'external',
41
+ generate: 'client',
42
+ };
39
43
 
40
44
  // The web-only-construct guard (svelte-adapter-dom-shim skill §7/§22) RUNS HERE, on every
41
45
  // `.svelte` Metro compiles — not just via svelte-check/the language server. `{@html}` compiles
@@ -50,8 +54,8 @@ const COMPILER_OPTIONS = { fragments: 'tree', css: 'external', generate: 'client
50
54
  // `require()` cannot pull an ESM/TS module, but `transform` is already async.
51
55
  let preprocessorPromise;
52
56
  function webOnlyConstructGuard() {
53
- preprocessorPromise ??= import('@symbiote-native/svelte/preprocessor').then(mod =>
54
- mod.forbidWebOnlyConstructs(),
57
+ preprocessorPromise ??= import('@symbiote-native/svelte/preprocessor').then(
58
+ mod => mod.forbidWebOnlyConstructs(),
55
59
  );
56
60
  return preprocessorPromise;
57
61
  }
@@ -63,8 +67,8 @@ function webOnlyConstructGuard() {
63
67
  // src/preprocessor/scoped-styles.ts for why Svelte's own `result.css` cannot be used instead.
64
68
  let scopedStylesPromise;
65
69
  function scopedStylesPreprocessor() {
66
- scopedStylesPromise ??= import('@symbiote-native/svelte/scoped-styles').then(mod =>
67
- mod.scopedStyles(),
70
+ scopedStylesPromise ??= import('@symbiote-native/svelte/scoped-styles').then(
71
+ mod => mod.scopedStyles(),
68
72
  );
69
73
  return scopedStylesPromise;
70
74
  }
@@ -75,11 +79,13 @@ function scopedStylesPreprocessor() {
75
79
  // renders as a forced line break on device. Loaded lazily for the same reason as the two above.
76
80
  let collapseTextWhitespacePromise;
77
81
  function collapseTextWhitespacePreprocessor() {
78
- collapseTextWhitespacePromise ??= import('@symbiote-native/svelte/collapse-text-whitespace').then(
79
- mod => mod.collapseTextWhitespace(),
80
- );
82
+ collapseTextWhitespacePromise ??=
83
+ import('@symbiote-native/svelte/collapse-text-whitespace').then(mod =>
84
+ mod.collapseTextWhitespace(),
85
+ );
81
86
  return collapseTextWhitespacePromise;
82
87
  }
88
+
83
89
  //
84
90
  // Svelte 5's compiler strips <script lang="ts"> types structurally, with no external type
85
91
  // resolution needed (unlike @vue/compiler-sfc's compileScript, which needs registerTS + a real
@@ -108,13 +114,19 @@ function compileSvelteFile(src, filename) {
108
114
  function stripTypeScript(src, filename) {
109
115
  const { outputText } = ts.transpileModule(src, {
110
116
  fileName: filename,
111
- compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ESNext },
117
+ compilerOptions: {
118
+ module: ts.ModuleKind.ESNext,
119
+ target: ts.ScriptTarget.ESNext,
120
+ },
112
121
  });
113
122
  return outputText;
114
123
  }
115
124
 
116
125
  function compileSvelteModuleFile(src, filename) {
117
- const { js } = compileModule(stripTypeScript(src, filename), { generate: 'client', filename });
126
+ const { js } = compileModule(stripTypeScript(src, filename), {
127
+ generate: 'client',
128
+ filename,
129
+ });
118
130
  return js.code;
119
131
  }
120
132
 
@@ -127,7 +139,10 @@ module.exports.transform = async function transform(params) {
127
139
  if (params.filename.endsWith('.svelte')) {
128
140
  // Throws with a message naming the RN alternative. Deliberately BEFORE compile(), so the
129
141
  // author sees the real diagnosis rather than a downstream symptom.
130
- (await webOnlyConstructGuard()).markup({ content: params.src, filename: params.filename });
142
+ (await webOnlyConstructGuard()).markup({
143
+ content: params.src,
144
+ filename: params.filename,
145
+ });
131
146
  const stylePreprocessed = await (
132
147
  await scopedStylesPreprocessor()
133
148
  ).markup({ content: params.src, filename: params.filename });
@@ -143,7 +158,10 @@ module.exports.transform = async function transform(params) {
143
158
  filename: params.filename + '.tsx',
144
159
  });
145
160
  }
146
- if (params.filename.endsWith('.svelte.ts') || params.filename.endsWith('.svelte.js')) {
161
+ if (
162
+ params.filename.endsWith('.svelte.ts') ||
163
+ params.filename.endsWith('.svelte.js')
164
+ ) {
147
165
  const code = compileSvelteModuleFile(params.src, params.filename);
148
166
  return upstreamTransformer.transform({
149
167
  ...params,
package/package.json CHANGED
@@ -1,7 +1,17 @@
1
1
  {
2
2
  "name": "@symbiote-native/svelte",
3
- "version": "0.3.0",
3
+ "version": "2.0.0",
4
4
  "description": "SymbioteNative's Svelte adapter — a DOM shim over stock compiled Svelte output driving real native iOS/Android views through the same engine as every other adapter.",
5
+ "keywords": [
6
+ "react-native",
7
+ "symbiote-native",
8
+ "svelte",
9
+ "fabric",
10
+ "cross-platform",
11
+ "ios",
12
+ "android",
13
+ "renderer"
14
+ ],
5
15
  "license": "MIT",
6
16
  "repository": {
7
17
  "type": "git",
@@ -63,22 +73,22 @@
63
73
  },
64
74
  "dependencies": {
65
75
  "typescript": "~6.0.0",
66
- "@symbiote-native/components": "0.5.0",
67
- "@symbiote-native/css-parser": "0.4.0"
76
+ "@symbiote-native/components": "2.0.0",
77
+ "@symbiote-native/css-parser": "0.5.0"
68
78
  },
69
79
  "peerDependencies": {
70
- "@symbiote-native/engine": ">=0.1.7",
71
80
  "svelte": ">=5.56.0",
72
- "react-native": ">=0.86"
81
+ "react-native": ">=0.86",
82
+ "@symbiote-native/engine": "^0.5.0"
73
83
  },
74
84
  "devDependencies": {
75
85
  "@vue/runtime-core": "^3.5.13",
76
86
  "react": "19.2.3",
77
87
  "svelte": "^5.56.0",
78
- "@symbiote-native/engine": "0.3.0",
79
- "@symbiote-native/react": "0.4.0",
80
- "@symbiote-native/test-utils": "0.2.0",
81
- "@symbiote-native/vue": "0.5.0"
88
+ "@symbiote-native/engine": "0.5.0",
89
+ "@symbiote-native/react": "2.0.0",
90
+ "@symbiote-native/test-utils": "0.3.0",
91
+ "@symbiote-native/vue": "2.0.0"
82
92
  },
83
93
  "scripts": {
84
94
  "typecheck": "tsc --build",
@@ -1,40 +0,0 @@
1
- <script lang="ts" module>
2
- // RefreshControl primitive. On iOS this is the PullToRefreshView Fabric node that lives INSIDE
3
- // a ScrollView (a sibling of the content container), giving the pull-to-refresh gesture. On
4
- // Android it is AndroidSwipeRefreshLayout and WRAPS the scroll view (a childless-on-iOS,
5
- // wraps-on-Android shape — see refresh-control-props.ts). `refreshing` is a controlled prop:
6
- // the parent owns it and pushes it down each commit; native reports the gesture via the direct
7
- // `topRefresh` event, which the engine routes to the host's `onRefresh` bag entry.
8
- import type { IRefreshControlProps } from './refresh-control-props';
9
-
10
- export type { IRefreshControlProps };
11
- </script>
12
-
13
- <script lang="ts">
14
- import { dlog } from '@symbiote-native/engine';
15
- import { resolveAccessibilityProps } from '@symbiote-native/components';
16
- import { createAttachmentsSync } from '../runes/attachments';
17
- import type { ShimElement } from '../dom-shim';
18
-
19
- let { children, ...rest }: IRefreshControlProps = $props();
20
-
21
- dlog('RefreshControl -> PullToRefreshView');
22
- dlog(`RefreshControl refreshing=${String(rest.refreshing)}`);
23
- if (rest.enabled !== undefined)
24
- dlog(`RefreshControl enabled=${String(rest.enabled)} (Android-only)`);
25
- if (rest.onRefresh !== undefined)
26
- dlog('RefreshControl onRefresh listener wired');
27
-
28
- const bag = $derived(resolveAccessibilityProps(rest));
29
-
30
- // See View.svelte's note on `{@attach}`.
31
- let hostShim = $state.raw<ShimElement | null>(null);
32
- const syncAttachments = createAttachmentsSync();
33
- $effect(() => {
34
- syncAttachments(hostShim, rest);
35
- });
36
- </script>
37
-
38
- <symbiote-refresh-control p={bag} bind:this={hostShim}>
39
- {@render children?.()}
40
- </symbiote-refresh-control>
@@ -1,5 +0,0 @@
1
- import type { IRefreshControlProps } from './refresh-control-props';
2
- export type { IRefreshControlProps };
3
- declare const RefreshControl: import("svelte").Component<IRefreshControlProps, {}, "">;
4
- type RefreshControl = ReturnType<typeof RefreshControl>;
5
- export default RefreshControl;
@@ -1,34 +0,0 @@
1
- <script lang="ts" module>
2
- // SafeAreaView primitive. A plain view whose native side insets its children to the safe area
3
- // (notch, rounded corners, system bars). There is no JS-side translation; RN just renders the
4
- // native RCTSafeAreaView and lets the host do the inset math, so this maps style + children
5
- // straight onto the intrinsic — same object-bag pattern as View.svelte.
6
- import type { ISafeAreaViewProps } from './safe-area-view-props';
7
-
8
- export type { ISafeAreaViewProps };
9
- </script>
10
-
11
- <script lang="ts">
12
- import { dlog } from '@symbiote-native/engine';
13
- import { resolveAccessibilityProps } from '@symbiote-native/components';
14
-
15
- import { createAttachmentsSync } from '../runes/attachments';
16
- import type { ShimElement } from '../dom-shim';
17
-
18
- let { children, ...rest }: ISafeAreaViewProps = $props();
19
-
20
- dlog('SafeAreaView -> SafeAreaView');
21
-
22
- const bag = $derived(resolveAccessibilityProps(rest));
23
-
24
- // See View.svelte's note on `{@attach}`.
25
- let hostShim = $state.raw<ShimElement | null>(null);
26
- const syncAttachments = createAttachmentsSync();
27
- $effect(() => {
28
- syncAttachments(hostShim, rest);
29
- });
30
- </script>
31
-
32
- <symbiote-safe-area-view p={bag} bind:this={hostShim}>
33
- {@render children?.()}
34
- </symbiote-safe-area-view>
@@ -1,5 +0,0 @@
1
- import type { ISafeAreaViewProps } from './safe-area-view-props';
2
- export type { ISafeAreaViewProps };
3
- declare const SafeAreaView: import("svelte").Component<ISafeAreaViewProps, {}, "">;
4
- type SafeAreaView = ReturnType<typeof SafeAreaView>;
5
- export default SafeAreaView;
@@ -1,34 +0,0 @@
1
- <script lang="ts" module>
2
- // See View.svelte's header comment — same object-bag design (svelte-adapter-dom-shim
3
- // skill §3g(c)). No IResponderProps here, matching every other adapter's ITextProps.
4
- import type { ITextProps } from './text-props';
5
-
6
- export type { ITextProps };
7
- </script>
8
-
9
- <script lang="ts">
10
- import { resolveTextProps } from '@symbiote-native/components';
11
- import { createAttachmentsSync } from '../runes/attachments';
12
- import type { ShimElement } from '../dom-shim';
13
-
14
- let { children, ...rest }: ITextProps = $props();
15
-
16
- // See View.svelte's note: `{@attach}` arrives as a symbol-keyed entry in the same rest object.
17
- let hostShim = $state.raw<ShimElement | null>(null);
18
- const syncAttachments = createAttachmentsSync();
19
- $effect(() => {
20
- syncAttachments(hostShim, rest);
21
- });
22
-
23
- // A FRESH object per pass, exactly as View.svelte builds its own: ShimElement's `p` setter
24
- // diffs the incoming bag against the one it stored last time, so handing it the SAME live rest
25
- // proxy twice compares that object with itself and applies nothing — every prop update after
26
- // mount was silently dropped.
27
- // resolveTextProps carries RN's Text.js defaults (ellipsizeMode 'tail', allowFontScaling
28
- // true); without them native falls back to `clip` and a clamped Text cuts mid-word.
29
- const bag = $derived(resolveTextProps({ ...rest }));
30
- </script>
31
-
32
- <symbiote-text p={bag} bind:this={hostShim}>
33
- {@render children?.()}
34
- </symbiote-text>
@@ -1,5 +0,0 @@
1
- import type { ITextProps } from './text-props';
2
- export type { ITextProps };
3
- declare const Text: import("svelte").Component<ITextProps, {}, "">;
4
- type Text = ReturnType<typeof Text>;
5
- export default Text;
@@ -1,43 +0,0 @@
1
- <script lang="ts" module>
2
- // Host primitive for app code. Per svelte-adapter-dom-shim skill §3g(c): Symbiote's
3
- // custom-element codegen path stringifies individual attributes, so unlike Vue's View
4
- // (which spreads props onto the intrinsic tag and lets patchProp route each one), this
5
- // component assembles every prop into ONE object bag and hands it to `<symbiote-view p={bag}>`
6
- // — the shim element's `p` setter diffs it per key into routeProp. `children` is pulled out
7
- // first: it is a Svelte-specific field (a Snippet), never forwarded into the bag, mirroring
8
- // why every adapter's IViewProps is declared per-adapter (CLAUDE.md
9
- // <prop_types_split_agnostic_vs_per_adapter>) rather than shared verbatim.
10
- import type { IViewProps } from './view-props';
11
-
12
- export type { IViewProps };
13
- </script>
14
-
15
- <script lang="ts">
16
- import { createAttachmentsSync } from '../runes/attachments';
17
- import type { ShimElement } from '../dom-shim';
18
-
19
- let { children, id, nativeID, ...rest }: IViewProps = $props();
20
-
21
- // `{@attach fn}` on a component lands as a SYMBOL-keyed prop, so it rides in `rest` alongside
22
- // the ordinary bag keys and never reaches the engine (routeProp only walks string keys). This
23
- // is the one directive-shaped Svelte feature that IS legal on a component, hence the only way
24
- // an author reaches the committed host node — see runes/attachments.ts.
25
- let hostShim = $state.raw<ShimElement | null>(null);
26
- const syncAttachments = createAttachmentsSync();
27
- $effect(() => {
28
- syncAttachments(hostShim, rest);
29
- });
30
-
31
- // RN's modern `id` is just a W3C-named alias for `nativeID`: View.js copies it over
32
- // (`processedProps.nativeID = id`), so `id` wins when both are set. Fold it here and drop
33
- // the raw `id` key so it never reaches Fabric (every non-function prop in the bag otherwise
34
- // passes through to the slot as-is) — mirrors React's `resolveId` in
35
- // `adapters/react/src/components.ts`, adapted to the object-bag idiom.
36
- const bag = $derived(
37
- id === undefined ? { ...rest, nativeID } : { ...rest, nativeID: id },
38
- );
39
- </script>
40
-
41
- <symbiote-view p={bag} bind:this={hostShim}>
42
- {@render children?.()}
43
- </symbiote-view>