@symbiote-native/svelte 1.0.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.
- package/README.md +8 -10
- package/build/components/{activity-indicator/activity-indicator-props.d.ts → activity-indicator-props.d.ts} +1 -1
- package/build/components/flat-list/index.svelte +4 -4
- package/build/components/{image-background/image-background-props.d.ts → image-background-props.d.ts} +1 -1
- package/build/components/index.d.ts +6 -24
- package/build/components/index.js +0 -18
- package/build/components/keyboard-avoiding-view/index.svelte +6 -6
- package/build/components/modal/index.svelte +7 -7
- package/build/components/pressable/pressable-props.d.ts +1 -0
- package/build/components/refresh-control-props.d.ts +1 -0
- package/build/components/text-props.d.ts +1 -0
- package/build/components/virtualized-list/index.svelte +71 -250
- package/build/dom-shim/canonical-prop-names.d.ts +2 -0
- package/build/dom-shim/canonical-prop-names.js +320 -0
- package/build/dom-shim/document.js +1 -1
- package/build/dom-shim/element.d.ts +9 -3
- package/build/dom-shim/element.js +195 -29
- package/build/dom-shim/patch-globals.js +31 -5
- package/build/dom-shim/style-cache.d.ts +1 -0
- package/build/dom-shim/style-cache.js +27 -0
- package/build/dom-shim/text.js +2 -2
- package/build/host-instance.d.ts +2 -3
- package/build/host-instance.js +7 -1
- package/build/index.d.ts +7 -4
- package/build/index.js +17 -5
- package/build/intrinsic-elements.d.ts +63 -0
- package/build/modules/animated/index.d.ts +7 -27
- package/build/modules/animated/index.js +24 -34
- package/build/modules/image/index.d.ts +3 -0
- package/build/modules/image/index.js +15 -0
- package/build/preprocessor/scoped-styles.js +1 -1
- package/build/register.d.ts +1 -1
- package/build/register.js +13 -31
- package/build/root-element.js +1 -1
- package/metro-css-parser.cjs +2 -1
- package/metro-svelte-transformer.cjs +1 -17
- package/package.json +7 -15
- package/build/components/RefreshControl.svelte +0 -40
- package/build/components/RefreshControl.svelte.d.ts +0 -5
- package/build/components/SafeAreaView.svelte +0 -34
- package/build/components/SafeAreaView.svelte.d.ts +0 -5
- package/build/components/Text.svelte +0 -34
- package/build/components/Text.svelte.d.ts +0 -5
- package/build/components/View.svelte +0 -43
- package/build/components/View.svelte.d.ts +0 -5
- package/build/components/activity-indicator/activity-indicator-platform.android.d.ts +0 -3
- package/build/components/activity-indicator/activity-indicator-platform.android.js +0 -5
- package/build/components/activity-indicator/activity-indicator-platform.d.ts +0 -1
- package/build/components/activity-indicator/activity-indicator-platform.ios.d.ts +0 -3
- package/build/components/activity-indicator/activity-indicator-platform.ios.js +0 -6
- package/build/components/activity-indicator/activity-indicator-platform.js +0 -5
- package/build/components/activity-indicator/index.svelte +0 -63
- package/build/components/activity-indicator/index.svelte.d.ts +0 -5
- package/build/components/button.svelte +0 -76
- package/build/components/button.svelte.d.ts +0 -5
- package/build/components/image/image-logic.d.ts +0 -2
- package/build/components/image/image-logic.js +0 -135
- package/build/components/image/index.d.ts +0 -3
- package/build/components/image/index.js +0 -10
- package/build/components/image/index.svelte +0 -66
- package/build/components/image/index.svelte.d.ts +0 -5
- package/build/components/image-background/index.svelte +0 -87
- package/build/components/image-background/index.svelte.d.ts +0 -5
- package/build/components/input-accessory-view/index.svelte +0 -51
- package/build/components/input-accessory-view/index.svelte.d.ts +0 -5
- package/build/components/pressable/index.svelte +0 -185
- package/build/components/pressable/index.svelte.d.ts +0 -9
- package/build/components/scroll-view/index.svelte +0 -331
- package/build/components/scroll-view/index.svelte.d.ts +0 -18
- package/build/components/scroll-view/scroll-view-platform-types.d.ts +0 -3
- package/build/components/scroll-view/scroll-view-platform.android.d.ts +0 -3
- package/build/components/scroll-view/scroll-view-platform.android.js +0 -3
- package/build/components/scroll-view/scroll-view-platform.d.ts +0 -1
- package/build/components/scroll-view/scroll-view-platform.ios.d.ts +0 -3
- package/build/components/scroll-view/scroll-view-platform.ios.js +0 -3
- package/build/components/scroll-view/scroll-view-platform.js +0 -4
- package/build/components/scroll-view/scroll-view-sticky-context.d.ts +0 -7
- package/build/components/scroll-view/scroll-view-sticky-context.js +0 -1
- package/build/components/scroll-view/sticky-header-props.d.ts +0 -10
- package/build/components/scroll-view/sticky-header-props.js +0 -1
- package/build/components/scroll-view/sticky-header.svelte +0 -287
- package/build/components/scroll-view/sticky-header.svelte.d.ts +0 -5
- package/build/components/switch/index.svelte +0 -134
- package/build/components/switch/index.svelte.d.ts +0 -5
- package/build/components/text-input/index.svelte +0 -372
- package/build/components/text-input/index.svelte.d.ts +0 -17
- package/build/components/touchable-highlight/index.svelte +0 -139
- package/build/components/touchable-highlight/index.svelte.d.ts +0 -5
- package/build/components/touchable-native-feedback/index.d.ts +0 -9
- package/build/components/touchable-native-feedback/index.js +0 -14
- package/build/components/touchable-native-feedback/touchable-native-feedback.svelte +0 -49
- package/build/components/touchable-native-feedback/touchable-native-feedback.svelte.d.ts +0 -5
- package/build/components/touchable-opacity/index.svelte +0 -181
- package/build/components/touchable-opacity/index.svelte.d.ts +0 -5
- package/build/components/touchable-without-feedback/index.svelte +0 -75
- package/build/components/touchable-without-feedback/index.svelte.d.ts +0 -5
- package/build/modules/animated/animated-component-props.d.ts +0 -7
- package/build/modules/animated/animated-component-props.js +0 -1
- package/build/modules/animated/create-animated-component.d.ts +0 -5
- package/build/modules/animated/create-animated-component.js +0 -131
- package/build/preprocessor/lower-host-primitives.d.ts +0 -10
- package/build/preprocessor/lower-host-primitives.js +0 -455
- package/build/state-style.d.ts +0 -1
- package/build/state-style.js +0 -8
- /package/build/components/{activity-indicator/activity-indicator-props.js → activity-indicator-props.js} +0 -0
- /package/build/components/{image-background/image-background-props.js → image-background-props.js} +0 -0
- /package/build/{components/scroll-view/scroll-view-platform-types.js → intrinsic-elements.js} +0 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// The prop names an app may write on a bare intrinsic tag, DERIVED — never hand-maintained.
|
|
2
|
+
//
|
|
3
|
+
// Why the shim needs a list of names at all. Svelte's compiler destroys two things a bare tag
|
|
4
|
+
// carries, and both are settled before any code of ours runs:
|
|
5
|
+
//
|
|
6
|
+
// `<view testID="x">` `fix_attribute_casing` lowercases every STATIC attribute name
|
|
7
|
+
// (transform-template/fix-attribute-casing.js) — `testid` reaches
|
|
8
|
+
// Fabric, which declares no such prop, and it is dropped on device.
|
|
9
|
+
// `<pressable testID={v}>` the same, for a DYNAMIC name, on any tag that is not an SVG
|
|
10
|
+
// element name (`get_attribute_name` -> `normalize_attribute`).
|
|
11
|
+
// `<text-input multiline={false}>`
|
|
12
|
+
// a hyphenated tag takes `set_custom_element_data`, which stringifies
|
|
13
|
+
// a scalar and drops an object unless `get_setters(node)` finds a real
|
|
14
|
+
// prototype setter for that exact name (attributes.js:245-265).
|
|
15
|
+
//
|
|
16
|
+
// One list answers all three: an accessor per name defeats the stringifier, and the same names
|
|
17
|
+
// keyed by their lowercased spelling recover the casing in `setAttribute`. See `element.ts`.
|
|
18
|
+
//
|
|
19
|
+
// GENERATED from this adapter's own barrel — every `I*Props` type it exports, unioned through the
|
|
20
|
+
// TypeScript checker. That is the authoritative per-tag prop surface (`IViewProps` and its
|
|
21
|
+
// siblings), and it grows on its own when a primitive gains a prop or the barrel gains a type.
|
|
22
|
+
// `canonical-prop-names.test.ts` re-derives it and fails on any drift; regenerate with the snippet
|
|
23
|
+
// in that file's header rather than editing this array by hand.
|
|
24
|
+
//
|
|
25
|
+
// Deriving it from `react-native`'s own ViewConfig `validAttributes` was measured and rejected:
|
|
26
|
+
// it covers the NATIVE surface only, missing 110 of the 255 camelCase names our shared layer
|
|
27
|
+
// reads — `readOnly`, `inputMode`, `enterKeyHint`, `activeOpacity`, `delayLongPress`,
|
|
28
|
+
// `pressRetentionOffset` and the rest of what our behaviors fold in JS.
|
|
29
|
+
export const CANONICAL_PROP_NAMES = [
|
|
30
|
+
'accessibilityActions',
|
|
31
|
+
'accessibilityElementsHidden',
|
|
32
|
+
'accessibilityHint',
|
|
33
|
+
'accessibilityIgnoresInvertColors',
|
|
34
|
+
'accessibilityLabel',
|
|
35
|
+
'accessibilityLabelledBy',
|
|
36
|
+
'accessibilityLanguage',
|
|
37
|
+
'accessibilityLargeContentTitle',
|
|
38
|
+
'accessibilityLiveRegion',
|
|
39
|
+
'accessibilityRespondsToUserInteraction',
|
|
40
|
+
'accessibilityRole',
|
|
41
|
+
'accessibilityShowsLargeContentViewer',
|
|
42
|
+
'accessibilityState',
|
|
43
|
+
'accessibilityValue',
|
|
44
|
+
'accessibilityViewIsModal',
|
|
45
|
+
'accessible',
|
|
46
|
+
'activeOpacity',
|
|
47
|
+
'adjustsFontSizeToFit',
|
|
48
|
+
'allowFontScaling',
|
|
49
|
+
'allowSwipeDismissal',
|
|
50
|
+
'alt',
|
|
51
|
+
'alwaysBounceHorizontal',
|
|
52
|
+
'alwaysBounceVertical',
|
|
53
|
+
'android_disableSound',
|
|
54
|
+
'android_ripple',
|
|
55
|
+
'animated',
|
|
56
|
+
'animating',
|
|
57
|
+
'animationType',
|
|
58
|
+
'aria-busy',
|
|
59
|
+
'aria-checked',
|
|
60
|
+
'aria-disabled',
|
|
61
|
+
'aria-expanded',
|
|
62
|
+
'aria-hidden',
|
|
63
|
+
'aria-label',
|
|
64
|
+
'aria-labelledby',
|
|
65
|
+
'aria-live',
|
|
66
|
+
'aria-modal',
|
|
67
|
+
'aria-selected',
|
|
68
|
+
'aria-valuemax',
|
|
69
|
+
'aria-valuemin',
|
|
70
|
+
'aria-valuenow',
|
|
71
|
+
'aria-valuetext',
|
|
72
|
+
'autoCapitalize',
|
|
73
|
+
'autoComplete',
|
|
74
|
+
'autoCorrect',
|
|
75
|
+
'autoFocus',
|
|
76
|
+
'automaticallyAdjustKeyboardInsets',
|
|
77
|
+
'backdropColor',
|
|
78
|
+
'background',
|
|
79
|
+
'backgroundColor',
|
|
80
|
+
'barStyle',
|
|
81
|
+
'behavior',
|
|
82
|
+
'blurOnSubmit',
|
|
83
|
+
'blurRadius',
|
|
84
|
+
'bounces',
|
|
85
|
+
'bouncesZoom',
|
|
86
|
+
'cancelable',
|
|
87
|
+
'capInsets',
|
|
88
|
+
'centerContent',
|
|
89
|
+
'children',
|
|
90
|
+
'class',
|
|
91
|
+
'collapsable',
|
|
92
|
+
'color',
|
|
93
|
+
'colors',
|
|
94
|
+
'columnWrapperStyle',
|
|
95
|
+
'contentContainerStyle',
|
|
96
|
+
'contentInset',
|
|
97
|
+
'contentInsetAdjustmentBehavior',
|
|
98
|
+
'contentOffset',
|
|
99
|
+
'crossOrigin',
|
|
100
|
+
'cursorColor',
|
|
101
|
+
'data',
|
|
102
|
+
'decelerationRate',
|
|
103
|
+
'defaultSource',
|
|
104
|
+
'defaultValue',
|
|
105
|
+
'delayHoverIn',
|
|
106
|
+
'delayHoverOut',
|
|
107
|
+
'delayLongPress',
|
|
108
|
+
'delayPressIn',
|
|
109
|
+
'delayPressOut',
|
|
110
|
+
'directionalLockEnabled',
|
|
111
|
+
'disableIntervalMomentum',
|
|
112
|
+
'disabled',
|
|
113
|
+
'editable',
|
|
114
|
+
'ellipsizeMode',
|
|
115
|
+
'empty',
|
|
116
|
+
'enabled',
|
|
117
|
+
'endFillColor',
|
|
118
|
+
'enterKeyHint',
|
|
119
|
+
'extraData',
|
|
120
|
+
'fadeDuration',
|
|
121
|
+
'fadingEdgeLength',
|
|
122
|
+
'focusable',
|
|
123
|
+
'footer',
|
|
124
|
+
'getItem',
|
|
125
|
+
'getItemCount',
|
|
126
|
+
'getItemLayout',
|
|
127
|
+
'hardwareAccelerated',
|
|
128
|
+
'hasTVPreferredFocus',
|
|
129
|
+
'header',
|
|
130
|
+
'height',
|
|
131
|
+
'hidden',
|
|
132
|
+
'hidesWhenStopped',
|
|
133
|
+
'highlighted',
|
|
134
|
+
'hitSlop',
|
|
135
|
+
'horizontal',
|
|
136
|
+
'id',
|
|
137
|
+
'imageStyle',
|
|
138
|
+
'importantForAccessibility',
|
|
139
|
+
'indicatorStyle',
|
|
140
|
+
'initialNumToRender',
|
|
141
|
+
'initialScrollIndex',
|
|
142
|
+
'inputAccessoryViewID',
|
|
143
|
+
'inputMode',
|
|
144
|
+
'invertStickyHeaders',
|
|
145
|
+
'inverted',
|
|
146
|
+
'ios_backgroundColor',
|
|
147
|
+
'item',
|
|
148
|
+
'keyExtractor',
|
|
149
|
+
'keyboardDismissMode',
|
|
150
|
+
'keyboardShouldPersistTaps',
|
|
151
|
+
'keyboardType',
|
|
152
|
+
'keyboardVerticalOffset',
|
|
153
|
+
'leadingItem',
|
|
154
|
+
'loadingIndicatorSource',
|
|
155
|
+
'maintainVisibleContentPosition',
|
|
156
|
+
'maxFontSizeMultiplier',
|
|
157
|
+
'maxLength',
|
|
158
|
+
'maxToRenderPerBatch',
|
|
159
|
+
'maximumZoomScale',
|
|
160
|
+
'minPressDuration',
|
|
161
|
+
'minimumFontScale',
|
|
162
|
+
'minimumZoomScale',
|
|
163
|
+
'mount',
|
|
164
|
+
'multiline',
|
|
165
|
+
'nativeID',
|
|
166
|
+
'navigationBarTranslucent',
|
|
167
|
+
'needsOffscreenAlphaCompositing',
|
|
168
|
+
'nestedScrollEnabled',
|
|
169
|
+
'networkActivityIndicatorVisible',
|
|
170
|
+
'nextFocusDown',
|
|
171
|
+
'nextFocusForward',
|
|
172
|
+
'nextFocusLeft',
|
|
173
|
+
'nextFocusRight',
|
|
174
|
+
'nextFocusUp',
|
|
175
|
+
'numColumns',
|
|
176
|
+
'numberOfLines',
|
|
177
|
+
'onAccessibilityAction',
|
|
178
|
+
'onAccessibilityEscape',
|
|
179
|
+
'onAccessibilityTap',
|
|
180
|
+
'onBlur',
|
|
181
|
+
'onContentSizeChange',
|
|
182
|
+
'onDismiss',
|
|
183
|
+
'onEndEditing',
|
|
184
|
+
'onEndReached',
|
|
185
|
+
'onEndReachedThreshold',
|
|
186
|
+
'onError',
|
|
187
|
+
'onFocus',
|
|
188
|
+
'onHideUnderlay',
|
|
189
|
+
'onHoverIn',
|
|
190
|
+
'onHoverOut',
|
|
191
|
+
'onKeyPress',
|
|
192
|
+
'onLayout',
|
|
193
|
+
'onLoad',
|
|
194
|
+
'onLoadEnd',
|
|
195
|
+
'onLoadStart',
|
|
196
|
+
'onLongPress',
|
|
197
|
+
'onMagicTap',
|
|
198
|
+
'onMomentumScrollBegin',
|
|
199
|
+
'onMomentumScrollEnd',
|
|
200
|
+
'onMoveShouldSetResponder',
|
|
201
|
+
'onMoveShouldSetResponderCapture',
|
|
202
|
+
'onOrientationChange',
|
|
203
|
+
'onPartialLoad',
|
|
204
|
+
'onPress',
|
|
205
|
+
'onPressIn',
|
|
206
|
+
'onPressMove',
|
|
207
|
+
'onPressOut',
|
|
208
|
+
'onProgress',
|
|
209
|
+
'onRefresh',
|
|
210
|
+
'onRequestClose',
|
|
211
|
+
'onResponderEnd',
|
|
212
|
+
'onResponderGrant',
|
|
213
|
+
'onResponderMove',
|
|
214
|
+
'onResponderReject',
|
|
215
|
+
'onResponderRelease',
|
|
216
|
+
'onResponderStart',
|
|
217
|
+
'onResponderTerminate',
|
|
218
|
+
'onResponderTerminationRequest',
|
|
219
|
+
'onScroll',
|
|
220
|
+
'onScrollBeginDrag',
|
|
221
|
+
'onScrollEndDrag',
|
|
222
|
+
'onScrollToIndexFailed',
|
|
223
|
+
'onScrollToTop',
|
|
224
|
+
'onSelectionChange',
|
|
225
|
+
'onShow',
|
|
226
|
+
'onShowUnderlay',
|
|
227
|
+
'onStartReached',
|
|
228
|
+
'onStartReachedThreshold',
|
|
229
|
+
'onStartShouldSetResponder',
|
|
230
|
+
'onStartShouldSetResponderCapture',
|
|
231
|
+
'onSubmitEditing',
|
|
232
|
+
'onTextLayout',
|
|
233
|
+
'onValueChange',
|
|
234
|
+
'onViewableItemsChanged',
|
|
235
|
+
'overScrollMode',
|
|
236
|
+
'pagingEnabled',
|
|
237
|
+
'persistentScrollbar',
|
|
238
|
+
'pinchGestureEnabled',
|
|
239
|
+
'placeholder',
|
|
240
|
+
'placeholderTextColor',
|
|
241
|
+
'pointerEvents',
|
|
242
|
+
'presentationStyle',
|
|
243
|
+
'pressRetentionOffset',
|
|
244
|
+
'progressBackgroundColor',
|
|
245
|
+
'progressViewOffset',
|
|
246
|
+
'progressiveRenderingEnabled',
|
|
247
|
+
'readOnly',
|
|
248
|
+
'referrerPolicy',
|
|
249
|
+
'refreshControl',
|
|
250
|
+
'refreshing',
|
|
251
|
+
'removeClippedSubviews',
|
|
252
|
+
'renderToHardwareTextureAndroid',
|
|
253
|
+
'resizeMethod',
|
|
254
|
+
'resizeMode',
|
|
255
|
+
'returnKeyType',
|
|
256
|
+
'role',
|
|
257
|
+
'screenReaderFocusable',
|
|
258
|
+
'scrollEnabled',
|
|
259
|
+
'scrollEventThrottle',
|
|
260
|
+
'scrollIndicatorInsets',
|
|
261
|
+
'section',
|
|
262
|
+
'sectionFooter',
|
|
263
|
+
'sectionHeader',
|
|
264
|
+
'sectionSeparator',
|
|
265
|
+
'sections',
|
|
266
|
+
'secureTextEntry',
|
|
267
|
+
'selectTextOnFocus',
|
|
268
|
+
'selectable',
|
|
269
|
+
'selection',
|
|
270
|
+
'selectionColor',
|
|
271
|
+
'selectionHandleColor',
|
|
272
|
+
'separator',
|
|
273
|
+
'shouldRasterizeIOS',
|
|
274
|
+
'showSoftInputOnFocus',
|
|
275
|
+
'showsHorizontalScrollIndicator',
|
|
276
|
+
'showsVerticalScrollIndicator',
|
|
277
|
+
'size',
|
|
278
|
+
'snapToAlignment',
|
|
279
|
+
'snapToEnd',
|
|
280
|
+
'snapToInterval',
|
|
281
|
+
'snapToOffsets',
|
|
282
|
+
'snapToStart',
|
|
283
|
+
'source',
|
|
284
|
+
'src',
|
|
285
|
+
'srcSet',
|
|
286
|
+
'statusBarTranslucent',
|
|
287
|
+
'stickyHeaderIndices',
|
|
288
|
+
'stickySectionHeadersEnabled',
|
|
289
|
+
'style',
|
|
290
|
+
'submitBehavior',
|
|
291
|
+
'supportedOrientations',
|
|
292
|
+
'testID',
|
|
293
|
+
'textAlign',
|
|
294
|
+
'textContentType',
|
|
295
|
+
'thumbColor',
|
|
296
|
+
'tintColor',
|
|
297
|
+
'title',
|
|
298
|
+
'titleColor',
|
|
299
|
+
'touchSoundDisabled',
|
|
300
|
+
'trackColor',
|
|
301
|
+
'trailingItem',
|
|
302
|
+
'translucent',
|
|
303
|
+
'transparent',
|
|
304
|
+
'underlayColor',
|
|
305
|
+
'underlineColorAndroid',
|
|
306
|
+
'unstable_pressDelay',
|
|
307
|
+
'updateCellsBatchingPeriod',
|
|
308
|
+
'useForeground',
|
|
309
|
+
'value',
|
|
310
|
+
'viewabilityConfig',
|
|
311
|
+
'viewabilityConfigCallbackPairs',
|
|
312
|
+
'visible',
|
|
313
|
+
'width',
|
|
314
|
+
'windowSize',
|
|
315
|
+
'zoomScale',
|
|
316
|
+
];
|
|
317
|
+
// Lowercased spelling -> the name the author wrote. Built once, at module load. No entry can
|
|
318
|
+
// collide: the derivation asserts it (`canonical-prop-names.test.ts`), because two props whose
|
|
319
|
+
// names differ only in case would make the repair a coin toss.
|
|
320
|
+
export const CANONICAL_BY_LOWER = new Map(CANONICAL_PROP_NAMES.map(name => [name.toLowerCase(), name]));
|
|
@@ -14,7 +14,7 @@ export class ShimDocument {
|
|
|
14
14
|
// Our own event names never delegate (§5c: no camelCase name matches Svelte's 23-name
|
|
15
15
|
// DELEGATED_EVENTS list), but a minimal, unattached node converts any stray read into a
|
|
16
16
|
// no-op instead of a crash — one line, per §4's "provide a stub anyway" call.
|
|
17
|
-
body = new ShimElement('
|
|
17
|
+
body = new ShimElement('view');
|
|
18
18
|
createElement(tag, options) {
|
|
19
19
|
const element = new ShimElement(tag);
|
|
20
20
|
if (options?.is !== undefined)
|
|
@@ -7,7 +7,8 @@ export declare class ShimElement extends ShimElementBase {
|
|
|
7
7
|
readonly tagName: string;
|
|
8
8
|
readonly namespaceURI: string | undefined;
|
|
9
9
|
private attributes;
|
|
10
|
-
private
|
|
10
|
+
private doorBag;
|
|
11
|
+
private pBag;
|
|
11
12
|
private lastBag;
|
|
12
13
|
constructor(tagName: string, namespaceURI?: string);
|
|
13
14
|
get nodeName(): string;
|
|
@@ -15,11 +16,16 @@ export declare class ShimElement extends ShimElementBase {
|
|
|
15
16
|
get style(): {
|
|
16
17
|
cssText: string;
|
|
17
18
|
};
|
|
19
|
+
writeStyleValue(value: unknown): void;
|
|
20
|
+
get className(): unknown;
|
|
21
|
+
set className(value: unknown);
|
|
18
22
|
get p(): IShimPropBag;
|
|
19
23
|
set p(bag: IShimPropBag | undefined);
|
|
20
|
-
setAttribute(name: string, value:
|
|
21
|
-
getAttribute(name: string):
|
|
24
|
+
setAttribute(name: string, value: unknown): void;
|
|
25
|
+
getAttribute(name: string): unknown;
|
|
22
26
|
removeAttribute(name: string): void;
|
|
27
|
+
writeBagKey(name: string, value: unknown): void;
|
|
28
|
+
private foldedBag;
|
|
23
29
|
addEventListener(name: string, handler: (event: unknown) => void): void;
|
|
24
30
|
removeEventListener(name: string): void;
|
|
25
31
|
cloneNode(deep?: boolean): ShimElement;
|
|
@@ -1,20 +1,34 @@
|
|
|
1
|
-
// The workhorse of the shim
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// a real property
|
|
7
|
-
//
|
|
8
|
-
//
|
|
1
|
+
// The workhorse of the shim, and it has FIVE doors, not one — svelte decides which by tag name,
|
|
2
|
+
// by compiled namespace and by whether the attribute starts with `on`, so an element cannot
|
|
3
|
+
// choose its own:
|
|
4
|
+
//
|
|
5
|
+
// set_custom_element_data hyphenated tags (`text-input`, …). Stringifies a scalar; an OBJECT
|
|
6
|
+
// lands as a real property set, which is what makes `p={{…}}` work.
|
|
7
|
+
// set_attribute every other tag, and the hyphenated ones for a non-string value.
|
|
8
|
+
// set_class -> `setAttribute('class')` in the svg namespace, `dom.className`
|
|
9
|
+
// in html. Both spellings land in the same bag key.
|
|
10
|
+
// set_style -> `dom.style.cssText`, STRINGIFIED, plus the authored value under
|
|
11
|
+
// a private Symbol. See `style-cache.ts`.
|
|
12
|
+
// addEventListener any `on<Name>` attribute, via `$.event()`, with the `on` stripped.
|
|
13
|
+
//
|
|
14
|
+
// All five converge on one folded bag and out through `routeProp`, the entry point React's flat
|
|
15
|
+
// bag and Vue's patchProp already use — because only `routeProp` knows which `on*` names the
|
|
16
|
+
// node's ViewConfig declares as events and which are ordinary callback props a behavior reads.
|
|
17
|
+
// `p` is ours to name (§3g(c)); a component forwarding a whole bag emits it, app code need not.
|
|
18
|
+
//
|
|
19
|
+
// They converge, they do not merge: `p` owns its key SET, since a key dropped from the bag has to
|
|
20
|
+
// reset the prop, while the other four own a key each. Hence `doorBag` / `pBag` below.
|
|
9
21
|
//
|
|
10
22
|
// A per-key diff in the setter is MANDATORY (§3g(c)), not an optimization: Svelte's
|
|
11
23
|
// `set_custom_element_data` has no early-out guard and runs on every effect re-fire, so
|
|
12
24
|
// without the diff every prop gets rewritten whenever any one of them changes.
|
|
13
|
-
import { createElement, dlog, routeProp,
|
|
25
|
+
import { createElement, dlog, routeProp, toPublicInstance, } from '@symbiote-native/engine';
|
|
14
26
|
import { descriptorFor } from '@symbiote-native/components';
|
|
15
27
|
import { normalizeSvelteClass } from '../class-value.js';
|
|
28
|
+
import { CANONICAL_BY_LOWER, CANONICAL_PROP_NAMES, } from './canonical-prop-names.js';
|
|
16
29
|
import { foldHostBag } from './fold-host-bag.js';
|
|
17
30
|
import { ShimNode } from './shim-node.js';
|
|
31
|
+
import { discoverStyleCacheKey } from './style-cache.js';
|
|
18
32
|
// The bag a not-yet-live element is diffed against in onMadeLive. A module constant rather than a
|
|
19
33
|
// fresh `{}` per node: it is only ever read, and the identity lets applyBagDiff skip its second
|
|
20
34
|
// pass outright on the create path.
|
|
@@ -39,12 +53,18 @@ export class ShimElementBase extends ShimNode {
|
|
|
39
53
|
export class ShimElement extends ShimElementBase {
|
|
40
54
|
tagName;
|
|
41
55
|
namespaceURI;
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
attributes;
|
|
47
|
-
|
|
56
|
+
// LAZY, and that is a measured decision, not a style one. A primitive carrying its whole prop
|
|
57
|
+
// surface in the `p` bag never writes this on the create path — but an eager field allocated a
|
|
58
|
+
// Map per element regardless, 9 002 of them on a 1 000-row create, in a window where GC is the
|
|
59
|
+
// largest single bucket (29%).
|
|
60
|
+
attributes = undefined;
|
|
61
|
+
// The four attribute doors write `doorBag`, `set p` writes `pBag`, `lastBag` is the folded merge.
|
|
62
|
+
// Held in ONE object, `set p` deleted the `class` `from_tree` had written at clone time, so
|
|
63
|
+
// `<view p={handlers} class="x">` committed with no style at all and nothing was red. Unreachable
|
|
64
|
+
// while an element used one door, which every lowered tag and adapter component does.
|
|
65
|
+
// Both LAZY: an element touching one door still allocates one object.
|
|
66
|
+
doorBag = undefined;
|
|
67
|
+
pBag = undefined;
|
|
48
68
|
lastBag = EMPTY_BAG;
|
|
49
69
|
constructor(tagName, namespaceURI) {
|
|
50
70
|
super();
|
|
@@ -59,12 +79,26 @@ export class ShimElement extends ShimElementBase {
|
|
|
59
79
|
// `.claude/rules/svelte-shim-is-the-per-node-create-path.md` records about the two Maps below it:
|
|
60
80
|
// an eager field is one object per element, ~9 000 per create, in the window where GC is the
|
|
61
81
|
// largest bucket. Only `set_style` touches this, and no lowered element takes that path.
|
|
62
|
-
styleSlot;
|
|
82
|
+
styleSlot = undefined;
|
|
63
83
|
get style() {
|
|
64
84
|
return (this.styleSlot ??= { cssText: '' });
|
|
65
85
|
}
|
|
86
|
+
// The real `style` seam, reached from the Symbol accessor below rather than from `.style`: that
|
|
87
|
+
// slot only ever sees `String(value)`. Not part of the DOM surface.
|
|
88
|
+
writeStyleValue(value) {
|
|
89
|
+
this.writeBagKey('style', value);
|
|
90
|
+
}
|
|
91
|
+
// `set_class`'s html-namespace exit. Without the setter it lands as a plain JS property, and the
|
|
92
|
+
// failure is half-silent: a static `class="card"` rides the template through `setAttribute` and
|
|
93
|
+
// keeps working, so only the dynamic spelling stops styling.
|
|
94
|
+
get className() {
|
|
95
|
+
return this.lastBag.class;
|
|
96
|
+
}
|
|
97
|
+
set className(value) {
|
|
98
|
+
this.writeBagKey('class', value);
|
|
99
|
+
}
|
|
66
100
|
// The single object-bag prop. The literal name is ours to choose (§3g(c)) — the adapter's
|
|
67
|
-
// own View.svelte/Text.svelte/… emit `<
|
|
101
|
+
// own View.svelte/Text.svelte/… emit `<view p={bag}>`; app code never sees it.
|
|
68
102
|
get p() {
|
|
69
103
|
return this.lastBag;
|
|
70
104
|
}
|
|
@@ -72,7 +106,8 @@ export class ShimElement extends ShimElementBase {
|
|
|
72
106
|
// The fold runs HERE rather than at element creation because an alias can arrive on an update
|
|
73
107
|
// (`id` bound to a signal), and it runs before the diff so `lastBag` is always the folded shape
|
|
74
108
|
// — otherwise a seeded default would look like a change on every single set.
|
|
75
|
-
|
|
109
|
+
this.pBag = bag;
|
|
110
|
+
const next = this.foldedBag();
|
|
76
111
|
const prev = this.lastBag;
|
|
77
112
|
this.lastBag = next;
|
|
78
113
|
// THUNK, not a string (see debug.ts's header): this setter runs once per element per create
|
|
@@ -85,29 +120,93 @@ export class ShimElement extends ShimElementBase {
|
|
|
85
120
|
applyBagDiff(this.engineNode, prev, next);
|
|
86
121
|
this.surface?.requestCommit();
|
|
87
122
|
}
|
|
123
|
+
// A BARE tag's props arrive here, one key at a time, and they used to stop here: an inert Map,
|
|
124
|
+
// nothing routed, nothing committed, nothing red. That was survivable only while every host
|
|
125
|
+
// element in an app was produced by the lowering transform, which builds the `p` bag above —
|
|
126
|
+
// and it is exactly what made that transform load-bearing for CORRECTNESS on this adapter
|
|
127
|
+
// alone. Routing the key makes `<view testID="x">` and `<view p={{ testID: 'x' }}>` the same
|
|
128
|
+
// commit, so the transform goes back to being the optimisation it is everywhere else.
|
|
129
|
+
//
|
|
130
|
+
// `value` is `unknown`, not `string`: Svelte's `set_attribute` hands the raw value straight
|
|
131
|
+
// through for a name with no prototype setter, so an object `style` or a number arrives
|
|
132
|
+
// unstringified. The DOM would coerce it; we must not — `routeProp` wants the real value.
|
|
133
|
+
//
|
|
134
|
+
// The Map stays, and is not redundant: `getAttribute` must answer with the name the caller
|
|
135
|
+
// wrote, while the bag has been folded (`id` -> `nativeID`) by the time it is stored.
|
|
136
|
+
// `name` arrives LOWERCASED for every static attribute on every tag, and for a dynamic one on any
|
|
137
|
+
// tag whose name is not also an SVG element — the compiler settles that, so the repair has to
|
|
138
|
+
// happen here. One Map lookup per attribute write, no allocation; a name already canonical (or
|
|
139
|
+
// one we do not know) is handed on untouched. See `canonical-prop-names.ts`.
|
|
88
140
|
setAttribute(name, value) {
|
|
89
141
|
(this.attributes ??= new Map()).set(name, value);
|
|
142
|
+
this.writeBagKey(CANONICAL_BY_LOWER.get(name) ?? name, value);
|
|
90
143
|
}
|
|
91
144
|
getAttribute(name) {
|
|
92
145
|
return this.attributes?.get(name) ?? null;
|
|
93
146
|
}
|
|
94
147
|
removeAttribute(name) {
|
|
95
148
|
this.attributes?.delete(name);
|
|
149
|
+
// Same repair as the write, or a removal clears a key nothing ever set.
|
|
150
|
+
this.writeBagKey(CANONICAL_BY_LOWER.get(name) ?? name, undefined);
|
|
96
151
|
}
|
|
152
|
+
// One key into the same bag `set p` writes, so the three spellings cannot drift into three
|
|
153
|
+
// representations. Folded on every write because the fold is idempotent by construction
|
|
154
|
+
// (`fold-host-bag.ts` states and tests that) — a bare tag has no other pass that could do it.
|
|
155
|
+
//
|
|
156
|
+
// Not `private`: the prototype accessors installed at the bottom of this file are the third
|
|
157
|
+
// writer, and they are defined from module scope because there are ~290 of them.
|
|
158
|
+
writeBagKey(name, value) {
|
|
159
|
+
const door = { ...this.doorBag };
|
|
160
|
+
if (value === undefined)
|
|
161
|
+
delete door[name];
|
|
162
|
+
else
|
|
163
|
+
door[name] = value;
|
|
164
|
+
this.doorBag = door;
|
|
165
|
+
const prev = this.lastBag;
|
|
166
|
+
const next = this.foldedBag();
|
|
167
|
+
this.lastBag = next;
|
|
168
|
+
if (this.engineNode === undefined)
|
|
169
|
+
return; // not live yet — onMadeLive() replays in full
|
|
170
|
+
applyBagDiff(this.engineNode, prev, next);
|
|
171
|
+
this.surface?.requestCommit();
|
|
172
|
+
}
|
|
173
|
+
// A door key WINS over the same name inside `p`: fixed precedence, so the committed value does
|
|
174
|
+
// not depend on which setter svelte called last. Branched to keep a one-door element at one fold.
|
|
175
|
+
foldedBag() {
|
|
176
|
+
const door = this.doorBag;
|
|
177
|
+
const bag = this.pBag;
|
|
178
|
+
if (door === undefined)
|
|
179
|
+
return foldHostBag(this.tagName, normalizeBagClasses(bag ?? {}));
|
|
180
|
+
if (bag === undefined)
|
|
181
|
+
return foldHostBag(this.tagName, normalizeBagClasses(door));
|
|
182
|
+
return foldHostBag(this.tagName, normalizeBagClasses({ ...bag, ...door }));
|
|
183
|
+
}
|
|
184
|
+
// THE FIFTH DOOR, and it converges on the same bag as the other four. Svelte turns EVERY
|
|
185
|
+
// `on<Name>` attribute into `$.event()`, whatever the name means to us — so this receives both
|
|
186
|
+
// `Press`, which the node's ViewConfig declares as an event, and `ValueChange`, which no config
|
|
187
|
+
// declares and which `behaviors/{switch,text-input}.ts` read as `node.props.onValueChange`.
|
|
188
|
+
// Calling `setEventListener` directly answered "event" for both and stashed the second under a
|
|
189
|
+
// name nothing dispatches to: the toggle moved natively and the app's callback never ran.
|
|
190
|
+
// Only `routeProp` knows which is which, so the handler goes back through the bag under the name
|
|
191
|
+
// the author wrote.
|
|
97
192
|
addEventListener(name, handler) {
|
|
98
|
-
|
|
99
|
-
if (this.engineNode !== undefined)
|
|
100
|
-
setEventListener(this.engineNode, name, handler);
|
|
193
|
+
this.writeBagKey(propNameForEvent(name), handler);
|
|
101
194
|
}
|
|
102
195
|
removeEventListener(name) {
|
|
103
|
-
this.
|
|
104
|
-
if (this.engineNode !== undefined)
|
|
105
|
-
setEventListener(this.engineNode, name, undefined);
|
|
196
|
+
this.writeBagKey(propNameForEvent(name), undefined);
|
|
106
197
|
}
|
|
107
198
|
cloneNode(deep) {
|
|
108
199
|
const clone = new ShimElement(this.tagName, this.namespaceURI);
|
|
109
200
|
if (this.attributes !== undefined)
|
|
110
201
|
clone.attributes = new Map(this.attributes);
|
|
202
|
+
// The bag, not just the Map — `from_tree` builds each template ONCE, writes its static
|
|
203
|
+
// attributes onto that master, and then clones per instance, so without this every attribute
|
|
204
|
+
// an app spells statically (`<view testID="x" class="card">`) is dropped from every instance.
|
|
205
|
+
// A reference copy is safe because `writeBagKey` and `set p` both publish a fresh object.
|
|
206
|
+
// `doorBag` too, or a `set p` on the CLONE re-folds without the master's static attributes.
|
|
207
|
+
clone.doorBag = this.doorBag;
|
|
208
|
+
clone.pBag = this.pBag;
|
|
209
|
+
clone.lastBag = this.lastBag;
|
|
111
210
|
if (deep === true) {
|
|
112
211
|
for (const child of this.children)
|
|
113
212
|
clone.appendChild(child.cloneNode(true));
|
|
@@ -121,22 +220,89 @@ export class ShimElement extends ShimElementBase {
|
|
|
121
220
|
createEngineNode() {
|
|
122
221
|
const descriptor = descriptorFor(this.tagName);
|
|
123
222
|
// The INTRINSIC TAG as the third argument, and this is the only place the tag alphabet still
|
|
124
|
-
// exists: `descriptor.component` is the Fabric view name (`
|
|
223
|
+
// exists: `descriptor.component` is the Fabric view name (`view` -> `RCTView`), so a
|
|
125
224
|
// host-behavior registry keyed by tag can only be reached from here. The argument defaults to
|
|
126
225
|
// `component`, so passing it changes nothing until a behavior is registered.
|
|
127
226
|
return toPublicInstance(createElement(descriptor.component, descriptor.isText, this.tagName));
|
|
128
227
|
}
|
|
228
|
+
// One replay, because a handler is an ordinary bag key now — it arrives before the node is live
|
|
229
|
+
// exactly like `class` or `style` does, and leaves through the same diff.
|
|
129
230
|
onMadeLive() {
|
|
130
231
|
const engineNode = this.engineNode;
|
|
131
232
|
if (engineNode === undefined)
|
|
132
233
|
return;
|
|
133
234
|
applyBagDiff(engineNode, EMPTY_BAG, this.lastBag);
|
|
134
|
-
if (this.domListeners === undefined)
|
|
135
|
-
return;
|
|
136
|
-
for (const [name, handler] of this.domListeners)
|
|
137
|
-
setEventListener(engineNode, name, handler);
|
|
138
235
|
}
|
|
139
236
|
}
|
|
237
|
+
// Svelte hands `addEventListener` the authored name minus its `on`, so `onPress` arrives as
|
|
238
|
+
// `Press`. Putting it back is what lets the handler re-enter through the ordinary prop bag, where
|
|
239
|
+
// `routeProp` decides event-vs-prop from the node's ViewConfig — the one decision this layer
|
|
240
|
+
// cannot make. A name that was already lowercase (`onclick` -> `click`) rebuilds as `onclick`,
|
|
241
|
+
// which no config declares and which therefore lands inertly in `node.props`, exactly as a DOM
|
|
242
|
+
// event name should on a native host.
|
|
243
|
+
//
|
|
244
|
+
// A charCode test rather than `name[0] !== name[0].toUpperCase()`: this runs per handler per
|
|
245
|
+
// element on the create path, and the comparison spelling allocates two strings to answer it.
|
|
246
|
+
function propNameForEvent(name) {
|
|
247
|
+
const first = name.charCodeAt(0);
|
|
248
|
+
if (first >= 65 /* A */ && first <= 90 /* Z */)
|
|
249
|
+
return `on${name}`;
|
|
250
|
+
return `on${name[0].toUpperCase()}${name.slice(1)}`;
|
|
251
|
+
}
|
|
252
|
+
// The seam `set_style` writes the authored value to. Discovered once, at module load, because
|
|
253
|
+
// svelte's key is a module-private Symbol — see `style-cache.ts` for the whole mechanism.
|
|
254
|
+
Object.defineProperty(ShimElement.prototype, discoverStyleCacheKey(), {
|
|
255
|
+
get() {
|
|
256
|
+
return this.p.style;
|
|
257
|
+
},
|
|
258
|
+
set(value) {
|
|
259
|
+
this.writeStyleValue(value);
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
// The setters `get_setters` has to find, so `set_custom_element_data` stops guessing. Its
|
|
263
|
+
// heuristic branch — reached whenever no real setter exists for the name — hands a scalar on as
|
|
264
|
+
// `String(value)` and assigns an object to a plain JS property the shim never reads, so a
|
|
265
|
+
// hyphenated tag committed `"false"` for `multiline={false}` and nothing at all for an object.
|
|
266
|
+
// With a setter present the raw value lands in the same folded bag `set p` and `setAttribute`
|
|
267
|
+
// write, and the three doors agree.
|
|
268
|
+
//
|
|
269
|
+
// ONE-TIME, at module load: ~290 defineProperty calls, nothing per node
|
|
270
|
+
// (`svelte-shim-is-the-per-node-create-path.md`). The instance shape does not move — these live on
|
|
271
|
+
// the prototype — so no element pays for them.
|
|
272
|
+
//
|
|
273
|
+
// A name the shim ALREADY owns is skipped rather than overwritten, and it is owned in TWO places
|
|
274
|
+
// that need two different tests. On the PROTOTYPE: `style` is a `{cssText}` slot `set_style`
|
|
275
|
+
// writes through, `className` is `set_class`'s html exit, `p` is the bag itself — an `in` test
|
|
276
|
+
// covers those, and covers a future member of the chain the day it is added.
|
|
277
|
+
//
|
|
278
|
+
// On the INSTANCE is the one that cost a device crash. `children` is a canonical prop name AND
|
|
279
|
+
// ShimNode's own field — the tree itself — and `in` cannot see it, because a class field lives on
|
|
280
|
+
// the instance and the prototype is empty at module load. Installing an accessor over it is
|
|
281
|
+
// harmless under vitest, where `useDefineForClassFields` makes `this.children = []` a
|
|
282
|
+
// `defineProperty` that shadows the accessor, and FATAL under Metro, whose loose class fields
|
|
283
|
+
// compile it to an assignment that the prototype setter swallows — so `this.children` read back
|
|
284
|
+
// `this.p.children`, i.e. undefined, and `makeLive` died iterating it on the app's ROOT node.
|
|
285
|
+
// Every one of the 328 headless tests passed.
|
|
286
|
+
//
|
|
287
|
+
// The probe is a real instance built before the loop, so it answers with whatever fields the
|
|
288
|
+
// ACTIVE transform materialises rather than a list someone has to keep. Its fields carry explicit
|
|
289
|
+
// `= undefined` initializers for the same reason: a bare declaration emits nothing under Metro and
|
|
290
|
+
// the probe would not see it.
|
|
291
|
+
const SHAPE_PROBE = new ShimElement('view');
|
|
292
|
+
for (const name of CANONICAL_PROP_NAMES) {
|
|
293
|
+
if (name in ShimElement.prototype)
|
|
294
|
+
continue;
|
|
295
|
+
if (Object.hasOwn(SHAPE_PROBE, name))
|
|
296
|
+
continue;
|
|
297
|
+
Object.defineProperty(ShimElement.prototype, name, {
|
|
298
|
+
get() {
|
|
299
|
+
return this.p[name];
|
|
300
|
+
},
|
|
301
|
+
set(value) {
|
|
302
|
+
this.writeBagKey(name, value);
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
}
|
|
140
306
|
// Both spellings route through the engine's shared class+style merge (routeProp's
|
|
141
307
|
// CLASS_PROP_KEYS), so both get the same clsx normalization here.
|
|
142
308
|
const CLASS_BAG_KEYS = ['class', 'className'];
|