@symbiote-native/svelte 0.1.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/LICENSE +21 -0
- package/README.md +161 -0
- package/build/bootstrap.d.ts +7 -0
- package/build/bootstrap.js +17 -0
- package/build/class-value.d.ts +6 -0
- package/build/class-value.js +81 -0
- package/build/components/RefreshControl.svelte +38 -0
- package/build/components/RefreshControl.svelte.d.ts +5 -0
- package/build/components/SafeAreaView.svelte +34 -0
- package/build/components/SafeAreaView.svelte.d.ts +5 -0
- package/build/components/Text.svelte +25 -0
- package/build/components/Text.svelte.d.ts +5 -0
- package/build/components/View.svelte +41 -0
- package/build/components/View.svelte.d.ts +5 -0
- package/build/components/activity-indicator/activity-indicator-platform.android.d.ts +3 -0
- package/build/components/activity-indicator/activity-indicator-platform.android.js +5 -0
- package/build/components/activity-indicator/activity-indicator-platform.d.ts +1 -0
- package/build/components/activity-indicator/activity-indicator-platform.ios.d.ts +3 -0
- package/build/components/activity-indicator/activity-indicator-platform.ios.js +6 -0
- package/build/components/activity-indicator/activity-indicator-platform.js +5 -0
- package/build/components/activity-indicator/activity-indicator-props.d.ts +5 -0
- package/build/components/activity-indicator/activity-indicator-props.js +1 -0
- package/build/components/activity-indicator/index.svelte +57 -0
- package/build/components/activity-indicator/index.svelte.d.ts +5 -0
- package/build/components/button-props.d.ts +5 -0
- package/build/components/button-props.js +1 -0
- package/build/components/button.svelte +71 -0
- package/build/components/button.svelte.d.ts +5 -0
- package/build/components/flat-list/flat-list-props.d.ts +10 -0
- package/build/components/flat-list/flat-list-props.js +1 -0
- package/build/components/flat-list/index.svelte +254 -0
- package/build/components/flat-list/index.svelte.d.ts +79 -0
- package/build/components/image/image-logic.d.ts +2 -0
- package/build/components/image/image-logic.js +125 -0
- package/build/components/image/image-props.d.ts +5 -0
- package/build/components/image/image-props.js +1 -0
- package/build/components/image/index.d.ts +3 -0
- package/build/components/image/index.js +10 -0
- package/build/components/image/index.svelte +66 -0
- package/build/components/image/index.svelte.d.ts +5 -0
- package/build/components/image-background/image-background-props.d.ts +10 -0
- package/build/components/image-background/image-background-props.js +1 -0
- package/build/components/image-background/index.svelte +81 -0
- package/build/components/image-background/index.svelte.d.ts +5 -0
- package/build/components/index.d.ts +50 -0
- package/build/components/index.js +24 -0
- package/build/components/input-accessory-view/index.svelte +48 -0
- package/build/components/input-accessory-view/index.svelte.d.ts +5 -0
- package/build/components/input-accessory-view/input-accessory-view-props.d.ts +11 -0
- package/build/components/input-accessory-view/input-accessory-view-props.js +1 -0
- package/build/components/keyboard-avoiding-view/index.svelte +135 -0
- package/build/components/keyboard-avoiding-view/index.svelte.d.ts +5 -0
- package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.d.ts +15 -0
- package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.js +1 -0
- package/build/components/modal/index.svelte +126 -0
- package/build/components/modal/index.svelte.d.ts +5 -0
- package/build/components/modal/modal-props.d.ts +24 -0
- package/build/components/modal/modal-props.js +1 -0
- package/build/components/pressable/index.svelte +160 -0
- package/build/components/pressable/index.svelte.d.ts +5 -0
- package/build/components/pressable/pressable-props.d.ts +27 -0
- package/build/components/pressable/pressable-props.js +1 -0
- package/build/components/refresh-control-props.d.ts +19 -0
- package/build/components/refresh-control-props.js +1 -0
- package/build/components/safe-area-view-props.d.ts +10 -0
- package/build/components/safe-area-view-props.js +1 -0
- package/build/components/scroll-view/index.svelte +298 -0
- package/build/components/scroll-view/index.svelte.d.ts +18 -0
- package/build/components/scroll-view/scroll-view-platform-types.d.ts +3 -0
- package/build/components/scroll-view/scroll-view-platform-types.js +1 -0
- package/build/components/scroll-view/scroll-view-platform.android.d.ts +3 -0
- package/build/components/scroll-view/scroll-view-platform.android.js +3 -0
- package/build/components/scroll-view/scroll-view-platform.d.ts +1 -0
- package/build/components/scroll-view/scroll-view-platform.ios.d.ts +3 -0
- package/build/components/scroll-view/scroll-view-platform.ios.js +3 -0
- package/build/components/scroll-view/scroll-view-platform.js +4 -0
- package/build/components/scroll-view/scroll-view-props.d.ts +78 -0
- package/build/components/scroll-view/scroll-view-props.js +1 -0
- package/build/components/scroll-view/scroll-view-sticky-context.d.ts +7 -0
- package/build/components/scroll-view/scroll-view-sticky-context.js +1 -0
- package/build/components/scroll-view/sticky-header-props.d.ts +10 -0
- package/build/components/scroll-view/sticky-header-props.js +1 -0
- package/build/components/scroll-view/sticky-header.svelte +254 -0
- package/build/components/scroll-view/sticky-header.svelte.d.ts +5 -0
- package/build/components/section-list/index.svelte +107 -0
- package/build/components/section-list/index.svelte.d.ts +57 -0
- package/build/components/section-list/section-list-props.d.ts +1 -0
- package/build/components/section-list/section-list-props.js +1 -0
- package/build/components/switch/index.svelte +123 -0
- package/build/components/switch/index.svelte.d.ts +5 -0
- package/build/components/switch/switch-platform-types.d.ts +5 -0
- package/build/components/switch/switch-platform-types.js +1 -0
- package/build/components/switch/switch-platform.android.d.ts +3 -0
- package/build/components/switch/switch-platform.android.js +8 -0
- package/build/components/switch/switch-platform.d.ts +1 -0
- package/build/components/switch/switch-platform.ios.d.ts +3 -0
- package/build/components/switch/switch-platform.ios.js +7 -0
- package/build/components/switch/switch-platform.js +5 -0
- package/build/components/switch/switch-props.d.ts +5 -0
- package/build/components/switch/switch-props.js +1 -0
- package/build/components/text-input/index.svelte +300 -0
- package/build/components/text-input/index.svelte.d.ts +12 -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 +23 -0
- package/build/components/text-props.js +1 -0
- package/build/components/touchable-base-props.d.ts +10 -0
- package/build/components/touchable-base-props.js +1 -0
- package/build/components/touchable-highlight/index.svelte +39 -0
- package/build/components/touchable-highlight/index.svelte.d.ts +5 -0
- package/build/components/touchable-highlight/touchable-highlight-props.d.ts +5 -0
- package/build/components/touchable-highlight/touchable-highlight-props.js +1 -0
- package/build/components/touchable-native-feedback/index.d.ts +9 -0
- package/build/components/touchable-native-feedback/index.js +14 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +8 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback.svelte +44 -0
- package/build/components/touchable-native-feedback/touchable-native-feedback.svelte.d.ts +5 -0
- package/build/components/touchable-opacity/index.svelte +124 -0
- package/build/components/touchable-opacity/index.svelte.d.ts +5 -0
- package/build/components/touchable-opacity/touchable-opacity-props.d.ts +5 -0
- package/build/components/touchable-opacity/touchable-opacity-props.js +1 -0
- package/build/components/touchable-without-feedback/index.svelte +19 -0
- package/build/components/touchable-without-feedback/index.svelte.d.ts +5 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +2 -0
- package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
- package/build/components/view-props.d.ts +29 -0
- package/build/components/view-props.js +1 -0
- package/build/components/virtualized-list/index.svelte +702 -0
- package/build/components/virtualized-list/index.svelte.d.ts +79 -0
- package/build/components/virtualized-list/virtualized-list-props.d.ts +70 -0
- package/build/components/virtualized-list/virtualized-list-props.js +73 -0
- package/build/components/virtualized-section-list/index.svelte +206 -0
- package/build/components/virtualized-section-list/index.svelte.d.ts +57 -0
- package/build/components/virtualized-section-list/virtualized-section-list-props.d.ts +62 -0
- package/build/components/virtualized-section-list/virtualized-section-list-props.js +1 -0
- package/build/create-tunnel/index.d.ts +3 -0
- package/build/create-tunnel/index.js +3 -0
- package/build/create-tunnel/tunnel-in.svelte +24 -0
- package/build/create-tunnel/tunnel-in.svelte.d.ts +9 -0
- package/build/create-tunnel/tunnel-out.svelte +14 -0
- package/build/create-tunnel/tunnel-out.svelte.d.ts +7 -0
- package/build/create-tunnel/tunnel.d.ts +9 -0
- package/build/create-tunnel/tunnel.js +9 -0
- package/build/descriptor-to-svelte.d.ts +7 -0
- package/build/descriptor-to-svelte.js +104 -0
- package/build/dom-shim/comment.d.ts +9 -0
- package/build/dom-shim/comment.js +24 -0
- package/build/dom-shim/document-fragment.d.ts +8 -0
- package/build/dom-shim/document-fragment.js +23 -0
- package/build/dom-shim/document.d.ts +23 -0
- package/build/dom-shim/document.js +61 -0
- package/build/dom-shim/element.d.ts +22 -0
- package/build/dom-shim/element.js +130 -0
- package/build/dom-shim/index.d.ts +5 -0
- package/build/dom-shim/index.js +7 -0
- package/build/dom-shim/patch-globals.d.ts +2 -0
- package/build/dom-shim/patch-globals.js +94 -0
- package/build/dom-shim/shim-node.d.ts +30 -0
- package/build/dom-shim/shim-node.js +207 -0
- package/build/dom-shim/text.d.ts +14 -0
- package/build/dom-shim/text.js +97 -0
- package/build/host-instance.d.ts +5 -0
- package/build/host-instance.js +48 -0
- package/build/index.d.ts +28 -0
- package/build/index.js +77 -0
- package/build/modules/accessibility-info/index.android.d.ts +2 -0
- package/build/modules/accessibility-info/index.android.js +4 -0
- package/build/modules/accessibility-info/index.d.ts +1 -0
- package/build/modules/accessibility-info/index.ios.d.ts +2 -0
- package/build/modules/accessibility-info/index.ios.js +3 -0
- package/build/modules/accessibility-info/index.js +5 -0
- package/build/modules/animated/AnimatedImage.svelte +115 -0
- package/build/modules/animated/AnimatedImage.svelte.d.ts +5 -0
- package/build/modules/animated/AnimatedScrollView.svelte +92 -0
- package/build/modules/animated/AnimatedScrollView.svelte.d.ts +17 -0
- package/build/modules/animated/AnimatedText.svelte +58 -0
- package/build/modules/animated/AnimatedText.svelte.d.ts +5 -0
- package/build/modules/animated/AnimatedView.svelte +95 -0
- package/build/modules/animated/AnimatedView.svelte.d.ts +5 -0
- package/build/modules/animated/animated-component-props.d.ts +7 -0
- package/build/modules/animated/animated-component-props.js +1 -0
- package/build/modules/animated/index.d.ts +58 -0
- package/build/modules/animated/index.js +69 -0
- package/build/modules/app-registry/index.d.ts +7 -0
- package/build/modules/app-registry/index.js +28 -0
- package/build/modules/status-bar/index.d.ts +5 -0
- package/build/modules/status-bar/index.js +21 -0
- package/build/modules/status-bar/index.svelte +20 -0
- package/build/modules/status-bar/index.svelte.d.ts +4 -0
- package/build/native-view-bridge.d.ts +4 -0
- package/build/native-view-bridge.js +27 -0
- package/build/preprocessor/forbid-web-only-constructs.d.ts +8 -0
- package/build/preprocessor/forbid-web-only-constructs.js +204 -0
- package/build/preprocessor/scoped-styles.d.ts +10 -0
- package/build/preprocessor/scoped-styles.js +399 -0
- package/build/render.d.ts +7 -0
- package/build/render.js +46 -0
- package/build/root-element.d.ts +3 -0
- package/build/root-element.js +27 -0
- package/build/runes/attachments.d.ts +5 -0
- package/build/runes/attachments.js +73 -0
- package/build/runes/dimensions-value.d.ts +4 -0
- package/build/runes/dimensions-value.js +30 -0
- package/build/runes/media-query.d.ts +8 -0
- package/build/runes/media-query.js +38 -0
- package/build/runes/use-color-scheme.svelte.d.ts +4 -0
- package/build/runes/use-color-scheme.svelte.js +25 -0
- package/build/runes/use-window-dimensions.svelte.d.ts +4 -0
- package/build/runes/use-window-dimensions.svelte.js +43 -0
- package/build/runes/window.d.ts +6 -0
- package/build/runes/window.js +28 -0
- package/build/scope-token.d.ts +2 -0
- package/build/scope-token.js +33 -0
- package/build/style-scope.d.ts +1 -0
- package/build/style-scope.js +29 -0
- package/metro-css-parser.cjs +9 -0
- package/metro-svelte-transformer.cjs +156 -0
- package/package.json +83 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// The one place a shim node is bound EAGERLY instead of lazily (svelte-adapter-dom-shim skill
|
|
2
|
+
// §9, §10). Every other shim node only gets its ISymbioteNode on first insertion under a live
|
|
3
|
+
// parent; the root has no parent to inherit liveness from — it IS the top of the live tree, so
|
|
4
|
+
// it must be live from the moment it exists. This is what Svelte's `mount(Component, { target })`
|
|
5
|
+
// receives as `target`.
|
|
6
|
+
//
|
|
7
|
+
// `makeLive` already does exactly the right thing generically (create the engine node, replay
|
|
8
|
+
// accumulated state, recurse into children) — it just never appends the result anywhere, since
|
|
9
|
+
// normally an ancestor's `insertOne` does that. For the root there is no ancestor shim node;
|
|
10
|
+
// the surface plays that role, so this file makes the one `surface.appendChild` call directly.
|
|
11
|
+
import { ShimElement } from './dom-shim/index.js';
|
|
12
|
+
const ROOT_INTRINSIC = 'symbiote-view';
|
|
13
|
+
// Unlike Vue/React, whose app root mounts directly onto the surface (its own flex:1
|
|
14
|
+
// class/style reaches the engine's synthetic flex:1 AppContainer with nothing in between),
|
|
15
|
+
// Svelte's compiled output needs a real DOM-like `target` to call appendChild on — this
|
|
16
|
+
// wrapper. Without flex:1 here, a flex:1-styled app root nested one level inside it has no
|
|
17
|
+
// resolved parent height to grow into and the whole tree collapses to 0x0: every prop still
|
|
18
|
+
// commits correctly (nothing throws), so this only shows up as a blank, untappable screen.
|
|
19
|
+
const ROOT_WRAPPER_STYLE = { flex: 1 };
|
|
20
|
+
export function createRootShimElement(surface) {
|
|
21
|
+
const root = new ShimElement(ROOT_INTRINSIC);
|
|
22
|
+
root.p = { style: ROOT_WRAPPER_STYLE };
|
|
23
|
+
const engineNode = root.makeLive(surface);
|
|
24
|
+
surface.appendChild(engineNode);
|
|
25
|
+
surface.requestCommit();
|
|
26
|
+
return root;
|
|
27
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ShimElement } from '../dom-shim';
|
|
2
|
+
type IAttachmentSync = (host: ShimElement | null | undefined, props: object) => void;
|
|
3
|
+
export declare function createAttachmentsSync(): IAttachmentSync;
|
|
4
|
+
export declare function pickAttachmentProps(props: object): Record<symbol, unknown>;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// `{@attach fn}` support for this adapter's components.
|
|
2
|
+
//
|
|
3
|
+
// WHY IT IS THE ONLY ROUTE. Svelte's compiler rejects `use:` / `transition:` / `class:` /
|
|
4
|
+
// `style:` on a COMPONENT ("This type of directive is not valid on components"), and app code
|
|
5
|
+
// here never authors a host element (skill §7), so a directive is unreachable by construction.
|
|
6
|
+
// Attachments are the exception: `{@attach fn}` compiles on a component, and `fromAction` from
|
|
7
|
+
// `svelte/attachments` converts any third-party action into one. That makes this the single seam
|
|
8
|
+
// through which a Svelte author reaches the committed native node, the twin of Vue's
|
|
9
|
+
// user-defined custom directive (which works untouched in adapters/vue).
|
|
10
|
+
//
|
|
11
|
+
// THE MECHANISM. `{@attach fn}` compiles to a prop whose key is a SYMBOL created by
|
|
12
|
+
// `createAttachmentKey()`, i.e. `Symbol(ATTACHMENT_KEY)`; Svelte's own spread path identifies one
|
|
13
|
+
// by `symbol.description === ATTACHMENT_KEY` (internal/client/dom/elements/attributes.js). Both
|
|
14
|
+
// `rest_props` and `spread_props` expose symbol keys through their `ownKeys` traps, so
|
|
15
|
+
// `Object.getOwnPropertySymbols()` over `$props()` — or over the `...rest` object — finds them.
|
|
16
|
+
//
|
|
17
|
+
// WHY IT DELEGATES TO SVELTE'S OWN `attach()` RATHER THAN CALLING THE FUNCTION DIRECTLY. A
|
|
18
|
+
// DYNAMIC attachment expression does not compile to a changing prop VALUE; the compiler emits a
|
|
19
|
+
// STABLE wrapper and moves the reactivity inside it:
|
|
20
|
+
//
|
|
21
|
+
// {@attach which === 'first' ? first : second}
|
|
22
|
+
// -> [$.attachment()]: ($$node) => ($.get(which) === 'first' ? first : second)($$node)
|
|
23
|
+
//
|
|
24
|
+
// So identity-diffing the prop can never see the swap — the read that changes lives in the
|
|
25
|
+
// attachment BODY. `attach(node, getFn)` (internal/client/dom/elements/attachments.js) is exactly
|
|
26
|
+
// the machinery that handles this: a managed effect around `getFn()` for the identity case, and a
|
|
27
|
+
// branch effect around `fn(node)` so a reactive read inside the body re-runs it with the previous
|
|
28
|
+
// teardown fired first. Reimplementing it would be reimplementing it wrong.
|
|
29
|
+
//
|
|
30
|
+
// WHY A PLAIN `.ts` HERE AND NOT A `.svelte.ts` RUNE. The `$effect` stays at the component call
|
|
31
|
+
// site; this file holds no rune syntax. `.svelte.ts` files are compiled by `compileModule` in
|
|
32
|
+
// Metro (metro-svelte-transformer.cjs) but NOT by vitest, which has no Svelte plugin — so a rune
|
|
33
|
+
// file imported by every component would break every smoke test in this package. Same split, and
|
|
34
|
+
// the same call shape, as `createDescriptorChildrenSync`.
|
|
35
|
+
import { attach } from 'svelte/internal/client';
|
|
36
|
+
import { createAttachmentKey } from 'svelte/attachments';
|
|
37
|
+
// Read off a real key rather than hardcoded: `ATTACHMENT_KEY` lives in svelte/src/constants.js
|
|
38
|
+
// and is not part of the public export surface, so a rename there would otherwise silently
|
|
39
|
+
// disable every attachment. The literal is the fallback for a Svelte build that stops giving the
|
|
40
|
+
// key a description at all.
|
|
41
|
+
const ATTACHMENT_KEY_DESCRIPTION = createAttachmentKey().description ?? '@attach';
|
|
42
|
+
// Call once during component init, then drive it from an `$effect`:
|
|
43
|
+
//
|
|
44
|
+
// const syncAttachments = createAttachmentsSync();
|
|
45
|
+
// $effect(() => { syncAttachments(hostShim, rest); });
|
|
46
|
+
//
|
|
47
|
+
// That effect depends ONLY on the host ref — the attachment values are read lazily inside
|
|
48
|
+
// `attach`'s own effects, never in the caller's body — so it re-runs only when the host node
|
|
49
|
+
// itself changes, and Svelte then destroys the previous run's child effects (firing every
|
|
50
|
+
// teardown) before the new node is attached to.
|
|
51
|
+
export function createAttachmentsSync() {
|
|
52
|
+
return (host, props) => {
|
|
53
|
+
if (host === null || host === undefined)
|
|
54
|
+
return;
|
|
55
|
+
for (const key of attachmentKeys(props)) {
|
|
56
|
+
attach(host, () => Reflect.get(props, key));
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// For a component that delegates to another Symbiote component by naming individual props
|
|
61
|
+
// instead of spreading (the list family, whose wrappers forward ~30 named props, and anything
|
|
62
|
+
// rebuilding its props through a plain string-keyed Record such as Animated's `reduceProps`).
|
|
63
|
+
// Symbol keys do not survive either, so the wrapper re-spreads just the attachment ones.
|
|
64
|
+
export function pickAttachmentProps(props) {
|
|
65
|
+
const forwarded = {};
|
|
66
|
+
for (const key of attachmentKeys(props)) {
|
|
67
|
+
forwarded[key] = Reflect.get(props, key);
|
|
68
|
+
}
|
|
69
|
+
return forwarded;
|
|
70
|
+
}
|
|
71
|
+
function attachmentKeys(props) {
|
|
72
|
+
return Object.getOwnPropertySymbols(props).filter(key => key.description === ATTACHMENT_KEY_DESCRIPTION);
|
|
73
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// The one plumbing piece behind every reactive screen-metric value in this bucket: a `{ get
|
|
2
|
+
// current() }` box whose read is re-run whenever the engine's Dimensions module emits 'change'.
|
|
3
|
+
//
|
|
4
|
+
// `createSubscriber` (svelte/reactivity) rather than hand-rolled bookkeeping: it is pure — no DOM
|
|
5
|
+
// anywhere in its implementation — and is the integration point Svelte itself documents for an
|
|
6
|
+
// external event source (its own `MediaQuery` is built on it). What it buys us over a `$state` +
|
|
7
|
+
// `$effect` pair is the reason every value here is a module-level singleton rather than a
|
|
8
|
+
// `useX()` call:
|
|
9
|
+
// - `start` runs lazily, on the FIRST reactive read, and the teardown runs when the LAST
|
|
10
|
+
// reading effect is destroyed. So importing this module attaches no Dimensions listener, and
|
|
11
|
+
// N components reading `innerWidth.current` share exactly one.
|
|
12
|
+
// - `.current` is legal OUTSIDE a component too (it just reads through, untracked), whereas
|
|
13
|
+
// `$effect` throws `effect_orphan` anywhere but a component.
|
|
14
|
+
// That is also why these files are plain `.ts`, not `.svelte.ts`: they contain no rune syntax, so
|
|
15
|
+
// there is nothing for svelte/compiler's compileModule step to do (same as runes/attachments.ts).
|
|
16
|
+
import { createSubscriber } from 'svelte/reactivity';
|
|
17
|
+
import { Dimensions } from '@symbiote-native/engine';
|
|
18
|
+
export function createDimensionsValue(read) {
|
|
19
|
+
const subscribe = createSubscriber(update => {
|
|
20
|
+
const subscription = Dimensions.addEventListener('change', update);
|
|
21
|
+
return () => subscription.remove();
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
get current() {
|
|
25
|
+
// Registers the dependency when read inside an effect; a no-op read outside one.
|
|
26
|
+
subscribe();
|
|
27
|
+
return read();
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type IReactiveValue } from './dimensions-value';
|
|
2
|
+
export type IOrientation = 'portrait' | 'landscape';
|
|
3
|
+
export declare const orientation: IReactiveValue<IOrientation>;
|
|
4
|
+
export interface IWidthQueryBounds {
|
|
5
|
+
minWidth?: number;
|
|
6
|
+
maxWidth?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function createWidthQuery(bounds: IWidthQueryBounds): IReactiveValue<boolean>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// What replaces `MediaQuery` from `svelte/reactivity` here — and why it is deliberately NOT a
|
|
2
|
+
// `MediaQuery`-shaped class.
|
|
3
|
+
//
|
|
4
|
+
// `new MediaQuery('min-width: 800px')` takes an arbitrary CSS media-query STRING and answers it
|
|
5
|
+
// against `window.matchMedia`, which does not exist in React Native. A faithful-looking port
|
|
6
|
+
// would have to accept the same string and answer whatever it could from RN sources. The CSS
|
|
7
|
+
// media-query grammar has dozens of features; RN can honestly answer three or four of them. Every
|
|
8
|
+
// other one — `hover`, `pointer`, `prefers-reduced-motion`, `print`, `color-gamut`, `aspect-ratio`
|
|
9
|
+
// — would have to come back `false`, which is indistinguishable from a legitimate "no". That is
|
|
10
|
+
// precisely the "works by accident" dependency the symbiote-web-lib-portability-check skill says
|
|
11
|
+
// to reject: the failure is silent, on device, in a value the author already believes.
|
|
12
|
+
//
|
|
13
|
+
// So the surface is narrower and each supported feature is its own named export. An unsupported
|
|
14
|
+
// one is not a `false` — it is a name that does not exist, i.e. a compile error at the import.
|
|
15
|
+
// The three media features people actually reach for on a phone map cleanly:
|
|
16
|
+
// (orientation: …) -> `orientation` below
|
|
17
|
+
// (min-width / max-width) -> `createWidthQuery` below
|
|
18
|
+
// (prefers-color-scheme) -> the existing `useColorScheme()` rune, over Appearance.
|
|
19
|
+
// Dark mode is not re-exposed here: `useColorScheme()` already covers it and a second accessor
|
|
20
|
+
// for one value is duplication, not ergonomics.
|
|
21
|
+
import { Dimensions } from '@symbiote-native/engine';
|
|
22
|
+
import { createDimensionsValue } from './dimensions-value.js';
|
|
23
|
+
// A square window counts as portrait, matching the CSS `(orientation: portrait)` definition
|
|
24
|
+
// (height >= width), not a coin flip.
|
|
25
|
+
export const orientation = createDimensionsValue(() => {
|
|
26
|
+
const { width, height } = Dimensions.get('window');
|
|
27
|
+
return height >= width ? 'portrait' : 'landscape';
|
|
28
|
+
});
|
|
29
|
+
export function createWidthQuery(bounds) {
|
|
30
|
+
return createDimensionsValue(() => {
|
|
31
|
+
const { width } = Dimensions.get('window');
|
|
32
|
+
if (bounds.minWidth !== undefined && width < bounds.minWidth)
|
|
33
|
+
return false;
|
|
34
|
+
if (bounds.maxWidth !== undefined && width > bounds.maxWidth)
|
|
35
|
+
return false;
|
|
36
|
+
return true;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// useColorScheme, the Svelte twin of React's hook / Vue's composable
|
|
2
|
+
// (adapters/vue/src/composables/use-color-scheme.ts), over the framework-agnostic Appearance
|
|
3
|
+
// module (@symbiote-native/engine). See use-window-dimensions.svelte.ts's header for why this
|
|
4
|
+
// lives in `runes/` with a `.svelte.ts` extension and returns a boxed getter object instead of a
|
|
5
|
+
// bare `$state` variable.
|
|
6
|
+
import { Appearance, } from '@symbiote-native/engine';
|
|
7
|
+
export function useColorScheme() {
|
|
8
|
+
let colorScheme = $state(Appearance.getColorScheme());
|
|
9
|
+
$effect(() => {
|
|
10
|
+
// Re-read on mount in case the scheme changed between this function's own call and the
|
|
11
|
+
// effect actually running — this write is the effect's only touch of `colorScheme`
|
|
12
|
+
// (never a read), so the effect has no dependency on it and runs exactly once on mount,
|
|
13
|
+
// cleaning up exactly once on unmount.
|
|
14
|
+
colorScheme = Appearance.getColorScheme();
|
|
15
|
+
const subscription = Appearance.addChangeListener(preferences => {
|
|
16
|
+
colorScheme = preferences.colorScheme;
|
|
17
|
+
});
|
|
18
|
+
return () => subscription.remove();
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
get current() {
|
|
22
|
+
return colorScheme;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Svelte twin of React's hook / Vue's composable (use-window-dimensions.ts), over the
|
|
2
|
+
// framework-agnostic Dimensions module. Seeds from Dimensions.get('window'), subscribes to
|
|
3
|
+
// 'change', and re-checks right after subscribing to close the gap between this call's read
|
|
4
|
+
// and the effect's mount-time listener attach.
|
|
5
|
+
//
|
|
6
|
+
// `.svelte.ts`: runes ($state/$effect) only work in files with this extension outside a
|
|
7
|
+
// `.svelte` component. `runes/` is this adapter's bucket name for framework-lifecycle helpers
|
|
8
|
+
// (CLAUDE.md's <adapter_src_follows_framework_idioms>) - Svelte calls $state/$effect "runes".
|
|
9
|
+
//
|
|
10
|
+
// Returns a boxed getter, not a bare `$state` variable: Svelte 5 reactivity is lexically
|
|
11
|
+
// scoped to the declaring module, so returning a raw `let dimensions = $state(...)` loses
|
|
12
|
+
// reactivity for the caller. `{ get current() { ... } }` is the sanctioned pattern - read
|
|
13
|
+
// `useWindowDimensions().current` inside `$derived`/template/`$effect`, like unwrapping Vue's
|
|
14
|
+
// `Ref.value`.
|
|
15
|
+
import { Dimensions, } from '@symbiote-native/engine';
|
|
16
|
+
export function useWindowDimensions() {
|
|
17
|
+
let dimensions = $state(Dimensions.get('window'));
|
|
18
|
+
// Plain closure variable, not $state: reading `dimensions` here instead would make the
|
|
19
|
+
// $effect depend on its own write and re-run itself. This keeps the effect's dependency set
|
|
20
|
+
// empty, so it runs once on mount and cleans up once on unmount.
|
|
21
|
+
let last = dimensions;
|
|
22
|
+
$effect(() => {
|
|
23
|
+
const handleChange = (next) => {
|
|
24
|
+
if (last.width !== next.width ||
|
|
25
|
+
last.height !== next.height ||
|
|
26
|
+
last.scale !== next.scale ||
|
|
27
|
+
last.fontScale !== next.fontScale) {
|
|
28
|
+
last = next;
|
|
29
|
+
dimensions = next;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const subscription = Dimensions.addEventListener('change', (set) => handleChange(set.window));
|
|
33
|
+
// Covers an update missed between the call-time `get` and subscribing here; the equality
|
|
34
|
+
// guard filters the no-op if nothing changed.
|
|
35
|
+
handleChange(Dimensions.get('window'));
|
|
36
|
+
return () => subscription.remove();
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
get current() {
|
|
40
|
+
return dimensions;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type IReactiveValue } from './dimensions-value';
|
|
2
|
+
export declare const innerWidth: IReactiveValue<number>;
|
|
3
|
+
export declare const innerHeight: IReactiveValue<number>;
|
|
4
|
+
export declare const outerWidth: IReactiveValue<number>;
|
|
5
|
+
export declare const outerHeight: IReactiveValue<number>;
|
|
6
|
+
export declare const devicePixelRatio: IReactiveValue<number>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// The React Native twin of `svelte/reactivity/window`. Upstream reads `window.innerWidth`,
|
|
2
|
+
// `window.outerWidth`, `window.devicePixelRatio` and friends, all of which are absent here — the
|
|
3
|
+
// dom-shim patches only the DOM classes stock compiled Svelte output touches (svelte-adapter-dom-
|
|
4
|
+
// shim skill), never the browser's window-metric properties — so every one of those values would
|
|
5
|
+
// read `undefined` forever and never update. Same names, same `.current` shape, real engine
|
|
6
|
+
// sources underneath.
|
|
7
|
+
//
|
|
8
|
+
// Two deliberate differences from upstream, both improvements:
|
|
9
|
+
// - the type is `number`, not `number | undefined`. Upstream's `undefined` is its SSR branch;
|
|
10
|
+
// there is no SSR here, and Dimensions always answers (it falls back to zero metrics when
|
|
11
|
+
// DeviceInfo is unresolvable), so a consumer never has to unwrap.
|
|
12
|
+
// - `outerWidth`/`outerHeight` are the physical SCREEN, not a "browser window inside a desktop
|
|
13
|
+
// window" — the closest honest reading of outer-vs-inner on a device, where an app's window
|
|
14
|
+
// is the screen minus system chrome (status bar, navigation bar).
|
|
15
|
+
//
|
|
16
|
+
// NOT provided, on purpose: `scrollX` / `scrollY` / `screenLeft` / `screenTop`. There is no
|
|
17
|
+
// window-level scroll offset in React Native (scrolling is per-ScrollView, observed through its
|
|
18
|
+
// own `onScroll`), and an app has no position within a desktop-style window manager. Rather than
|
|
19
|
+
// export a value that is always 0, or one that throws when it is finally read on a device, these
|
|
20
|
+
// are simply absent — the import fails at compile time, which the author sees immediately.
|
|
21
|
+
import { Dimensions, PixelRatio } from '@symbiote-native/engine';
|
|
22
|
+
import { createDimensionsValue } from './dimensions-value.js';
|
|
23
|
+
export const innerWidth = createDimensionsValue(() => Dimensions.get('window').width);
|
|
24
|
+
export const innerHeight = createDimensionsValue(() => Dimensions.get('window').height);
|
|
25
|
+
export const outerWidth = createDimensionsValue(() => Dimensions.get('screen').width);
|
|
26
|
+
export const outerHeight = createDimensionsValue(() => Dimensions.get('screen').height);
|
|
27
|
+
// PixelRatio.get() IS Dimensions.get('window').scale, so the same 'change' event carries it.
|
|
28
|
+
export const devicePixelRatio = createDimensionsValue(() => PixelRatio.get());
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// The `<style>`-scoping rule for ONE class token, and nothing else.
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS IS ITS OWN FILE — it looks like it belongs in `./style-scope`, and it cannot live
|
|
4
|
+
// there. The build-time half (`preprocessor/scoped-styles.ts`) and the runtime half
|
|
5
|
+
// (`./style-scope`) must apply the SAME rule or a static `class="card"` and a dynamic
|
|
6
|
+
// `class={'card'}` would disagree, so it has to be shared. But `svelte.config.js` is loaded
|
|
7
|
+
// DIRECTLY by Node (svelte-check, the language server): Node strips the types off a `.ts` file
|
|
8
|
+
// yet still runs its own ESM resolver over whatever that file imports, where an extensionless
|
|
9
|
+
// relative specifier is a hard `ERR_MODULE_NOT_FOUND`. `./style-scope` imports `./class-value`
|
|
10
|
+
// imports `@symbiote-native/engine` — an entire extensionless graph the preprocessor must never
|
|
11
|
+
// pull in. So the shared rule lives HERE, with ZERO imports of its own, reachable from the
|
|
12
|
+
// preprocessor through the `@symbiote-native/svelte/scope-token` exports subpath (which supplies
|
|
13
|
+
// the extension at both targets, `src` in-workspace and `build` when published).
|
|
14
|
+
//
|
|
15
|
+
// Keep this file import-free. Adding one import to it breaks `svelte-check` for every consuming
|
|
16
|
+
// app, and nothing in this package's own test run would notice.
|
|
17
|
+
const SCOPE_SEPARATOR = '__';
|
|
18
|
+
// A token is matched in its camelCase form because that is the only form the registry is keyed
|
|
19
|
+
// in (@symbiote-native/css-parser's `extractClassName` always camelCases, so `.card-title`
|
|
20
|
+
// registers as `cardTitle`) while markup idiomatically writes the kebab form. A token this file's
|
|
21
|
+
// `<style>` block does NOT define is returned VERBATIM, not camelCased — unlike Vue's rewriter,
|
|
22
|
+
// which camelCases everything it sees. The engine's `resolveOne` already falls back kebab->camel
|
|
23
|
+
// on a miss, so camelCasing here would buy nothing and would silently rename a class the author
|
|
24
|
+
// is forwarding to some other file's global rule.
|
|
25
|
+
export function scopeToken(token, localNames, scopeId) {
|
|
26
|
+
const camelToken = kebabToCamel(token);
|
|
27
|
+
return localNames.has(camelToken) ? camelToken + SCOPE_SEPARATOR + scopeId : token;
|
|
28
|
+
}
|
|
29
|
+
// Duplicated from @symbiote-native/css-parser rather than imported, for the same reason
|
|
30
|
+
// core/engine/src/style-registry/index.ts duplicates it — plus the import-free constraint above.
|
|
31
|
+
export function kebabToCamel(value) {
|
|
32
|
+
return value.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function scopeSvelteClass(value: unknown, localNames: ReadonlySet<string>, scopeId: string): unknown;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Runtime half of the `<style>` scoping pass. The build-time half — `preprocessor/
|
|
2
|
+
// scoped-styles.ts` — rewrites every STATIC class token in a component's own markup while it
|
|
3
|
+
// still has the source text. A DYNAMIC `class={expr}` has no tokens to read at build time, so
|
|
4
|
+
// its value is wrapped in a call to `scopeSvelteClass` instead. Same split Vue's scoped-style
|
|
5
|
+
// path already uses (`scopeClassName` in @symbiote-native/engine, symbiote-sfc-style-compiler §5).
|
|
6
|
+
// Both halves apply the identical per-token rule by sharing `./scope-token` — see that file for
|
|
7
|
+
// why the rule is not simply declared here.
|
|
8
|
+
//
|
|
9
|
+
// WHY NOT REUSE THE ENGINE'S `scopeClassName` DIRECTLY. Its input surface is Vue's:
|
|
10
|
+
// `string | Record<string, boolean> | Array<those>`. Svelte's is wider — `class={['card', cond &&
|
|
11
|
+
// 'on']}` leaves a literal `false` in the array, which `scopeClassName` hands to `.split()` and
|
|
12
|
+
// throws on. Widening a function the other three adapters depend on, to serve one adapter's
|
|
13
|
+
// syntax, is the trade §22b already refused for `resolveClassName`. Instead this normalizes
|
|
14
|
+
// through the adapter's OWN clsx boundary first (`normalizeSvelteClass`) and only scopes what
|
|
15
|
+
// comes back as a class string; everything else — a resolved style object, `false`, a mixed array
|
|
16
|
+
// holding a style object — is handed on untouched, taking exactly the path it took before.
|
|
17
|
+
import { normalizeSvelteClass } from './class-value.js';
|
|
18
|
+
import { scopeToken } from './scope-token.js';
|
|
19
|
+
const CLASS_SEPARATOR = ' ';
|
|
20
|
+
export function scopeSvelteClass(value, localNames, scopeId) {
|
|
21
|
+
const normalized = normalizeSvelteClass(value);
|
|
22
|
+
if (typeof normalized !== 'string')
|
|
23
|
+
return normalized;
|
|
24
|
+
return normalized
|
|
25
|
+
.split(/\s+/)
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map(token => scopeToken(token, localNames, scopeId))
|
|
28
|
+
.join(CLASS_SEPARATOR);
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Re-exports @symbiote-native/css-parser verbatim from INSIDE this package, so a consuming app's
|
|
2
|
+
// own require('@symbiote-native/svelte/metro-css-parser') resolves css-parser relative to THIS
|
|
3
|
+
// file's location (adapters/svelte/node_modules, where css-parser IS a real dependency — see
|
|
4
|
+
// package.json), not relative to the app's own node_modules. Twin of
|
|
5
|
+
// adapters/vue/metro-css-parser.cjs. Unlike Vue's, this adapter's own metro-svelte-transformer.cjs
|
|
6
|
+
// already handles standalone style files itself (its isStyleFile branch), so this subpath exists
|
|
7
|
+
// only for parity with the other adapters and for a future consumer that wants CSS-only handling
|
|
8
|
+
// without the .svelte transform (e.g. importing a plain .css from non-.svelte source).
|
|
9
|
+
module.exports = require('@symbiote-native/css-parser').createCssMetroTransformer();
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Metro babel transformer that teaches the bundler to read Svelte components (.svelte). Metro
|
|
2
|
+
// has no Svelte plugin, so we do the single-pass compile here: run svelte/compiler's compile()
|
|
3
|
+
// with this adapter's own compiler options, then hand the generated client JS to RN's own babel
|
|
4
|
+
// transformer. This is the Metro twin of adapters/vue/metro-vue-transformer.cjs.
|
|
5
|
+
//
|
|
6
|
+
// Unlike Vue's transformer, this does NOT rewrite any framework import: the whole
|
|
7
|
+
// svelte-adapter-dom-shim strategy is that STOCK compiled Svelte client output (its own
|
|
8
|
+
// `import * as $ from 'svelte/internal/client'`) runs completely unchanged against a globalThis
|
|
9
|
+
// DOM shim — there is no @symbiote-native/svelte/runtime-helpers retarget the way Vue needs one.
|
|
10
|
+
// Verified against the installed svelte@5.56.8: compiling a trivial component with
|
|
11
|
+
// {fragments:'tree', css:'external', generate:'client'} produces exactly
|
|
12
|
+
// `import * as $ from 'svelte/internal/client'` + `$.from_tree(...)` + `$.set_custom_element_data`
|
|
13
|
+
// calls — the same shape the dom-shim's init_operations patches already target.
|
|
14
|
+
//
|
|
15
|
+
// Ships as a package-level export (`@symbiote-native/svelte/metro-svelte-transformer`) rather
|
|
16
|
+
// than living in each consuming app: a consumer's own metro.config.js just points
|
|
17
|
+
// babelTransformerPath at it.
|
|
18
|
+
|
|
19
|
+
const { compile, compileModule } = require('svelte/compiler');
|
|
20
|
+
const ts = require('typescript');
|
|
21
|
+
const {
|
|
22
|
+
compileCssFile,
|
|
23
|
+
isStyleFile,
|
|
24
|
+
resolveUpstreamTransformer,
|
|
25
|
+
} = require('@symbiote-native/css-parser');
|
|
26
|
+
|
|
27
|
+
const upstreamTransformer = resolveUpstreamTransformer();
|
|
28
|
+
|
|
29
|
+
// Mirrors adapters/svelte/svelte.config.js's own compilerOptions exactly (fragments:'tree' is
|
|
30
|
+
// mandatory per svelte-adapter-dom-shim skill §2 — it makes the compiler emit from_tree(),
|
|
31
|
+
// element-by-element via document.createElement, never from_html()'s innerHTML-on-a-<template>,
|
|
32
|
+
// so the shim needs no HTML parser; css:'external' keeps Svelte from injecting a <style> into a
|
|
33
|
+
// document.head that doesn't meaningfully exist). Duplicated here rather than imported from that
|
|
34
|
+
// file: svelte.config.js is ESM (`export default`, loaded directly by Node-native tooling —
|
|
35
|
+
// svelte-check, the language server, Vite), while this file is `require()`d by Metro directly
|
|
36
|
+
// and must stay pure CommonJS with no build step of its own, exactly like
|
|
37
|
+
// metro-vue-transformer.cjs.
|
|
38
|
+
const COMPILER_OPTIONS = { fragments: 'tree', css: 'external', generate: 'client' };
|
|
39
|
+
|
|
40
|
+
// The web-only-construct guard (svelte-adapter-dom-shim skill §7/§22) RUNS HERE, on every
|
|
41
|
+
// `.svelte` Metro compiles — not just via svelte-check/the language server. `{@html}` compiles
|
|
42
|
+
// to `$.html()`, which assigns to an `innerHTML` the shim does not define, so the content
|
|
43
|
+
// silently never renders; editor tooling only covers a developer who has it wired up, and a
|
|
44
|
+
// consuming app whose own `svelte.config.js` never registers the preprocessor would still ship
|
|
45
|
+
// the broken bundle. A build-time gate is the only one nobody can be missing.
|
|
46
|
+
//
|
|
47
|
+
// Resolved by package self-reference rather than a relative path so the same line works from
|
|
48
|
+
// `src/*.ts` in this workspace and from `build/*.js` in a published install — `exports` and
|
|
49
|
+
// `publishConfig.exports` each map `./preprocessor` at their own target. Loaded lazily and once:
|
|
50
|
+
// `require()` cannot pull an ESM/TS module, but `transform` is already async.
|
|
51
|
+
let preprocessorPromise;
|
|
52
|
+
function webOnlyConstructGuard() {
|
|
53
|
+
preprocessorPromise ??= import('@symbiote-native/svelte/preprocessor').then(mod =>
|
|
54
|
+
mod.forbidWebOnlyConstructs(),
|
|
55
|
+
);
|
|
56
|
+
return preprocessorPromise;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The `<style>` preprocessor, loaded the same lazy way and for the same reason. Unlike the guard
|
|
60
|
+
// above (which only throws and hands the source back untouched), this one REWRITES the source —
|
|
61
|
+
// it compiles the style block into registerStyles() output and scopes every `class` in the
|
|
62
|
+
// component's own markup — so its returned `code` is what compile() must be given. See
|
|
63
|
+
// src/preprocessor/scoped-styles.ts for why Svelte's own `result.css` cannot be used instead.
|
|
64
|
+
let scopedStylesPromise;
|
|
65
|
+
function scopedStylesPreprocessor() {
|
|
66
|
+
scopedStylesPromise ??= import('@symbiote-native/svelte/scoped-styles').then(mod =>
|
|
67
|
+
mod.scopedStyles(),
|
|
68
|
+
);
|
|
69
|
+
return scopedStylesPromise;
|
|
70
|
+
}
|
|
71
|
+
//
|
|
72
|
+
// Svelte 5's compiler strips <script lang="ts"> types structurally, with no external type
|
|
73
|
+
// resolution needed (unlike @vue/compiler-sfc's compileScript, which needs registerTS + a real
|
|
74
|
+
// `fs` for a type-only import from another file) — so no TypeScript/filesystem wiring is needed
|
|
75
|
+
// here the way metro-vue-transformer.cjs needs it.
|
|
76
|
+
function compileSvelteFile(src, filename) {
|
|
77
|
+
const { js } = compile(src, { ...COMPILER_OPTIONS, filename });
|
|
78
|
+
return js.code;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// `.svelte.js`/`.svelte.ts` files (adapters/svelte/src/runes/*.svelte.ts and any package that
|
|
82
|
+
// ships its own runes) carry rune syntax ($state/$effect/...) OUTSIDE a component's markup, so
|
|
83
|
+
// they need svelte/compiler's separate MODULE api, not compile() — compile() expects a
|
|
84
|
+
// component and throws a parse error on a bare module. Uncompiled, a literal `$state(...)` call
|
|
85
|
+
// hits svelte/index-client.js's dev-guard export and throws `rune_outside_svelte` the instant it
|
|
86
|
+
// runs — this filename check was previously MISSING entirely, so every .svelte.ts rune file
|
|
87
|
+
// shipped uncompiled and would crash on first call on a real device.
|
|
88
|
+
//
|
|
89
|
+
// Unlike compile() (which strips <script lang="ts"> types itself via bundled acorn-typescript),
|
|
90
|
+
// compileModule() does NOT parse TypeScript at all — it throws js_parse_error on a bare
|
|
91
|
+
// return-type annotation regardless of the .ts filename passed. So TS has to be stripped BEFORE
|
|
92
|
+
// compileModule() ever sees the source. ts.transpileModule() (isolated-file "strip only" mode,
|
|
93
|
+
// module:ESNext/target:ESNext) does exactly this: drops type-only imports/annotations, keeps
|
|
94
|
+
// every used value import, and leaves $state/$effect call expressions untouched — it does not
|
|
95
|
+
// require full-program type information the way metro-vue-transformer.cjs's registerTS step does.
|
|
96
|
+
function stripTypeScript(src, filename) {
|
|
97
|
+
const { outputText } = ts.transpileModule(src, {
|
|
98
|
+
fileName: filename,
|
|
99
|
+
compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ESNext },
|
|
100
|
+
});
|
|
101
|
+
return outputText;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function compileSvelteModuleFile(src, filename) {
|
|
105
|
+
const { js } = compileModule(stripTypeScript(src, filename), { generate: 'client', filename });
|
|
106
|
+
return js.code;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Exported separately so tests can assert on the compiled output directly without driving the
|
|
110
|
+
// full upstream RN Babel preset.
|
|
111
|
+
module.exports.compileSvelteFile = compileSvelteFile;
|
|
112
|
+
module.exports.compileSvelteModuleFile = compileSvelteModuleFile;
|
|
113
|
+
|
|
114
|
+
module.exports.transform = async function transform(params) {
|
|
115
|
+
if (params.filename.endsWith('.svelte')) {
|
|
116
|
+
// Throws with a message naming the RN alternative. Deliberately BEFORE compile(), so the
|
|
117
|
+
// author sees the real diagnosis rather than a downstream symptom.
|
|
118
|
+
(await webOnlyConstructGuard()).markup({ content: params.src, filename: params.filename });
|
|
119
|
+
const preprocessed = await (
|
|
120
|
+
await scopedStylesPreprocessor()
|
|
121
|
+
).markup({ content: params.src, filename: params.filename });
|
|
122
|
+
const code = compileSvelteFile(preprocessed.code, params.filename);
|
|
123
|
+
// Re-label as .tsx so RN's transformer processes the module exactly like app source; Metro
|
|
124
|
+
// tracks the real path separately. Matches metro-vue-transformer.cjs's identical trick.
|
|
125
|
+
return upstreamTransformer.transform({
|
|
126
|
+
...params,
|
|
127
|
+
src: code,
|
|
128
|
+
filename: params.filename + '.tsx',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (params.filename.endsWith('.svelte.ts') || params.filename.endsWith('.svelte.js')) {
|
|
132
|
+
const code = compileSvelteModuleFile(params.src, params.filename);
|
|
133
|
+
return upstreamTransformer.transform({
|
|
134
|
+
...params,
|
|
135
|
+
src: code,
|
|
136
|
+
filename: params.filename + '.tsx',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// A standalone style file (as opposed to a component's own <style> block, handled by the
|
|
140
|
+
// preprocessor above) — the framework-agnostic path (core/css-parser's compileCssFile), usable
|
|
141
|
+
// from this adapter's examples exactly like from any other adapter's. isStyleFile recognizes
|
|
142
|
+
// .css/.scss/.sass/.less/.styl (+ .module.*).
|
|
143
|
+
if (isStyleFile(params.filename)) {
|
|
144
|
+
const { code } = await compileCssFile(params.src, params.filename);
|
|
145
|
+
return upstreamTransformer.transform({
|
|
146
|
+
...params,
|
|
147
|
+
src: code,
|
|
148
|
+
filename: params.filename + '.js',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return upstreamTransformer.transform(params);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Surface the upstream cache key so RN preset changes still bust Metro's cache. The compile step
|
|
155
|
+
// itself is invalidated by `--reset-cache`.
|
|
156
|
+
module.exports.getCacheKey = upstreamTransformer.getCacheKey;
|