@symbiote-native/vue 1.0.0 → 3.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.
- package/README.md +13 -12
- package/babel-jsx.cjs +26 -18
- package/build/components/activity-indicator-props.d.ts +5 -0
- package/build/components/activity-indicator-props.js +1 -0
- package/build/components/button-props.d.ts +5 -0
- package/build/components/button-props.js +1 -0
- package/build/components/flat-list/index.d.ts +3 -3
- package/build/components/flat-list/index.js +3 -3
- package/build/components/image-background-props.d.ts +7 -0
- package/build/components/image-background-props.js +1 -0
- package/build/components/image-props.d.ts +5 -0
- package/build/components/image-props.js +5 -0
- package/build/components/{input-accessory-view.d.ts → input-accessory-view-props.d.ts} +1 -3
- package/build/components/input-accessory-view-props.js +5 -0
- package/build/components/keyboard-avoiding-view.js +9 -5
- package/build/components/modal/index.js +1 -1
- package/build/components/pressable-props.d.ts +31 -0
- package/build/components/pressable-props.js +12 -0
- package/build/components/refresh-control-props.d.ts +16 -0
- package/build/components/refresh-control-props.js +9 -0
- package/build/components/safe-area-view-props.d.ts +6 -0
- package/build/components/safe-area-view-props.js +5 -0
- package/build/components/scroll-view/{shared.d.ts → scroll-view-props.d.ts} +4 -27
- package/build/components/scroll-view/scroll-view-props.js +1 -0
- package/build/components/section-list/index.d.ts +3 -3
- package/build/components/switch/switch-props.d.ts +6 -0
- package/build/components/switch/switch-props.js +1 -0
- package/build/components/text-input/text-input-props.d.ts +5 -0
- package/build/components/text-input/text-input-props.js +1 -0
- package/build/components/text-props.d.ts +24 -0
- package/build/components/text-props.js +6 -0
- package/build/components/touchable-native-feedback-props.d.ts +7 -0
- package/build/components/touchable-native-feedback-props.js +1 -0
- package/build/components/touchable-props.d.ts +17 -0
- package/build/components/touchable-props.js +1 -0
- package/build/components/touchable-without-feedback-props.d.ts +5 -0
- package/build/components/touchable-without-feedback-props.js +1 -0
- package/build/components/view-props.d.ts +29 -0
- package/build/components/view-props.js +5 -0
- package/build/components/virtualized-list/index.d.ts +3 -3
- package/build/components/virtualized-list/index.js +64 -46
- package/build/components/virtualized-section-list/index.d.ts +3 -3
- package/build/descriptor-to-vue.js +2 -2
- package/build/host-instance/index.js +2 -2
- package/build/index.d.ts +24 -27
- package/build/index.js +31 -16
- package/build/intrinsic-elements.d.ts +19 -0
- package/build/intrinsic-elements.js +1 -0
- package/build/jsx-runtime.d.ts +67 -0
- package/build/jsx-runtime.js +1 -0
- package/build/modules/animated/index.d.ts +41 -20
- package/build/modules/animated/index.js +20 -19
- package/build/modules/image/index.d.ts +3 -0
- package/build/modules/image/index.js +13 -0
- package/build/register.d.ts +1 -1
- package/build/register.js +13 -33
- package/build/renderer/index.js +108 -54
- package/build/runtime-helpers/index.d.ts +10 -1
- package/build/runtime-helpers/index.js +56 -22
- package/build/utils/normalize-attrs.js +3 -3
- package/intrinsic-tags.cjs +52 -0
- package/metro-vue-transformer.cjs +55 -345
- package/package.json +13 -9
- package/babel-lower-host-primitives.cjs +0 -225
- package/build/components/activity-indicator/index.android.d.ts +0 -4
- package/build/components/activity-indicator/index.android.js +0 -10
- package/build/components/activity-indicator/index.d.ts +0 -1
- package/build/components/activity-indicator/index.ios.d.ts +0 -4
- package/build/components/activity-indicator/index.ios.js +0 -10
- package/build/components/activity-indicator/index.js +0 -4
- package/build/components/activity-indicator/shared.d.ts +0 -8
- package/build/components/activity-indicator/shared.js +0 -48
- package/build/components/button.d.ts +0 -14
- package/build/components/button.js +0 -61
- package/build/components/image-background.d.ts +0 -9
- package/build/components/image-background.js +0 -103
- package/build/components/image.d.ts +0 -9
- package/build/components/image.js +0 -90
- package/build/components/input-accessory-view.js +0 -41
- package/build/components/pressable.d.ts +0 -58
- package/build/components/pressable.js +0 -233
- package/build/components/refresh-control.d.ts +0 -20
- package/build/components/refresh-control.js +0 -47
- package/build/components/safe-area-view.d.ts +0 -9
- package/build/components/safe-area-view.js +0 -33
- package/build/components/scroll-view/index.android.d.ts +0 -4
- package/build/components/scroll-view/index.android.js +0 -64
- package/build/components/scroll-view/index.d.ts +0 -1
- package/build/components/scroll-view/index.ios.d.ts +0 -4
- package/build/components/scroll-view/index.ios.js +0 -17
- package/build/components/scroll-view/index.js +0 -4
- package/build/components/scroll-view/shared.js +0 -271
- package/build/components/scroll-view/sticky-header.d.ts +0 -7
- package/build/components/scroll-view/sticky-header.js +0 -208
- package/build/components/switch/index.android.d.ts +0 -12
- package/build/components/switch/index.android.js +0 -13
- package/build/components/switch/index.d.ts +0 -2
- package/build/components/switch/index.ios.d.ts +0 -12
- package/build/components/switch/index.ios.js +0 -11
- package/build/components/switch/index.js +0 -5
- package/build/components/switch/shared.d.ts +0 -23
- package/build/components/switch/shared.js +0 -126
- package/build/components/text-input/index.d.ts +0 -24
- package/build/components/text-input/index.js +0 -260
- package/build/components/touchable-native-feedback.d.ts +0 -24
- package/build/components/touchable-native-feedback.js +0 -82
- package/build/components/touchable.d.ts +0 -50
- package/build/components/touchable.js +0 -281
- package/build/components.d.ts +0 -53
- package/build/components.js +0 -45
- package/build/composables/use-raw-attrs.d.ts +0 -1
- package/build/composables/use-raw-attrs.js +0 -26
- package/build/state-style.d.ts +0 -1
- package/build/state-style.js +0 -13
|
@@ -5,25 +5,7 @@ export declare const Animated: {
|
|
|
5
5
|
Value: typeof AnimatedValue;
|
|
6
6
|
ValueXY: typeof AnimatedValueXY;
|
|
7
7
|
Color: typeof AnimatedColor;
|
|
8
|
-
Easing:
|
|
9
|
-
step0(n: number): number;
|
|
10
|
-
step1(n: number): number;
|
|
11
|
-
linear(t: number): number;
|
|
12
|
-
ease(t: number): number;
|
|
13
|
-
quad(t: number): number;
|
|
14
|
-
cubic(t: number): number;
|
|
15
|
-
poly(n: number): import("@symbiote-native/engine").IEasingFunction;
|
|
16
|
-
sin(t: number): number;
|
|
17
|
-
circle(t: number): number;
|
|
18
|
-
exp(t: number): number;
|
|
19
|
-
elastic(bounciness?: number): import("@symbiote-native/engine").IEasingFunction;
|
|
20
|
-
back(s?: number): import("@symbiote-native/engine").IEasingFunction;
|
|
21
|
-
bounce(t: number): number;
|
|
22
|
-
bezier(x1: number, y1: number, x2: number, y2: number): import("@symbiote-native/engine").IEasingFunction;
|
|
23
|
-
in(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
|
|
24
|
-
out(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
|
|
25
|
-
inOut(easing: import("@symbiote-native/engine").IEasingFunction): import("@symbiote-native/engine").IEasingFunction;
|
|
26
|
-
};
|
|
8
|
+
Easing: import("@symbiote-native/engine").IEasing;
|
|
27
9
|
timing: typeof timing;
|
|
28
10
|
spring: typeof spring;
|
|
29
11
|
decay: typeof decay;
|
|
@@ -50,7 +32,46 @@ export declare const Animated: {
|
|
|
50
32
|
Image: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
51
33
|
[key: string]: any;
|
|
52
34
|
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
53
|
-
ScrollView:
|
|
35
|
+
ScrollView: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
38
|
+
FlatList: ReturnType<typeof createAnimatedComponent>;
|
|
39
|
+
SectionList: ReturnType<typeof createAnimatedComponent>;
|
|
40
|
+
createAnimatedComponent: typeof createAnimatedComponent;
|
|
41
|
+
} | {
|
|
42
|
+
Value: typeof AnimatedValue;
|
|
43
|
+
ValueXY: typeof AnimatedValueXY;
|
|
44
|
+
Color: typeof AnimatedColor;
|
|
45
|
+
Easing: import("@symbiote-native/engine").IEasing;
|
|
46
|
+
timing: (value: AnimatedValue, config: import("@symbiote-native/engine").ITimingConfig) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
47
|
+
spring: (value: AnimatedValue, config: import("@symbiote-native/engine").ISpringConfig) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
48
|
+
decay: (_value: AnimatedValue, _config: import("@symbiote-native/engine").IDecayConfig) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
49
|
+
parallel: (animations: import("@symbiote-native/engine").ICompositeAnimation[], _config?: import("@symbiote-native/engine").IParallelConfig) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
50
|
+
sequence: (animations: import("@symbiote-native/engine").ICompositeAnimation[]) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
51
|
+
stagger: (_time: number, animations: import("@symbiote-native/engine").ICompositeAnimation[]) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
52
|
+
loop: (_animation: import("@symbiote-native/engine").ICompositeAnimation) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
53
|
+
delay: (_time: number) => import("@symbiote-native/engine").ICompositeAnimation;
|
|
54
|
+
add: typeof add;
|
|
55
|
+
subtract: typeof subtract;
|
|
56
|
+
multiply: typeof multiply;
|
|
57
|
+
divide: typeof divide;
|
|
58
|
+
modulo: typeof modulo;
|
|
59
|
+
diffClamp: typeof diffClamp;
|
|
60
|
+
event: typeof event;
|
|
61
|
+
forkEvent: typeof forkEvent;
|
|
62
|
+
unforkEvent: typeof unforkEvent;
|
|
63
|
+
View: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
64
|
+
[key: string]: any;
|
|
65
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
66
|
+
Text: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
67
|
+
[key: string]: any;
|
|
68
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
69
|
+
Image: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
70
|
+
[key: string]: any;
|
|
71
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
72
|
+
ScrollView: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
73
|
+
[key: string]: any;
|
|
74
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
54
75
|
FlatList: ReturnType<typeof createAnimatedComponent>;
|
|
55
76
|
SectionList: ReturnType<typeof createAnimatedComponent>;
|
|
56
77
|
createAnimatedComponent: typeof createAnimatedComponent;
|
|
@@ -4,25 +4,29 @@
|
|
|
4
4
|
// meet here so `Animated.timing(new Animated.Value(0), ...).start()` works against the
|
|
5
5
|
// Vue-driven engine.
|
|
6
6
|
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';
|
|
7
|
-
import { View, Text } from '../../components.js';
|
|
8
|
-
import { Image } from '../../components/image.js';
|
|
9
|
-
import { ScrollView } from '../../components/scroll-view';
|
|
10
7
|
import { FlatList } from '../../components/flat-list/index.js';
|
|
11
8
|
import { SectionList } from '../../components/section-list/index.js';
|
|
12
9
|
import { createAnimatedComponent } from './create-animated-component.js';
|
|
13
10
|
export { createAnimatedComponent } from './create-animated-component.js';
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
// The TAGS, not components — there are no View/Text/Image components left to alias, and a string
|
|
12
|
+
// base is what `createAnimatedComponent` already handles (`animated-tag-base.test.ts`): it renders
|
|
13
|
+
// the intrinsic and the ref falls through to the raw engine node, the same fall-through the
|
|
14
|
+
// functional wrappers used to provide.
|
|
15
|
+
//
|
|
16
|
+
// Wrapping is itself vestigial — the engine resolves an AnimatedNode written into any prop of any
|
|
17
|
+
// host node (`core/engine/src/animated/host-binding.ts`), so `<view :style="{ opacity: v }">` needs
|
|
18
|
+
// no wrapper at all. These stay as RN-compatible aliases until the namespace is retired.
|
|
19
|
+
const AnimatedView = createAnimatedComponent('view');
|
|
20
|
+
const AnimatedText = createAnimatedComponent('text');
|
|
21
|
+
const AnimatedImage = createAnimatedComponent('image');
|
|
22
|
+
// `scroll-view` is a TAG now too (no more component, no more scroll-view/sticky-header cycle to
|
|
23
|
+
// dodge), so it takes the same eager shape as View/Text/Image above.
|
|
24
|
+
const AnimatedScrollView = createAnimatedComponent('scroll-view');
|
|
25
|
+
// LAZY, memoized getters, mirroring RN's `get FlatList()`. Kept deferred past module init even
|
|
26
|
+
// though the scroll-view cycle these once rode is gone (ScrollView above is eager now) — neither
|
|
27
|
+
// FlatList nor VirtualizedList imports this module, so there is no proven cycle left, but nothing
|
|
28
|
+
// forces one to stay absent either. Cheap insurance against a half-evaluated module handing back
|
|
29
|
+
// `undefined` and rendering nothing.
|
|
26
30
|
let animatedFlatList;
|
|
27
31
|
let animatedSectionList;
|
|
28
32
|
// The live, JS-driven driver namespace (real frames). RN's AnimatedImplementation.
|
|
@@ -58,10 +62,7 @@ export const Animated = {
|
|
|
58
62
|
View: AnimatedView,
|
|
59
63
|
Text: AnimatedText,
|
|
60
64
|
Image: AnimatedImage,
|
|
61
|
-
|
|
62
|
-
animatedScrollView ??= createAnimatedComponent(ScrollView);
|
|
63
|
-
return animatedScrollView;
|
|
64
|
-
},
|
|
65
|
+
ScrollView: AnimatedScrollView,
|
|
65
66
|
get FlatList() {
|
|
66
67
|
animatedFlatList ??= createAnimatedComponent(FlatList);
|
|
67
68
|
return animatedFlatList;
|
|
@@ -0,0 +1,13 @@
|
|
|
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. Sitting in `components/` made a statics object read as a
|
|
6
|
+
// component.
|
|
7
|
+
//
|
|
8
|
+
// A NAMESPACE OBJECT rather than five bare functions, deliberately: `Alert`, `Share`, `Linking`
|
|
9
|
+
// and `Vibration` are all `export const X = {…}`, and an app porting from RN writes
|
|
10
|
+
// `Image.getSize(...)` verbatim. Shared verbatim via `imageStatics`, the same source every other
|
|
11
|
+
// adapter's Image uses.
|
|
12
|
+
import { imageStatics } from '@symbiote-native/components';
|
|
13
|
+
export const Image = imageStatics;
|
package/build/register.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import '@symbiote-native/components/register';
|
package/build/register.js
CHANGED
|
@@ -1,35 +1,15 @@
|
|
|
1
|
-
// Side-effect ONLY.
|
|
1
|
+
// Side-effect ONLY. Exports nothing, and must never be re-exported from the barrel.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// `import './register'
|
|
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.
|
|
7
8
|
//
|
|
8
|
-
// WHY
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
// Babel merges the two into one dependency and the merged one stays lazy. See CLAUDE.md, "Never
|
|
16
|
-
// make correctness depend on a module's load-time side effect".
|
|
17
|
-
//
|
|
18
|
-
// `adapters/vue/src/index.test.ts` pins the barrel against both halves of that.
|
|
19
|
-
import { registerImageBehavior, registerInputAccessoryViewBehavior, registerPressableBehavior, registerSwitchBehavior, registerTextInputBehavior, } from '@symbiote-native/components';
|
|
20
|
-
registerPressableBehavior();
|
|
21
|
-
// Only the LOWERED tags carry this — the wrapper renders `symbiote-text-input-managed` and
|
|
22
|
-
// keeps running its own lifecycle. One owner per node; see `component-names/shared.ts`.
|
|
23
|
-
registerTextInputBehavior();
|
|
24
|
-
// Same reason as TextInput: the wrapper renders `symbiote-switch-managed` and runs its own
|
|
25
|
-
// lastNativeReport/snap-back lifecycle, so the engine's copy attaches only to the bare tag.
|
|
26
|
-
registerSwitchBehavior();
|
|
27
|
-
// Image owns no runtime — its behavior is a prop FOLD and nothing else, and it is registered on the
|
|
28
|
-
// same `symbiote-image` the wrapper already emits rather than on a `-managed` twin. That is safe
|
|
29
|
-
// only because the mapping is idempotent, which `core/components/src/behaviors/image.test.ts`
|
|
30
|
-
// asserts rather than assumes; a wrapper-built node simply folds a second time and nothing moves.
|
|
31
|
-
registerImageBehavior();
|
|
32
|
-
// Fold-only, and it shares the wrapper's tag for the same reason Image does: the mapping has no
|
|
33
|
-
// aliasing at all, so a wrapper-built node folding a second time moves nothing.
|
|
34
|
-
// `core/components/src/behaviors/input-accessory-view.test.ts` asserts that rather than assuming it.
|
|
35
|
-
registerInputAccessoryViewBehavior();
|
|
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';
|
package/build/renderer/index.js
CHANGED
|
@@ -2,47 +2,67 @@
|
|
|
2
2
|
// the engine's tiny mutation API; the engine owns all Fabric clone-on-write, so Vue
|
|
3
3
|
// drives the exact same retained tree React does: the proof the core is framework-
|
|
4
4
|
// agnostic.
|
|
5
|
-
import { createRenderer } from '@vue/runtime-core';
|
|
6
|
-
import { appendChild, createAnchor, createElement, createRawText, dlog, insertBefore, removeChild, routeProp, setProp, setText,
|
|
5
|
+
import { callWithErrorHandling, createRenderer, ErrorCodes, markRaw, } from '@vue/runtime-core';
|
|
6
|
+
import { appendChild, createAnchor, createElement, createRawText, childrenOf, dlog, insertBefore, isRawTextNode, isTextContainer, nextSiblingOf, parentOf, removeChild, routeProp, setProp, setText, textOf, toPublicInstance, SymbioteSurface, } from '@symbiote-native/engine';
|
|
7
7
|
import { descriptorFor } from '@symbiote-native/components';
|
|
8
8
|
import { normalizeVueAttrKey } from '../utils/normalize-attrs.js';
|
|
9
9
|
function isSurface(parent) {
|
|
10
10
|
return parent instanceof SymbioteSurface;
|
|
11
11
|
}
|
|
12
12
|
function isRawText(node) {
|
|
13
|
-
return node
|
|
13
|
+
return isRawTextNode(node);
|
|
14
14
|
}
|
|
15
|
-
// RN's Text
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
// (
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
//
|
|
15
|
+
// RN's two Text defaults left this renderer entirely on 2026-09-18, in two steps a month apart. The
|
|
16
|
+
// CREATE seed went first, because writing them as props cost a crossing every time the app authored
|
|
17
|
+
// the same value — 6 000 per 1 000-row create, measured with `writesOfUnchanged`. What stayed was a
|
|
18
|
+
// clear-back: an explicit `undefined` at patch time was substituted for the default, since RN treats
|
|
19
|
+
// a missing prop and an explicit `undefined` alike.
|
|
20
|
+
//
|
|
21
|
+
// That substitution is gone too, and it was redundant rather than wrong. The rule reads the AUTHORED
|
|
22
|
+
// bag at payload time (`foldTextDefaults`, `SymbioteFabricProps.cpp`), so it cannot tell a cleared
|
|
23
|
+
// prop from one never written — both are absent by the time it looks, and both get the default. The
|
|
24
|
+
// adapter was re-supplying an answer the layer below already had.
|
|
25
|
+
// `PROP_ALIASES` (`id` -> `nativeID`) left this renderer on 2026-09-18 — `routeProp` resolves it
|
|
26
|
+
// for every adapter now, and carries the per-node state the caveat here said was not worth it: with
|
|
27
|
+
// both names on one element upstream gives `id` unconditional priority, where a per-key rename
|
|
28
|
+
// resolved it by write order.
|
|
29
|
+
// RN-style event prop naming ('onPress', 'onValueChange', ...), the same convention JSX itself
|
|
30
|
+
// uses to separate an event from a plain value prop — good enough to decide whether to wrap,
|
|
31
|
+
// without duplicating routeProp's own ViewConfig-derived event lists here.
|
|
32
|
+
const EVENT_PROP_NAME = /^on[A-Z]/;
|
|
33
|
+
// A native event (a Fabric touch, a responder gesture) invokes a Pressable/TextInput/etc.
|
|
34
|
+
// listener straight out of the engine's own event dispatch (core/engine/src/dispatch.ts,
|
|
35
|
+
// core/components/src/behaviors/pressable.ts's `dispatch`), entirely outside anything Vue ever
|
|
36
|
+
// wraps — no render, no watcher, no lifecycle hook sits between the native event and the app's
|
|
37
|
+
// callback. So a throw inside `onPress={throwNow}` was a genuinely UNCAUGHT JS exception: it
|
|
38
|
+
// skipped `onErrorCaptured`, skipped `app.config.errorHandler`, skipped even this adapter's own
|
|
39
|
+
// default reporter (render.ts's `reportToHost`) and reached Hermes's own top-level handler
|
|
40
|
+
// directly — the raw, component-stack-less redbox this was device-reported as, not the "vue
|
|
41
|
+
// render (...)" framed one `reportToHost` produces. Real Vue DOM has the identical seam
|
|
42
|
+
// (`patchEvent`'s `createInvoker`) for the identical reason: a DOM event is native too.
|
|
35
43
|
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// Wrapping here, at `patchProp`, needs the OWNING instance — and `getCurrentInstance()` cannot
|
|
45
|
+
// supply it: Vue resets `currentRenderingInstance` at the END of `renderComponentRoot`, BEFORE
|
|
46
|
+
// the subsequent `patch()` call that actually reaches `mountElement`/`patchProp`, so by the time
|
|
47
|
+
// this runs it always reads null (measured via a real thrown press listener — `handleError` never
|
|
48
|
+
// found the `onErrorCaptured` ancestor because `if (instance)` was false). Real Vue DOM does not
|
|
49
|
+
// use `getCurrentInstance()` for this either: `hostPatchProp` is called with an explicit 6th
|
|
50
|
+
// argument, `parentComponent`, threaded down through `patch`/`mountElement` from the component
|
|
51
|
+
// whose render produced the vnode — the same instance the DOM renderer's own `patchEvent` passes
|
|
52
|
+
// to `callWithAsyncErrorHandling`. That parameter, not the global pointer, is the correct capture.
|
|
53
|
+
//
|
|
54
|
+
// `callWithErrorHandling` (the SYNC form, not `callWithAsyncErrorHandling`) is deliberate: a
|
|
55
|
+
// responder-negotiation callback (`onStartShouldSetResponder`, ...) returns a boolean the engine
|
|
56
|
+
// reads synchronously, and the sync form is the one that returns the call's result directly
|
|
57
|
+
// rather than folding it into promise-handling for the multi-hook case.
|
|
58
|
+
function wrapListenerForErrorHandling(listener, instance) {
|
|
59
|
+
const wrapper = (...args) => callWithErrorHandling(listener, instance, ErrorCodes.NATIVE_EVENT_HANDLER, args);
|
|
60
|
+
// Some listeners are marker-carrying functions, not plain callbacks — Animated.event's
|
|
61
|
+
// __getHandler() (core/engine/src/animated/event.ts) hands back exactly this shape,
|
|
62
|
+
// `Object.assign((...) => {...}, { __getEvent: () => this })`, and the engine's native-driver
|
|
63
|
+
// attach path reads `__getEvent` off the prop value it was given. A fresh closure with none of
|
|
64
|
+
// the original's own properties silently breaks that — carry them forward.
|
|
65
|
+
return Object.assign(wrapper, listener);
|
|
46
66
|
}
|
|
47
67
|
// One renderer per mounted surface: the options close over the surface so every mutation
|
|
48
68
|
// can ask it to (microtask-coalesced) recommit. Vue has no resetAfterCommit; instead
|
|
@@ -53,12 +73,10 @@ export function createSymbioteRenderer(surface) {
|
|
|
53
73
|
createElement(type) {
|
|
54
74
|
const descriptor = descriptorFor(type);
|
|
55
75
|
// `type` as the third argument, not just `descriptor.component`: the behavior registry is
|
|
56
|
-
// keyed by the INTRINSIC TAG (`
|
|
57
|
-
// resolved Fabric name (`RCTView`). This is the one place that
|
|
58
|
-
//
|
|
76
|
+
// keyed by the INTRINSIC TAG (`pressable`), while a node only ever carries the
|
|
77
|
+
// resolved Fabric name (`RCTView`). This is the one place that holds both, so a primitive
|
|
78
|
+
// whose machine lives on the engine node can be matched at all.
|
|
59
79
|
const node = createElement(descriptor.component, descriptor.isText, type);
|
|
60
|
-
if (descriptor.isText)
|
|
61
|
-
seedTextDefaults(node);
|
|
62
80
|
// The imperative public-instance API (measure / setNativeProps / focus / …) is already on
|
|
63
81
|
// the node's prototype, so a template/function ref to a host element exposes it exactly
|
|
64
82
|
// like React's getPublicInstance and toPublicInstance is the identity. The ref must keep
|
|
@@ -69,7 +87,16 @@ export function createSymbioteRenderer(surface) {
|
|
|
69
87
|
// one benchmark press — and a literal is built at the call site before dlog can decide
|
|
70
88
|
// anything (see core/engine/src/debug.ts).
|
|
71
89
|
dlog(() => `vue createElement ${descriptor.component} -> public instance`);
|
|
72
|
-
|
|
90
|
+
// markRaw is load-bearing, not an optimization: useTemplateRef()'s return value is
|
|
91
|
+
// `readonly(shallowRef(null))` (runtime-core.cjs.js), and Vue's readonly() wraps ANY
|
|
92
|
+
// `.value` whose Object.prototype.toString reads "[object Object]" — true of a plain class
|
|
93
|
+
// instance, unlike a real DOM Element, which fails that check for free. Unmarked, a node
|
|
94
|
+
// reached through useTemplateRef() (not a plain ref()/shallowRef(), both of which skip the
|
|
95
|
+
// wrap) came back as a deep-readonly Proxy: reads worked, so `committedOf`/`whenCommitted`
|
|
96
|
+
// saw a "committed" node, but `setNativeProps`'s `node.props.style = …` silently no-op'd
|
|
97
|
+
// with a dev-only "Set operation… target is readonly" warning — device-reported 2026-09-11
|
|
98
|
+
// as "flash the right chip" doing nothing on press.
|
|
99
|
+
return markRaw(toPublicInstance(node));
|
|
73
100
|
},
|
|
74
101
|
createText(text) {
|
|
75
102
|
// Vue mounts Fragment boundaries (v-for / v-if lists / multi-root) as EMPTY text
|
|
@@ -98,25 +125,26 @@ export function createSymbioteRenderer(surface) {
|
|
|
98
125
|
// this instead of insert() when an element's children collapse to a single string, so
|
|
99
126
|
// without the check a raw text lands under a non-Text parent - an invalid Fabric tree
|
|
100
127
|
// built silently, which is worse than the throw insert() would have given.
|
|
101
|
-
if (!el
|
|
128
|
+
if (!isTextContainer(el)) {
|
|
102
129
|
throw new Error(`Text string "${text}" must be rendered inside a <Text>`);
|
|
103
130
|
}
|
|
104
131
|
// An RCTText carries its string as a single raw-text child. Reuse a lone existing
|
|
105
132
|
// one to avoid churn; otherwise replace all children with a fresh raw-text node.
|
|
106
|
-
const
|
|
107
|
-
|
|
133
|
+
const existing = childrenOf(el);
|
|
134
|
+
const [first] = existing;
|
|
135
|
+
if (existing.length === 1 && first !== undefined && isRawText(first)) {
|
|
108
136
|
setText(first, text);
|
|
109
137
|
}
|
|
110
138
|
else {
|
|
111
|
-
for (const child of
|
|
139
|
+
for (const child of existing.slice())
|
|
112
140
|
removeChild(el, child);
|
|
113
141
|
appendChild(el, createRawText(text));
|
|
114
142
|
}
|
|
115
143
|
surface.requestCommit();
|
|
116
144
|
},
|
|
117
145
|
insert(child, parent, anchor) {
|
|
118
|
-
if (isRawText(child) && (isSurface(parent) || !parent
|
|
119
|
-
throw new Error(`Text string "${
|
|
146
|
+
if (isRawText(child) && (isSurface(parent) || !isTextContainer(parent))) {
|
|
147
|
+
throw new Error(`Text string "${textOf(child) ?? ''}" must be rendered inside a <Text>`);
|
|
120
148
|
}
|
|
121
149
|
if (isSurface(parent)) {
|
|
122
150
|
if (anchor)
|
|
@@ -135,7 +163,7 @@ export function createSymbioteRenderer(surface) {
|
|
|
135
163
|
remove(child) {
|
|
136
164
|
// A top-level node has no parent (it lives in surface.children); everything else
|
|
137
165
|
// detaches from its retained parent.
|
|
138
|
-
const parent = child
|
|
166
|
+
const parent = parentOf(child);
|
|
139
167
|
if (parent !== undefined)
|
|
140
168
|
removeChild(parent, child);
|
|
141
169
|
else
|
|
@@ -143,27 +171,53 @@ export function createSymbioteRenderer(surface) {
|
|
|
143
171
|
surface.requestCommit();
|
|
144
172
|
},
|
|
145
173
|
parentNode(node) {
|
|
146
|
-
return node
|
|
174
|
+
return parentOf(node) ?? surface;
|
|
147
175
|
},
|
|
148
176
|
nextSibling(node) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return
|
|
177
|
+
// `?? null` because Vue's RendererOptions types the miss as null, not undefined; the
|
|
178
|
+
// engine answers undefined uniformly and the surface fallback lives there now.
|
|
179
|
+
return nextSiblingOf(node, surface) ?? null;
|
|
152
180
|
},
|
|
153
|
-
patchProp(el, key,
|
|
181
|
+
patchProp(el, key, prev, next, _namespace, parentComponent) {
|
|
154
182
|
if (isSurface(el))
|
|
155
183
|
return;
|
|
156
184
|
// Kebab -> camel happens HERE, not only inside a component wrapper: the SFC transformer
|
|
157
185
|
// lowers View/Text to their intrinsic tags (metro-vue-transformer.cjs), so those props
|
|
158
186
|
// arrive one key at a time with no component to fold the bag. Idempotent for the wrapped
|
|
159
187
|
// path, which already normalized.
|
|
160
|
-
const
|
|
161
|
-
const
|
|
188
|
+
const name = normalizeVueAttrKey(key);
|
|
189
|
+
const value = next;
|
|
190
|
+
const routed = EVENT_PROP_NAME.test(name) && typeof value === 'function'
|
|
191
|
+
? wrapListenerForErrorHandling(value, parentComponent ?? null)
|
|
192
|
+
: value;
|
|
162
193
|
// routeProp makes the prop-vs-event decision from the node's ViewConfig (onPress on a
|
|
163
194
|
// View becomes a listener; onTintColor on a Switch stays a prop), shared with React. The
|
|
164
195
|
// class/style merge (explicit :style always winning, regardless of which of Vue's two
|
|
165
196
|
// independent patchProp calls lands last) is centralized there too (core/engine/src/node.ts).
|
|
166
|
-
|
|
197
|
+
//
|
|
198
|
+
// `value` REACHES HERE UNCHANGED, on every re-render, and that is upstream by design:
|
|
199
|
+
// `patchProps` excludes it from its own diff and then patches it on its own line
|
|
200
|
+
// (@vue/runtime-core 3.5.39 — `if (next !== prev && key !== "value")`, then
|
|
201
|
+
// `if ("value" in newProps)`). The reason is a DOM one: typing mutates `el.value` directly,
|
|
202
|
+
// so what Vue last SET is not what the element now HOLDS, and only the element can say.
|
|
203
|
+
// Upstream pairs it with a guard in the patcher, and that half we did not have — `patchDOMProp`
|
|
204
|
+
// writes only on a difference against the element's live value (@vue/runtime-dom,
|
|
205
|
+
// `if (oldValue !== newValue || !("_value" in el))`). So every `<text-input>` re-routed its
|
|
206
|
+
// value on every re-render of its parent: 1 000 writes and ~6 000 wire slots per relabel of
|
|
207
|
+
// the benchmark list, against solid's 0 for the identical tree.
|
|
208
|
+
//
|
|
209
|
+
// On `prev` rather than on the node's own prop: the divergence upstream protects against is
|
|
210
|
+
// not visible from here — native text lives on the far side and TextInput's behavior owns the
|
|
211
|
+
// mirror — and asking the host would cost a crossing per input per render to be told the
|
|
212
|
+
// declarative value, which is what `prev` already is.
|
|
213
|
+
//
|
|
214
|
+
// The COMMIT REQUEST still goes out, which is why this skips the route and does not return.
|
|
215
|
+
// The controlled handshake hangs off the commit beat, not off the write: `afterCommit` reads
|
|
216
|
+
// the node's own `value` against its native mirror. An idle commit is O(1) (F-12) and its
|
|
217
|
+
// post-commit hooks run whether or not the commit reached the host (F-7).
|
|
218
|
+
if (key !== 'value' || prev !== next) {
|
|
219
|
+
routeProp(el, name, routed);
|
|
220
|
+
}
|
|
167
221
|
surface.requestCommit();
|
|
168
222
|
},
|
|
169
223
|
// RN has no querySelector / scope-id / innerHTML. The first two are inert; static
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export * from '@vue/runtime-core';
|
|
2
2
|
import { type ObjectDirective } from '@vue/runtime-core';
|
|
3
3
|
import { type ISymbioteNode } from '@symbiote-native/engine';
|
|
4
|
-
export { Teleport, type ITeleportTarget } from '../create-portal';
|
|
5
4
|
export declare function normalizeProps(props: Record<string, unknown> | null | undefined): Record<string, unknown> | null;
|
|
6
5
|
export declare function normalizeStyle(value: unknown): unknown;
|
|
7
6
|
export declare function mergeProps(...args: Record<string, unknown>[]): Record<string, unknown>;
|
|
@@ -28,6 +27,16 @@ type IEventHandler<TEvent> = ((event: TEvent, ...args: never[]) => unknown) & {
|
|
|
28
27
|
* typed as required, matching upstream Vue's own declaration — its runtime-only `!fn` guard below
|
|
29
28
|
* (for a compiler-generated call site that could pass a falsy handler) isn't reflected in the
|
|
30
29
|
* type there either.
|
|
30
|
+
*
|
|
31
|
+
* `@press.self` on an element reaches this, and both halves were measured 2026-09-11 rather
|
|
32
|
+
* than assumed — the question arose because a modifier on a COMPONENT takes Vue's own event path
|
|
33
|
+
* and only an element emits the helper. `<pressable @press.self>` compiles to
|
|
34
|
+
* `_withModifiers(fn, ["self"])` imported `from "@symbiote-native/vue/runtime-helpers"` (the Metro
|
|
35
|
+
* transformer retargets every compiled `from 'vue'`), so the compiler-emitted call lands here and
|
|
36
|
+
* not on @vue/runtime-dom's. And `ISymbioteEvent` (`core/engine/src/node.ts`) declares `target`,
|
|
37
|
+
* `currentTarget` and `stopPropagation` as REQUIRED fields with the DOM semantics these guards
|
|
38
|
+
* read, so `.self` genuinely filters a bubbled press instead of degenerating to
|
|
39
|
+
* `undefined !== undefined`.
|
|
31
40
|
*/
|
|
32
41
|
export declare function withModifiers<TEvent extends IModifierGuardableEvent>(fn: IEventHandler<TEvent>, modifiers: readonly IEventModifier[]): IEventHandler<TEvent>;
|
|
33
42
|
type IKeyedEvent = {
|