@symbiote-native/svelte 3.0.2 → 3.0.4
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 +64 -29
- package/build/components/modal/index.svelte +7 -4
- package/build/components/virtualized-list/index.svelte +5 -2
- package/build/components/virtualized-section-list/index.svelte +3 -6
- package/build/create-portal/index.js +3 -6
- package/build/dom-shim/patch-globals.js +5 -17
- package/build/index.js +5 -8
- package/build/modules/animated/index.js +3 -4
- package/build/preprocessor/collapse-text-whitespace.js +3 -6
- package/build/preprocessor/forbid-web-only-constructs.js +1 -1
- package/build/preprocessor/scoped-styles.js +5 -7
- package/build/render.js +2 -2
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @symbiote-native/svelte
|
|
2
2
|
|
|
3
3
|
The **Svelte adapter** for [SymbioteNative](../../README.md) — render real native iOS/Android views
|
|
4
|
-
from Svelte 5, on the _same_ untouched core as React, Vue, and Angular, with React Native's own
|
|
4
|
+
from Svelte 5, on the _same_ untouched core as React, Vue, Solid, and Angular, with React Native's own
|
|
5
5
|
renderer never in the path. Svelte ships no official host-renderer API yet (`createRenderer` is
|
|
6
6
|
still an unmerged PR upstream), so instead of hooking a framework-blessed extension point this
|
|
7
7
|
adapter patches `globalThis`'s DOM classes: stock compiled Svelte client output believes it is
|
|
@@ -23,35 +23,84 @@ Svelte is the **sharpest test yet** that the core is genuinely framework-agnosti
|
|
|
23
23
|
|
|
24
24
|
## Install
|
|
25
25
|
|
|
26
|
+
```bash
|
|
27
|
+
npx @symbiote-native/cli new my-app --framework svelte
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
One command, nothing to wire by hand: every component here is a `.svelte` SFC with no TSX
|
|
31
|
+
alternative, so the Metro wiring below isn't optional — the generator sets it up, plus
|
|
32
|
+
`@symbiote-native/svelte`/`react-native`/`svelte` as your app's own dependencies.
|
|
33
|
+
|
|
34
|
+
<details>
|
|
35
|
+
<summary>Manual install (no generator — an existing app, or you want to wire it yourself)</summary>
|
|
36
|
+
|
|
26
37
|
```bash
|
|
27
38
|
npm install @symbiote-native/svelte react-native svelte
|
|
28
39
|
```
|
|
29
40
|
|
|
30
41
|
`react-native` and `svelte` stay your app's own top-level dependencies — this package only
|
|
31
|
-
replaces the JS renderer that drives them.
|
|
32
|
-
|
|
33
|
-
sets this up for a new app; wiring it into an existing one still follows
|
|
34
|
-
[`examples/svelte`](../../examples/svelte) rather than a generator:
|
|
42
|
+
replaces the JS renderer that drives them. Follow [`examples/svelte`](../../examples/svelte) for
|
|
43
|
+
both pieces of Metro wiring below; there is no wiring script for an existing app:
|
|
35
44
|
|
|
36
45
|
- `metro.config.js` — point `babelTransformerPath` at
|
|
37
46
|
`@symbiote-native/svelte/metro-svelte-transformer` (compiles `.svelte` on the way into the
|
|
38
47
|
bundle) and disable `inlineRequires` (see [the gotcha
|
|
39
48
|
below](#a-svelte-specific-gotcha--inlinerequires-vs-the-svelte-runtime)).
|
|
40
|
-
- `svelte.config.js` — registers the `forbidWebOnlyConstructs()
|
|
41
|
-
preprocessors, so `svelte-check` and the editor catch the same things
|
|
42
|
-
already guards against at build time
|
|
43
|
-
|
|
49
|
+
- `svelte.config.js` — registers the `forbidWebOnlyConstructs()`, `scopedStyles()`, and
|
|
50
|
+
`collapseTextWhitespace()` preprocessors, so `svelte-check` and the editor catch the same things
|
|
51
|
+
Metro's transformer already guards against at build time (the third one matters beyond
|
|
52
|
+
diagnostics: without it, a sentence wrapped across source lines for readability ships a literal
|
|
53
|
+
newline into the native text content — Svelte doesn't collapse it the way a browser or Vue's
|
|
54
|
+
compiler would). Also sets `compilerOptions: { fragments: 'tree', css: 'external' }` and a
|
|
55
|
+
`warningFilter` that silences `element_invalid_self_closing_tag` for `<pressable />`-style
|
|
56
|
+
self-closing primitives.
|
|
57
|
+
|
|
58
|
+
</details>
|
|
44
59
|
|
|
45
60
|
---
|
|
46
61
|
|
|
47
62
|
## Use it
|
|
48
63
|
|
|
64
|
+
The app is ordinary Svelte 5 — the native primitives are plain intrinsic tags, no import needed.
|
|
65
|
+
Styling is a CSS class against the component's own `<style>` block — the convention every example
|
|
66
|
+
app here follows. A tap→increment counter, using runes:
|
|
67
|
+
|
|
68
|
+
```svelte
|
|
69
|
+
<script lang="ts">
|
|
70
|
+
let count = $state(0);
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<safe-area-view class="screen">
|
|
74
|
+
<text>Taps: {count}</text>
|
|
75
|
+
<pressable onPress={() => count++}>
|
|
76
|
+
<text>Tap me</text>
|
|
77
|
+
</pressable>
|
|
78
|
+
</safe-area-view>
|
|
79
|
+
|
|
80
|
+
<style>
|
|
81
|
+
.screen {
|
|
82
|
+
flex: 1;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
padding: 24px;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
<details>
|
|
91
|
+
<summary>Native entry point (index.js) — already scaffolded by <code>npx @symbiote-native/cli new --framework svelte</code></summary>
|
|
92
|
+
|
|
49
93
|
The native entry reaches the _same_ seam as every other adapter. `createApp(App).mount(appName)`
|
|
50
94
|
wires the native-host seams and RN's own `AppRegistry`, then mounts via `@symbiote-native/engine` —
|
|
51
95
|
RN's own renderer is never in the path:
|
|
52
96
|
|
|
53
97
|
```js
|
|
54
98
|
// index.js
|
|
99
|
+
|
|
100
|
+
// Registers host behaviors (Image, Pressable, Switch, ...) that /bootstrap alone doesn't
|
|
101
|
+
// reach; deleting this breaks them silently (Metro's production inlineRequires makes a
|
|
102
|
+
// side-effect-only barrel import go lazy, see register.ts).
|
|
103
|
+
import '@symbiote-native/svelte';
|
|
55
104
|
import { createApp } from '@symbiote-native/svelte/bootstrap';
|
|
56
105
|
import App from './App.svelte';
|
|
57
106
|
import { name as appName } from './app.json';
|
|
@@ -59,21 +108,7 @@ import { name as appName } from './app.json';
|
|
|
59
108
|
createApp(App).mount(appName);
|
|
60
109
|
```
|
|
61
110
|
|
|
62
|
-
|
|
63
|
-
A tap→increment counter, using runes:
|
|
64
|
-
|
|
65
|
-
```svelte
|
|
66
|
-
<script lang="ts">
|
|
67
|
-
let count = $state(0);
|
|
68
|
-
</script>
|
|
69
|
-
|
|
70
|
-
<view style={{ padding: 24 }}>
|
|
71
|
-
<text>Taps: {count}</text>
|
|
72
|
-
<pressable onPress={() => count++}>
|
|
73
|
-
<text>Tap me</text>
|
|
74
|
-
</pressable>
|
|
75
|
-
</view>
|
|
76
|
-
```
|
|
111
|
+
</details>
|
|
77
112
|
|
|
78
113
|
The full canary is [`examples/svelte`](../../examples/svelte) — a stock RN 0.86 app whose
|
|
79
114
|
[`App.svelte`](../../examples/svelte/App.svelte) exercises the same surface as the React, Vue, and
|
|
@@ -84,8 +119,8 @@ Angular reference canaries.
|
|
|
84
119
|
## Parity — and the one gap
|
|
85
120
|
|
|
86
121
|
Svelte reaches the same primitives, runtime modules, `Animated` on both drivers, gestures,
|
|
87
|
-
accessibility, and the `VirtualizedList` family as React, Vue, and Angular, verified
|
|
88
|
-
iOS and Android. That parity is **structural, not hand-copied**: the component logic (state
|
|
122
|
+
accessibility, and the `VirtualizedList` family as React, Vue, Solid, and Angular, verified
|
|
123
|
+
on-device on iOS and Android. That parity is **structural, not hand-copied**: the component logic (state
|
|
89
124
|
machines + render functions) is written **once** in `@symbiote-native/components`, and Svelte
|
|
90
125
|
supplies only its lifecycle — runes (`$state` / `$derived` / `$effect`) driving hand-authored
|
|
91
126
|
`.svelte` markup that mirrors each `render-*.ts` directly, rather than a generic descriptor bridge
|
|
@@ -124,9 +159,9 @@ guarding it.
|
|
|
124
159
|
|
|
125
160
|
## Run it
|
|
126
161
|
|
|
127
|
-
[`examples/svelte`](../../examples/svelte) is a stock React Native 0.86 app. Requires Node ≥ 22
|
|
128
|
-
and the [RN environment
|
|
129
|
-
CocoaPods):
|
|
162
|
+
[`examples/svelte`](../../examples/svelte) is a stock React Native 0.86 app. Requires Node ≥ 22.13
|
|
163
|
+
(react-native 0.86's own `package.json#engines`) and the [RN environment
|
|
164
|
+
setup](https://reactnative.dev/docs/set-up-your-environment) (Xcode, CocoaPods):
|
|
130
165
|
|
|
131
166
|
```bash
|
|
132
167
|
cd examples/svelte
|
|
@@ -35,23 +35,26 @@
|
|
|
35
35
|
|
|
36
36
|
let rawProps: IModalProps = $props();
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
// Not a $derived candidate despite the $state+$effect shape below: modalReducer folds over the
|
|
39
|
+
// PREVIOUS `state` (self-referential — $derived can't read the value it's replacing) and must
|
|
40
|
+
// run POST-render (see the effect's own comment) so the keep-alive frame survives a commit.
|
|
41
|
+
let localState = $state(createInitialModalState(rawProps.visible === true));
|
|
39
42
|
|
|
40
43
|
const resolved = $derived(resolveAccessibilityProps(rawProps));
|
|
41
44
|
const isVisible = $derived(isModalVisible(resolved.visible));
|
|
42
|
-
const shouldRender = $derived(shouldRenderModal(isVisible,
|
|
45
|
+
const shouldRender = $derived(shouldRenderModal(isVisible, localState));
|
|
43
46
|
|
|
44
47
|
// Arms the iOS keep-alive on show; a hide is left to the native dismiss (state/modal.ts). The
|
|
45
48
|
// reducer is identity-stable, so the mount run triggers no extra render.
|
|
46
49
|
$effect(() => {
|
|
47
50
|
const action = modalVisibilityAction(isVisible);
|
|
48
|
-
if (action !== undefined)
|
|
51
|
+
if (action !== undefined) localState = modalReducer(localState, action);
|
|
49
52
|
});
|
|
50
53
|
|
|
51
54
|
// Modal.js: onDismiss is iOS-only — it drops the keep-alive, then tells the app.
|
|
52
55
|
function handleDismiss(): void {
|
|
53
56
|
if (Platform.OS !== 'ios') return;
|
|
54
|
-
|
|
57
|
+
localState = modalReducer(localState, { type: 'hide' });
|
|
55
58
|
rawProps.onDismiss?.();
|
|
56
59
|
}
|
|
57
60
|
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
//
|
|
13
13
|
// This file authors the raw `scroll-view` intrinsic directly, and needs to: it walks an indexable
|
|
14
14
|
// `plan.cells` list rather than taking an opaque children Snippet, so it can mark sticky cells
|
|
15
|
-
// itself. (There is no ScrollView component to render instead —
|
|
16
|
-
// app writes the tag too.)
|
|
15
|
+
// itself. (There is no ScrollView component to render instead — an app writes the tag too.)
|
|
17
16
|
//
|
|
18
17
|
// It does NOT author the CONTENT node. `registerScrollViewBehavior()` puts a `buildStructure` on
|
|
19
18
|
// the scroll tags, and exactly one thing may build `RCTScrollContentView` — emitting one here as
|
|
@@ -109,6 +108,10 @@
|
|
|
109
108
|
|
|
110
109
|
// The one folded state cell — the Svelte twin of Vue's plain listState / React's stateRef.
|
|
111
110
|
const listState: IListState<ItemT> = createInitialListState<ItemT>();
|
|
111
|
+
// Plain Map by design, like listState above: separatorVersion (below) is the manual
|
|
112
|
+
// invalidation signal a read tracks — a SvelteMap would double-track and still miss nothing,
|
|
113
|
+
// but adds reactive overhead this hot path doesn't need.
|
|
114
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
112
115
|
const separatorOverrides = new Map<number, Partial<ISeparatorProps<ItemT>>>();
|
|
113
116
|
let viewableTimer: ReturnType<typeof setTimeout> | null = null;
|
|
114
117
|
let batchTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -63,12 +63,9 @@
|
|
|
63
63
|
Platform.OS,
|
|
64
64
|
),
|
|
65
65
|
);
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
// actually read — isolated verification proved bare-shorthand forwarding through this exact
|
|
70
|
-
// two-hop generic-component chain works correctly in a standalone repro, so this must show
|
|
71
|
-
// what differs in the real component tree.
|
|
66
|
+
// Diagnostic: logs every INPUT this $derived reads, not just VirtualizedList's own result —
|
|
67
|
+
// `stickyHeaderIndices` can come back undefined on Android with the bare
|
|
68
|
+
// `stickySectionHeadersEnabled` shorthand, and only the inputs show what differs.
|
|
72
69
|
$effect(() => {
|
|
73
70
|
dlog(
|
|
74
71
|
`VirtualizedSectionList sticky-inputs enabled=${String(props.stickySectionHeadersEnabled)} ` +
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
// Portal — the Svelte adapter's same-surface portal: the twin of React's `createPortal
|
|
2
|
-
//
|
|
3
|
-
// (
|
|
4
|
-
// pair. This adapter shipped `createTunnel` and no portal in any spelling until 2026-08-20; the
|
|
5
|
-
// two do NOT overlap (see the table at the bottom of this header), so the tunnel was never a
|
|
6
|
-
// substitute.
|
|
1
|
+
// Portal — the Svelte adapter's same-surface portal: the twin of React's `createPortal`, Solid's
|
|
2
|
+
// `<Portal mount={…}>` and Angular's PortalDirective/PortalOutletDirective pair. Does NOT overlap
|
|
3
|
+
// with `createTunnel` (table at the bottom of this header) — the tunnel is not a substitute.
|
|
7
4
|
//
|
|
8
5
|
// WHY IT IS A COMPONENT (`<Portal mount={…}>`) AND NOT A CALL (`createPortal(content, target)`).
|
|
9
6
|
// NOT for Solid's reason. Solid had to avoid the call form because Solid evaluates JSX eagerly at
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// (svelte-adapter-dom-shim skill §2, §3a, §6). Must run before `mount()` (§3f) — not
|
|
3
3
|
// necessarily before `svelte` is imported.
|
|
4
4
|
//
|
|
5
|
-
// Single-root only, by design (§10
|
|
6
|
-
//
|
|
7
|
-
// `unmount()` calls it unconditionally.
|
|
5
|
+
// Single-root only, by design (§10): one Symbiote app = one process = one Svelte root, so
|
|
6
|
+
// `restoreGlobals()` needs no ref-counting — `unmount()` calls it unconditionally.
|
|
8
7
|
//
|
|
9
8
|
// Deliberately NOT patched, and must stay that way (§6b, §6c, §6d) — both verified against the
|
|
10
9
|
// real vendored RN source (.vendors/react-native/packages/react-native/Libraries/Core/), not
|
|
@@ -51,20 +50,9 @@ const PATCHED_KEYS = [
|
|
|
51
50
|
// with (unlike `navigator`/`requestAnimationFrame`, verified against .vendors/react-native —
|
|
52
51
|
// see the header comment above), so patching this is safe.
|
|
53
52
|
//
|
|
54
|
-
// `get()` returns a TRUTHY stand-in
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
// setters_cache.has(nodeName) || !customElements || customElements.get(nodeName)
|
|
58
|
-
// ? get_setters(node).includes(prop) <- a real setter decides; the value passes through
|
|
59
|
-
// : value && typeof value === 'object' <- a HEURISTIC: an object is set as a property and
|
|
60
|
-
// every scalar is handed on as String(value)
|
|
61
|
-
//
|
|
62
|
-
// Returning `undefined` — which it did until 2026-09-07 — selects the heuristic, so
|
|
63
|
-
// `<text-input multiline={false}>` committed the STRING "false" and an object-valued prop with no
|
|
64
|
-
// prototype setter was assigned to a plain JS property and silently lost. Anything truthy steers
|
|
65
|
-
// it to `get_setters`, which reaches `ShimElement.prototype`'s accessors (see
|
|
66
|
-
// `svelte-shim-element-global-must-be-an-ancestor.md` for why that walk terminates where it does).
|
|
67
|
-
// One reader in the whole client runtime, so nothing else is affected.
|
|
53
|
+
// `get()` returns a TRUTHY stand-in — the whole reason `set_custom_element_data` stays lossless.
|
|
54
|
+
// Falsy selects attributes.js's HEURISTIC path instead of `get_setters`: it stringifies scalars
|
|
55
|
+
// and drops any object-valued prop with no prototype setter.
|
|
68
56
|
const FAKE_CUSTOM_ELEMENT_REGISTRY = {
|
|
69
57
|
get: () => ShimElement,
|
|
70
58
|
};
|
package/build/index.js
CHANGED
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
// All Fabric clone-on-write lives in the engine, shared cross-adapter. App code names only
|
|
5
5
|
// @symbiote-native/svelte.
|
|
6
6
|
//
|
|
7
|
-
// Full component parity with React/Vue/Angular
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// markup mirroring its render-*.ts, reusing only the pure state/render logic. See skill §17/§18
|
|
11
|
-
// for the one open async-exception repro and the honest cross-cutting gap list.
|
|
7
|
+
// Full component parity with React/Vue/Angular (svelte-adapter-dom-shim skill §15): every
|
|
8
|
+
// `core/components` render function has a fixed tree shape, so no `descriptorToSvelte` bridge is
|
|
9
|
+
// needed — each component below is hand-authored Svelte markup mirroring its render-*.ts.
|
|
12
10
|
// Bare side-effect import, deliberately NOT a re-export and deliberately not beside one of the
|
|
13
11
|
// same specifier: it registers the press behavior with the engine's host-behavior registry. Any
|
|
14
12
|
// other shape is dropped by Metro's inlineRequires in a release build — see register.ts.
|
|
@@ -25,9 +23,8 @@ export { KeyboardAvoidingView, Modal, VirtualizedList, FlatList, VirtualizedSect
|
|
|
25
23
|
// threaded through this barrel (parity fix, see React's index.ts lines 55-58, 89, 245-250).
|
|
26
24
|
export { setImageSourceResolver } from '@symbiote-native/components';
|
|
27
25
|
// Pure, framework-agnostic runtime modules — same re-export every other adapter does per
|
|
28
|
-
// CLAUDE.md's <runtime_modules_layering>.
|
|
29
|
-
//
|
|
30
|
-
// adding it is a one-line parity fix, not new adapter work.
|
|
26
|
+
// CLAUDE.md's <runtime_modules_layering>. A pure engine re-export with no framework glue, so
|
|
27
|
+
// adding one is a one-line parity fix, not new adapter work.
|
|
31
28
|
export { Platform, StyleSheet, PlatformColor, DynamicColorIOS, processColor, setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, PixelRatio, PanResponder, dlog, isDebug, } from '@symbiote-native/engine';
|
|
32
29
|
// The two namespaces left behind by primitives that became tags, here rather than in the component
|
|
33
30
|
// block above for one reason: they carry no view. `Image` gets an adapter file only because it
|
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
// between the value graph and the node's Fabric tag, and both of those were already engine-side.
|
|
10
10
|
//
|
|
11
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
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// is `<scroll-view style={{ opacity: v }} p={{ onScroll: Animated.event(…) }}>`.
|
|
12
|
+
// which is not a value `<Animated.X>` can call. `Animated.ScrollView` left the same way:
|
|
13
|
+
// `bindAnimatedEvent` (node.ts) attaches a native-driven event on ANY host node.
|
|
14
|
+
// The whole API is `<scroll-view style={{ opacity: v }} p={{ onScroll: Animated.event(…) }}>`.
|
|
16
15
|
//
|
|
17
16
|
// The two below REMAIN because the LIST family is tier 3: a render prop decides their output shape
|
|
18
17
|
// in JS, so there is no tag for `<Animated.X>` to be an alias of.
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
// svelte-adapter-dom-shim skill §16: Svelte trims only the leading/trailing whitespace of a
|
|
2
2
|
// fragment's own children - never whitespace INSIDE a single Text node, and never a
|
|
3
|
-
// whitespace-only Text node BETWEEN two siblings. Vue's compiler (and every browser)
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// `$.text('Hello world, this is a\n long sentence.')` - the newline and indent ship into the
|
|
7
|
-
// native text content and render as a forced line break plus stray leading spaces on device.
|
|
8
|
-
// Reproduced live on an iOS simulator before this file existed; fixed with it registered.
|
|
3
|
+
// whitespace-only Text node BETWEEN two siblings. Vue's compiler (and every browser) collapse both.
|
|
4
|
+
// A Text node authored as `Hello world, this is a\n long sentence.` compiles verbatim, so the
|
|
5
|
+
// newline and indent ship into native text as a forced line break plus stray leading spaces.
|
|
9
6
|
//
|
|
10
7
|
// Two hazards:
|
|
11
8
|
// 1. Wrapped sentence - a Text node with real content plus an embedded whitespace run
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// §7 of svelte-adapter-dom-shim
|
|
1
|
+
// §7 of svelte-adapter-dom-shim: the constructs that compile cleanly and
|
|
2
2
|
// then silently do nothing under the DOM shim. Everything else in the original §4 table is
|
|
3
3
|
// already closed by "app code can't reach it" or "the TS/Svelte compiler already errors on it for
|
|
4
4
|
// free" (see the skill for the full reachability table). This preprocessor is the guard for what
|
|
@@ -127,13 +127,11 @@ export function scopedStyles() {
|
|
|
127
127
|
// ---------------------------------------------------------------------------------------------
|
|
128
128
|
// The <style> block, located WITHOUT parsing it.
|
|
129
129
|
//
|
|
130
|
-
// This is svelte's own tag regex (src/compiler/preprocess/index.js)
|
|
131
|
-
// `preprocess()` style hook uses to hand a block's content
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
// consulted, so only SCSS that is already valid CSS (nesting) survived. A `<!-- -->` comment is an
|
|
136
|
-
// alternative of the pattern purely so a commented-out block is skipped rather than matched.
|
|
130
|
+
// This is svelte's own tag regex (src/compiler/preprocess/index.js): what its official
|
|
131
|
+
// `preprocess()` style hook uses to hand a block's content unparsed, the reason `svelte-preprocess`
|
|
132
|
+
// can compile SCSS at all — `parse().css` would validate it as CSS whatever `lang` says.
|
|
133
|
+
// A `<!-- -->` comment is an alternative of the pattern purely so a commented-out block is
|
|
134
|
+
// skipped rather than matched.
|
|
137
135
|
const STYLE_TAG_PATTERN = /<!--[^]*?-->|<style((?:\s+[^=>'"/\s]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/\s]+)*\s*)(?:\/>|>([\S\s]*?)<\/style>)/g;
|
|
138
136
|
const LANG_ATTRIBUTE_PATTERN = /\blang\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/;
|
|
139
137
|
function findStyleBlock(content) {
|
package/build/render.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Mount a Svelte app onto a Fabric surface. The native host hands us a rootTag; we create a
|
|
2
2
|
// surface for it, install the DOM shim, and let stock compiled Svelte output drive it while
|
|
3
|
-
// believing it is talking to the real DOM.
|
|
4
|
-
// (
|
|
3
|
+
// believing it is talking to the real DOM.
|
|
4
|
+
// Single root per process (svelte-adapter-dom-shim skill §10), so
|
|
5
5
|
// patchGlobals()/restoreGlobals() need no ref-counting.
|
|
6
6
|
import { mount as svelteMount, unmount as svelteUnmount, } from 'svelte';
|
|
7
7
|
import { createSurface, disposeRoot, dlog, reportUncaughtError, } from '@symbiote-native/engine';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/svelte",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
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
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -73,22 +73,22 @@
|
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"typescript": "~6.0.0",
|
|
76
|
-
"@symbiote-native/components": "3.1.
|
|
77
|
-
"@symbiote-native/css-parser": "0.5.
|
|
76
|
+
"@symbiote-native/components": "3.1.2",
|
|
77
|
+
"@symbiote-native/css-parser": "0.5.2"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
80
|
"svelte": ">=5.56.0",
|
|
81
81
|
"react-native": ">=0.86",
|
|
82
|
-
"@symbiote-native/engine": "^1.3.
|
|
82
|
+
"@symbiote-native/engine": "^1.3.1"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@vue/runtime-core": "^3.5.13",
|
|
86
86
|
"react": "19.2.3",
|
|
87
87
|
"svelte": "^5.56.0",
|
|
88
|
-
"@symbiote-native/engine": "1.3.
|
|
89
|
-
"@symbiote-native/
|
|
90
|
-
"@symbiote-native/
|
|
91
|
-
"@symbiote-native/vue": "3.0.
|
|
88
|
+
"@symbiote-native/engine": "1.3.1",
|
|
89
|
+
"@symbiote-native/react": "3.0.4",
|
|
90
|
+
"@symbiote-native/test-utils": "0.4.4",
|
|
91
|
+
"@symbiote-native/vue": "3.0.4"
|
|
92
92
|
},
|
|
93
93
|
"scripts": {
|
|
94
94
|
"typecheck": "tsc --build",
|