react-native-gtkx 0.3.0-alpha.1 → 0.4.0-alpha.3
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 +35 -96
- package/dist/adw/index.d.ts +3 -2
- package/dist/adw/index.js +24 -6
- package/dist/adw/index.js.map +1 -1
- package/dist/adw/widgets.generated.d.ts +1 -1
- package/dist/adw/widgets.generated.js +2 -2
- package/dist/adw/widgets.generated.js.map +1 -1
- package/dist/animated/create-animated.d.ts +2 -0
- package/dist/animated/create-animated.js +2 -0
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/event.d.ts +15 -0
- package/dist/animated/event.js +80 -0
- package/dist/animated/event.js.map +1 -0
- package/dist/animated/index.d.ts +4 -0
- package/dist/animated/index.js +8 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/spring.d.ts +10 -0
- package/dist/animated/spring.js +20 -6
- package/dist/animated/spring.js.map +1 -1
- package/dist/animated/timing.d.ts +10 -0
- package/dist/animated/timing.js +21 -13
- package/dist/animated/timing.js.map +1 -1
- package/dist/apis/host.gtkx.js +70 -11
- package/dist/apis/host.gtkx.js.map +1 -1
- package/dist/common/navigation-stack.d.ts +6 -3
- package/dist/common/navigation-stack.js +8 -1
- package/dist/common/navigation-stack.js.map +1 -1
- package/dist/common/widget.js +2 -11
- package/dist/common/widget.js.map +1 -1
- package/dist/components/animated.d.ts +1 -0
- package/dist/components/animated.js +28 -4
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.js +32 -8
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/request-animation-frame.d.ts +33 -0
- package/dist/components/request-animation-frame.js +83 -0
- package/dist/components/request-animation-frame.js.map +1 -0
- package/dist/components/scroll-view.js +60 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/virtualized-list.js +1 -4
- package/dist/components/virtualized-list.js.map +1 -1
- package/dist/dnd/autoscroll.d.ts +44 -0
- package/dist/dnd/autoscroll.js +176 -0
- package/dist/dnd/autoscroll.js.map +1 -0
- package/dist/dnd/grid-order.d.ts +45 -0
- package/dist/dnd/grid-order.js +151 -0
- package/dist/dnd/grid-order.js.map +1 -0
- package/dist/dnd/grid.d.ts +44 -0
- package/dist/dnd/grid.js +397 -0
- package/dist/dnd/grid.js.map +1 -0
- package/dist/dnd/gtk-controllers.d.ts +16 -2
- package/dist/dnd/gtk-controllers.js +24 -4
- package/dist/dnd/gtk-controllers.js.map +1 -1
- package/dist/dnd/index.d.ts +4 -2
- package/dist/dnd/index.js +9 -2
- package/dist/dnd/index.js.map +1 -1
- package/dist/dnd/order-state.d.ts +14 -0
- package/dist/dnd/order-state.js +55 -0
- package/dist/dnd/order-state.js.map +1 -0
- package/dist/dnd/order.d.ts +19 -0
- package/dist/dnd/order.js +19 -0
- package/dist/dnd/order.js.map +1 -1
- package/dist/dnd/sortable.d.ts +29 -4
- package/dist/dnd/sortable.js +308 -115
- package/dist/dnd/sortable.js.map +1 -1
- package/dist/dnd/types.d.ts +236 -14
- package/dist/dnd/types.js +30 -0
- package/dist/dnd/types.js.map +1 -1
- package/dist/gesture-handler-compat/attach-context.d.ts +29 -0
- package/dist/gesture-handler-compat/attach-context.js +90 -0
- package/dist/gesture-handler-compat/attach-context.js.map +1 -0
- package/dist/gesture-handler-compat/detector-runtime.d.ts +14 -3
- package/dist/gesture-handler-compat/detector-runtime.js +15 -5
- package/dist/gesture-handler-compat/detector-runtime.js.map +1 -1
- package/dist/gesture-handler-compat/detector.js +57 -2
- package/dist/gesture-handler-compat/detector.js.map +1 -1
- package/dist/gesture-handler-compat/gesture-state-manager.d.ts +5 -0
- package/dist/gesture-handler-compat/gesture-state-manager.js +77 -0
- package/dist/gesture-handler-compat/gesture-state-manager.js.map +1 -0
- package/dist/gesture-handler-compat/index.d.ts +2 -2
- package/dist/gesture-handler-compat/index.js +38 -33
- package/dist/gesture-handler-compat/index.js.map +1 -1
- package/dist/gesture-handler-compat/recognizer.d.ts +12 -1
- package/dist/gesture-handler-compat/recognizer.js +23 -9
- package/dist/gesture-handler-compat/recognizer.js.map +1 -1
- package/dist/gesture-handler-compat/tag-registry.d.ts +14 -0
- package/dist/gesture-handler-compat/tag-registry.js +19 -0
- package/dist/gesture-handler-compat/tag-registry.js.map +1 -0
- package/dist/globals/index.d.ts +47 -0
- package/dist/globals/index.js +181 -0
- package/dist/globals/index.js.map +1 -0
- package/dist/globals/install.d.ts +1 -0
- package/dist/globals/install.js +14 -0
- package/dist/globals/install.js.map +1 -0
- package/dist/gtk/index.js +4 -3
- package/dist/gtk/index.js.map +1 -1
- package/dist/gtkx/bridge/adw-namespace.d.ts +2 -0
- package/dist/gtkx/bridge/adw-namespace.js +26 -0
- package/dist/gtkx/bridge/adw-namespace.js.map +1 -0
- package/dist/gtkx/bridge/adw.d.ts +23 -0
- package/dist/gtkx/bridge/adw.js +93 -0
- package/dist/gtkx/bridge/adw.js.map +1 -0
- package/dist/gtkx/bridge/color-scheme-parse.d.ts +11 -0
- package/dist/gtkx/bridge/color-scheme-parse.js +42 -0
- package/dist/gtkx/bridge/color-scheme-parse.js.map +1 -0
- package/dist/gtkx/bridge/color-scheme-portal.d.ts +24 -0
- package/dist/gtkx/bridge/color-scheme-portal.js +172 -0
- package/dist/gtkx/bridge/color-scheme-portal.js.map +1 -0
- package/dist/gtkx/bridge/core.d.ts +25 -0
- package/dist/gtkx/bridge/core.js +69 -0
- package/dist/gtkx/bridge/core.js.map +1 -0
- package/dist/gtkx/bridge/drag-layer.d.ts +27 -0
- package/dist/gtkx/bridge/drag-layer.js +177 -0
- package/dist/gtkx/bridge/drag-layer.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +1 -27
- package/dist/gtkx/bridge/index.js +12 -71
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/view-box.js +1 -1
- package/dist/gtkx/bridge/view-box.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.adw.d.ts +1 -0
- package/dist/gtkx/bridge/widgets.generated.adw.js +16 -0
- package/dist/gtkx/bridge/widgets.generated.adw.js.map +1 -0
- package/dist/gtkx/bridge/widgets.generated.d.ts +0 -1
- package/dist/gtkx/bridge/widgets.generated.js +3 -3
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/bin.js +1 -1
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +815 -481
- package/dist/mcp/data/generated.js +933 -513
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/mcp/resolve.d.ts +2 -2
- package/dist/mcp/resolve.js +2 -2
- package/dist/mcp/resolve.js.map +1 -1
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/metro/index.d.ts +1 -0
- package/dist/metro/index.js +12 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/sidebar.js +2 -45
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/reanimated-compat/animatable-value.d.ts +158 -0
- package/dist/reanimated-compat/animatable-value.js +316 -0
- package/dist/reanimated-compat/animatable-value.js.map +1 -0
- package/dist/reanimated-compat/animation.d.ts +34 -13
- package/dist/reanimated-compat/animation.js +97 -25
- package/dist/reanimated-compat/animation.js.map +1 -1
- package/dist/reanimated-compat/mutable.js +6 -6
- package/dist/reanimated-compat/mutable.js.map +1 -1
- package/dist/reanimated-compat/updater-animations.js +32 -12
- package/dist/reanimated-compat/updater-animations.js.map +1 -1
- package/dist/runner/host-dev.js +5 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +9 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/sea/bundle.js +1 -1
- package/dist/sea/bundle.js.map +1 -1
- package/dist/sea/native-shim.js +3 -2
- package/dist/sea/native-shim.js.map +1 -1
- package/dist/vite/index.js +130 -0
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +1 -1
- package/dist/vitest/index.js +1 -1
- package/dist/vitest/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/gtkx/bridge/theme.d.ts +0 -4
- package/dist/gtkx/bridge/theme.js +0 -5
- package/dist/gtkx/bridge/theme.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updater-animations.js","sourceRoot":"","sources":["../../src/reanimated-compat/updater-animations.ts"],"names":[],"mappings":"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","sourcesContent":["// An animation returned FROM a style or props updater, which is the shape the\n// documentation of `useAnimatedStyle` is written in:\n//\n// const style = useAnimatedStyle(() => ({\n// height: withSpring(open.value ? 320 : 0),\n// }))\n//\n// Assigning an animation to a shared value has always worked here\n// (mutable.ts). Returning one from an updater did not, and it failed the way\n// this repo likes least: silently. `with*` builders return a MARKED DESCRIPTOR\n// outside the initial run (animation.ts), the style layer's leaf test is\n// `typeof value === \"number\"`, and a descriptor is an object — so the property\n// was neither driven nor written nor warned about. It simply sat in the style\n// as `{kind: \"spring\", toValue: 543.4, …}`.\n//\n// Found by `spike/core-exports`, not by reading: `@gorhom/bottom-sheet` bounds\n// its scrollable with `height: animate({point: …})` from `useAnimatedStyle`,\n// and the probe's report that the height \"is not reaching the Yoga node\" was\n// true for a reason one layer earlier than layout — the height was never a\n// number on this platform at all. docs/research/animated-size.md §9.\n//\n// WHAT THIS MODULE IS. One running animation per key, on the platform's own\n// `Animated.Value` and the platform's one frame scheduler — the same\n// `buildAnimation` a shared value uses, so `withTiming`, `withSpring`,\n// `withDelay`, `withSequence`, `withRepeat`, `withDecay` and `withClamp` all\n// arrive here already implemented. The updater's object is republished with\n// each animated key replaced by the number its animation is currently at, and\n// a frame of a running animation costs no React render — it goes down exactly\n// the path a shared value written every frame goes down.\n//\n// THREE RULES, and all three are upstream's — every one of them read out of\n// `hook/useAnimatedStyle.ts`'s `styleUpdater`/`prepareAnimation` rather than\n// inferred from behaviour:\n//\n// - A key APPEARING for the first time is seeded at the animation's target\n// rather than animated to it. There is nothing to animate from, and it is\n// the same collapse `initialUpdaterRun` performs on the updater's first\n// run. Upstream reaches the same place from the other direction: its\n// `prepareAnimation` starts at `oldValues[key]`, which for a key that was\n// not in the last result is `undefined` and leaves the animation's own\n// `current` — its target — standing.\n// - A key whose previous result held a PLAIN NUMBER animates from that\n// number. `oldValues` is `state.last`, the whole previous updater result\n// kept raw, and `prepareAnimation`'s last branch is one line with the\n// comment already on it: `// previously it was a plain value, just set it\n// as starting point`. So `height: open.value ? withTiming(200) : 100` runs\n// 100 → 200, and the idiom people actually write — snap shut, open\n// smoothly — animates. It did not here: the key was dropped when it held a\n// number and re-seeded when it next held an animation, so the seed went\n// straight to 200 and the animation never played at all. Silently, which\n// is the failure this repo ranks worst.\n// - A re-run that produces an EQUIVALENT animation does not restart it. A\n// mapper re-runs whenever anything it read changed, which for a real app is\n// many times a second, and rebuilding the spring each time would leave it\n// crawling. Equivalence is the descriptor's target and shape\n// (`animationSignature`), not object identity — every run builds a fresh\n// object.\n//\n// AND THE REVERSE DIRECTION, animation → plain number, which is upstream's\n// too and is not the mirror image of the second rule: `styleUpdater`'s\n// non-animated branch does `delete animations[key]` and pushes the value\n// through `updateProps` in the SAME run, so the animation is cancelled — no\n// callback, no settle — and the number lands at once. It does not ease back.\n// The only thing this platform has to add is the render a refused property\n// needs to land at all, because \"at once\" through React is still a render (see\n// `onLanding`).\n//\n// WHERE THIS DIFFERS FROM UPSTREAM, said out loud: a restart picks the\n// animation up at the value it is currently at, with the velocity the new\n// descriptor asks for, where upstream also carries the previous animation's\n// VELOCITY across. For a target that moves once that is the same animation;\n// for a target that moves every frame ours is slightly more damped.\n//\n// AND THE CADENCE, the one rule here that is ours rather than upstream's — see\n// LANDING_INTERVAL_MS. It exists because none of the above is enough for a\n// property whose frames this platform refuses to write: for those, the value\n// only exists on screen at whatever the last React render committed, and \"the\n// render at the settle\" turned out to be arbitrarily far away.\nimport type { AnimatedValue, CompositeAnimation } from \"../animated/index\"\nimport {\n animationSignature,\n buildAnimation,\n isAnimationSpec,\n targetOf,\n type AnimationEngine,\n type AnimationSpec,\n} from \"./animation\"\n\n/** What an updater returns: a style object, or a props object. */\nexport type UpdaterObject = Record<string, unknown>\n\n// How far behind its own animation a property this platform will not drive at\n// frame rate is allowed to fall.\n//\n// The settle alone is not an answer, and `@gorhom/bottom-sheet` MOUNTING is\n// the measurement that says so. Its content mask is bounded by an animated\n// `height` whose target is derived from the sheet's own POSITION, so every\n// frame of the opening spring re-aims it — 37 re-aims over 673 ms, each\n// cancelling the last, so not one of them a settle — and then a further\n// 677 ms for the final spring to converge. Measured on the gallery's\n// `upstream-bottom-sheet` screen: the seed was 95.9 px (gorhom's\n// `animatedContentHeightMax` was still 0 on the mapper run that first\n// produced a `height` at all, and the real 832 px arrived 27 ms later, by\n// which time the key already had an entry and a moving target is an animation\n// rather than a seed), and `height` reached Yoga exactly TWICE in the whole\n// mount: 95.9 px at once, 954.6 px 1378 ms later. For 1.38 s the mask was a\n// tenth of its size, the list inside it had no bounded parent, and it mounted\n// zero of its 18 rows. Correct by the settle rule and plainly wrong to\n// anybody opening the screen.\n//\n// So the value lands on a cadence as well. 100 ms because that is the bound\n// under which a change still reads as immediate, and because it bounds the\n// cost by the CLOCK rather than by the animation: at most ten renders a\n// second per animated key, against sixty frames. The same mount now lands the\n// mask at 266 → 546 → 649 → 731 → 792 → 832 → 896 → 939 → 951 → 954 px before\n// settling at 954.6 — the shape the animation has, at a tenth of its rate —\n// with the first at ~100 ms rather than 1.38 s.\n//\n// What it costs, both halves measured in one `spike/core-exports` run on the\n// same machine: 4 settles and 4 renders against 290 refused-property frames\n// before, 4 settles + 38 landings and 42 renders against 294 after. Still an\n// order of magnitude under the per-frame layout write the refusal exists to\n// avoid, and no longer a promise that may never come due.\n// docs/research/animated-size.md §10.\nconst LANDING_INTERVAL_MS = 100\n\n// A landing that cannot move a widget is a render for nothing: GTK allocates\n// whole pixels, so a sub-pixel step changes no committed geometry. It is what\n// keeps a long animation over a short distance from spending ten renders a\n// second to travel one pixel — gorhom's `paddingBottom` crosses 26 px over the\n// same 1.38 s and lands 8 times, not 13.\nconst LANDING_EPSILON = 1\n\ntype Entry = {\n driver: AnimatedValue\n listenerId: string\n signature: string\n running: CompositeAnimation | null\n // The value React was last given for this key, and the moment it was given\n // — the two the cadence is measured against. They survive a restart on\n // purpose: what matters is how stale REACT's copy is, not how far the\n // animation currently in flight has come.\n landedValue: number\n landedAt: number\n}\n\nexport type UpdaterAnimations = {\n /**\n * Resolves `source`'s animations and publishes the result. Called from the\n * mapper, so `source` is a fresh object every time.\n */\n run(source: UpdaterObject): void\n dispose(): void\n}\n\n/**\n * @param publish Receives the updater's object with every animated key\n * replaced by a number. Called once per mapper run and once per animation\n * frame.\n * @param onSettled Called when an animation on `key` reaches its target on its\n * own (never when it is cancelled or replaced). The style layer uses it for\n * the properties it refuses to drive at frame rate, whose contract is that\n * the value lands on the next React render — without it, that promise is\n * only kept when something else happens to re-render.\n * @param onLanding Called when `key`'s current value has to reach React\n * without its animation having reached a target. Two occasions: while an\n * animation runs, at most once per `LANDING_INTERVAL_MS` and only when the\n * value has moved far enough to change a committed layout (the cadence); and\n * when a plain number REPLACES a running animation, where upstream cancels\n * and snaps and a refused property cannot snap without a render. The snap is\n * deliberately not rate-limited — it is a state change rather than a step of\n * one, it happens once per mapper run that flips the key rather than once per\n * frame, and upstream pushes exactly the same number of `updateProps` for it.\n * Same job as `onSettled` and the same handling at the call site; it is a\n * separate callback because a settle and a value that has merely got to be\n * published are different claims, and only the first one is a promise kept.\n * Omit it and both cost nothing at all — a caller with no refused properties\n * (`useAnimatedProps`) does.\n */\nexport const createUpdaterAnimations = (\n engine: AnimationEngine,\n publish: (resolved: UpdaterObject) => void,\n onSettled?: (key: string) => void,\n onLanding?: (key: string) => void,\n): UpdaterAnimations => {\n const entries = new Map<string, Entry>()\n // The clock the animations themselves run on, never `Date` — the whole\n // layer is driven by one injected scheduler so a test can take it\n // (components/frame-scheduler.ts). A driver that only has frame stamps and\n // no off-frame `now` cannot have a cadence, and falls back to the settle.\n const hasClock = engine.scheduler.now !== undefined\n const now = (): number => engine.scheduler.now?.() ?? 0\n let lastSource: UpdaterObject | null = null\n // A frame of one key's animation republishes the WHOLE object, which\n // re-enters `resolve`. The guard keeps that from restarting anything: the\n // run in progress publishes the final state itself.\n let resolving = false\n\n /**\n * @param replacement The plain value that took the key over, or `undefined`\n * when the key left the updater's result altogether.\n *\n * A number here owes the caller a render, and `landedValue` is what decides\n * it: that field is this module's whole model of what REACT holds for the\n * key, and everything since the last landing or settle went to the widget\n * without passing through React. So a plain number that differs from it has\n * to be published or it never arrives — on a refused property the snap IS\n * a render, and neither the cadence nor a settle is ever going to come for\n * it. Compared without the cadence's one-pixel epsilon and for the same\n * reason a settle ignores it: this is a resting value rather than a step\n * towards one, and there is nothing after it to correct a skipped render.\n *\n * Not asked for otherwise. A key that VANISHED, or one replaced by a\n * percentage or a colour, changes the style's SHAPE, and the caller already\n * pays exactly one render for that (hooks.ts); a value equal to the one\n * React already has is a render for nothing.\n */\n const drop = (key: string, replacement: unknown): void => {\n const entry = entries.get(key)\n if (entry === undefined) {\n return\n }\n entries.delete(key)\n entry.running?.stop()\n entry.driver.removeListener(entry.listenerId)\n if (\n typeof replacement === \"number\" &&\n !Object.is(replacement, entry.landedValue)\n ) {\n onLanding?.(key)\n }\n }\n\n const republish = (): void => {\n if (resolving || lastSource === null) {\n return\n }\n // No previous result on a frame: nothing can START an animation here — the\n // source has not changed — so there is nothing to look a starting point up\n // for.\n publish(resolve(lastSource, null))\n }\n\n /**\n * Records that `key`'s current value is the one React now holds. Called\n * wherever a render is asked for, so the cadence is measured from the last\n * value React was actually given rather than from the last frame.\n */\n const markLanded = (entry: Entry): void => {\n entry.landedValue = entry.driver.__getValue()\n entry.landedAt = now()\n }\n\n /** The cadence: see LANDING_INTERVAL_MS. */\n const considerLanding = (key: string, value: number): void => {\n if (onLanding === undefined || !hasClock) {\n return\n }\n const entry = entries.get(key)\n if (entry === undefined) {\n return\n }\n if (now() - entry.landedAt < LANDING_INTERVAL_MS) {\n return\n }\n if (Math.abs(value - entry.landedValue) < LANDING_EPSILON) {\n return\n }\n markLanded(entry)\n onLanding(key)\n }\n\n /** Aims `entry`'s driver at `spec`, replacing whatever it was running. */\n const start = (\n key: string,\n entry: Entry,\n spec: AnimationSpec,\n signature: string,\n ): Entry => {\n entry.signature = signature\n entry.running?.stop()\n const animation = buildAnimation(engine, entry.driver, spec)\n entry.running = animation\n animation.start((result) => {\n if (entry.running === animation) {\n entry.running = null\n }\n // `finished` only: a restart stops the previous animation, and reporting\n // that as a settle would publish through React on every frame the target\n // moves, which is the cost the refusal exists to avoid. A restart is\n // still covered — the cadence above is what carries a target that keeps\n // moving, and it does not care whose animation the frames belong to.\n if (result.finished) {\n markLanded(entry)\n onSettled?.(key)\n }\n })\n return entry\n }\n\n /**\n * Where an animation that is starting has to start FROM, for a key that was\n * not animating a moment ago: the value the PREVIOUS updater result held for\n * it, and only when that is a real number.\n *\n * This is `prepareAnimation(…, oldValues[key])` and nothing more —\n * `oldValues` is `state.last`, upstream's copy of the whole previous updater\n * result — so the second of the header's three rules costs no bookkeeping at\n * all. The previous result is an object this module was already holding for\n * `republish`; reading one key out of it is the entire mechanism, and the\n * entry does not have to carry a value across runs or outlive its animation.\n *\n * Numbers only, and `undefined` for everything else, which lines up with\n * upstream at both ends: a key that was absent has no starting point (its\n * `oldValues[key]` is `undefined`, and the animation's own `current` — the\n * target — stands), and a percentage string or a colour is not something the\n * numeric drivers here can start at.\n */\n const startingPoint = (\n previous: UpdaterObject | null,\n key: string,\n ): number | undefined => {\n const value = previous?.[key]\n return typeof value === \"number\" && Number.isFinite(value)\n ? value\n : undefined\n }\n\n const ensure = (\n key: string,\n spec: AnimationSpec,\n previous: UpdaterObject | null,\n ): Entry => {\n const signature = animationSignature(spec)\n const existing = entries.get(key)\n if (existing !== undefined) {\n return existing.signature === signature\n ? existing\n : start(key, existing, spec, signature)\n }\n const from = startingPoint(previous, key)\n // The driver is seeded before anything is built on purpose: a spring reads\n // its origin off the value it is given (animation.ts), so this IS where\n // \"animate from the old number\" happens.\n const driver = new engine.api.Value(from ?? targetOf(spec) ?? 0)\n const entry: Entry = {\n driver,\n listenerId: driver.addListener(({ value }) => {\n republish()\n considerLanding(key, value)\n }),\n signature,\n running: null,\n // Whichever branch follows, React's copy of this key is the value the\n // driver starts at: a seed changes the style's SHAPE and the caller\n // already pays one render for that (hooks.ts), and a starting point that\n // came out of the previous result is a number React was already handed.\n // So the cadence starts here either way.\n landedValue: driver.__getValue(),\n landedAt: now(),\n }\n entries.set(key, entry)\n // Nothing to animate from: seeded at the target and not started at all,\n // which is the first of the header's three rules.\n return from === undefined ? entry : start(key, entry, spec, signature)\n }\n\n const resolve = (\n source: UpdaterObject,\n previous: UpdaterObject | null,\n ): UpdaterObject => {\n resolving = true\n try {\n let resolved: UpdaterObject | null = null\n for (const key of Object.keys(source)) {\n const value = source[key]\n if (!isAnimationSpec(value)) {\n // A plain value replacing an animation cancels it and snaps, exactly\n // as an assignment to a shared value does and exactly as upstream's\n // `delete animations[key]` + `updateProps` in the same run does. The\n // number itself is published below, by identity.\n drop(key, value)\n continue\n }\n resolved ??= { ...source }\n resolved[key] = ensure(key, value, previous).driver.__getValue()\n }\n for (const key of [...entries.keys()]) {\n if (!(key in source)) {\n drop(key, undefined)\n }\n }\n return resolved ?? source\n } finally {\n resolving = false\n }\n }\n\n return {\n run(source) {\n // `state.last`, upstream's name for it: the result this run's animations\n // measure themselves against. Held for one run only, and it was already\n // being held.\n const previous = lastSource\n lastSource = source\n publish(resolve(source, previous))\n },\n dispose() {\n for (const key of [...entries.keys()]) {\n // Nothing is owed a render: the component is going away.\n drop(key, undefined)\n }\n lastSource = null\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"updater-animations.js","sourceRoot":"","sources":["../../src/reanimated-compat/updater-animations.ts"],"names":[],"mappings":"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","sourcesContent":["// An animation returned FROM a style or props updater, which is the shape the\n// documentation of `useAnimatedStyle` is written in:\n//\n// const style = useAnimatedStyle(() => ({\n// height: withSpring(open.value ? 320 : 0),\n// }))\n//\n// Assigning an animation to a shared value has always worked here\n// (mutable.ts). Returning one from an updater did not, and it failed the way\n// this repo likes least: silently. `with*` builders return a MARKED DESCRIPTOR\n// outside the initial run (animation.ts), the style layer's leaf test is\n// `typeof value === \"number\"`, and a descriptor is an object — so the property\n// was neither driven nor written nor warned about. It simply sat in the style\n// as `{kind: \"spring\", toValue: 543.4, …}`.\n//\n// Found by `spike/core-exports`, not by reading: `@gorhom/bottom-sheet` bounds\n// its scrollable with `height: animate({point: …})` from `useAnimatedStyle`,\n// and the probe's report that the height \"is not reaching the Yoga node\" was\n// true for a reason one layer earlier than layout — the height was never a\n// number on this platform at all. docs/research/animated-size.md §9.\n//\n// WHAT THIS MODULE IS. One running animation per key, on the platform's own\n// `Animated.Value` and the platform's one frame scheduler — the same\n// `buildAnimation` a shared value uses, so `withTiming`, `withSpring`,\n// `withDelay`, `withSequence`, `withRepeat`, `withDecay` and `withClamp` all\n// arrive here already implemented. The updater's object is republished with\n// each animated key replaced by the number its animation is currently at, and\n// a frame of a running animation costs no React render — it goes down exactly\n// the path a shared value written every frame goes down.\n//\n// THREE RULES, and all three are upstream's — every one of them read out of\n// `hook/useAnimatedStyle.ts`'s `styleUpdater`/`prepareAnimation` rather than\n// inferred from behaviour:\n//\n// - A key APPEARING for the first time is seeded at the animation's target\n// rather than animated to it. There is nothing to animate from, and it is\n// the same collapse `initialUpdaterRun` performs on the updater's first\n// run. Upstream reaches the same place from the other direction: its\n// `prepareAnimation` starts at `oldValues[key]`, which for a key that was\n// not in the last result is `undefined` and leaves the animation's own\n// `current` — its target — standing.\n// - A key whose previous result held a PLAIN NUMBER animates from that\n// number. `oldValues` is `state.last`, the whole previous updater result\n// kept raw, and `prepareAnimation`'s last branch is one line with the\n// comment already on it: `// previously it was a plain value, just set it\n// as starting point`. So `height: open.value ? withTiming(200) : 100` runs\n// 100 → 200, and the idiom people actually write — snap shut, open\n// smoothly — animates. It did not here: the key was dropped when it held a\n// number and re-seeded when it next held an animation, so the seed went\n// straight to 200 and the animation never played at all. Silently, which\n// is the failure this repo ranks worst.\n// - A re-run that produces an EQUIVALENT animation does not restart it. A\n// mapper re-runs whenever anything it read changed, which for a real app is\n// many times a second, and rebuilding the spring each time would leave it\n// crawling. Equivalence is the descriptor's target and shape\n// (`animationSignature`), not object identity — every run builds a fresh\n// object.\n//\n// AND THE REVERSE DIRECTION, animation → plain number, which is upstream's\n// too and is not the mirror image of the second rule: `styleUpdater`'s\n// non-animated branch does `delete animations[key]` and pushes the value\n// through `updateProps` in the SAME run, so the animation is cancelled — no\n// callback, no settle — and the number lands at once. It does not ease back.\n// The only thing this platform has to add is the render a refused property\n// needs to land at all, because \"at once\" through React is still a render (see\n// `onLanding`).\n//\n// WHERE THIS DIFFERS FROM UPSTREAM, said out loud: a restart picks the\n// animation up at the value it is currently at, with the velocity the new\n// descriptor asks for, where upstream also carries the previous animation's\n// VELOCITY across. For a target that moves once that is the same animation;\n// for a target that moves every frame ours is slightly more damped.\n//\n// AND THE CADENCE, the one rule here that is ours rather than upstream's — see\n// LANDING_INTERVAL_MS. It exists because none of the above is enough for a\n// property whose frames this platform refuses to write: for those, the value\n// only exists on screen at whatever the last React render committed, and \"the\n// render at the settle\" turned out to be arbitrarily far away.\nimport type { CompositeAnimation } from \"../animated/index\"\nimport {\n isAnimatableValue,\n maxAnimatableLeafDelta,\n sameAnimatableValue,\n type AnimatableValue,\n} from \"./animatable-value\"\nimport {\n animationSignature,\n buildAnimation,\n isAnimationSpec,\n makeAnimationDriver,\n targetOf,\n type AnimationDriver,\n type AnimationEngine,\n type AnimationSpec,\n} from \"./animation\"\n\n/** What an updater returns: a style object, or a props object. */\nexport type UpdaterObject = Record<string, unknown>\n\n// How far behind its own animation a property this platform will not drive at\n// frame rate is allowed to fall.\n//\n// The settle alone is not an answer, and `@gorhom/bottom-sheet` MOUNTING is\n// the measurement that says so. Its content mask is bounded by an animated\n// `height` whose target is derived from the sheet's own POSITION, so every\n// frame of the opening spring re-aims it — 37 re-aims over 673 ms, each\n// cancelling the last, so not one of them a settle — and then a further\n// 677 ms for the final spring to converge. Measured on the gallery's\n// `upstream-bottom-sheet` screen: the seed was 95.9 px (gorhom's\n// `animatedContentHeightMax` was still 0 on the mapper run that first\n// produced a `height` at all, and the real 832 px arrived 27 ms later, by\n// which time the key already had an entry and a moving target is an animation\n// rather than a seed), and `height` reached Yoga exactly TWICE in the whole\n// mount: 95.9 px at once, 954.6 px 1378 ms later. For 1.38 s the mask was a\n// tenth of its size, the list inside it had no bounded parent, and it mounted\n// zero of its 18 rows. Correct by the settle rule and plainly wrong to\n// anybody opening the screen.\n//\n// So the value lands on a cadence as well. 100 ms because that is the bound\n// under which a change still reads as immediate, and because it bounds the\n// cost by the CLOCK rather than by the animation: at most ten renders a\n// second per animated key, against sixty frames. The same mount now lands the\n// mask at 266 → 546 → 649 → 731 → 792 → 832 → 896 → 939 → 951 → 954 px before\n// settling at 954.6 — the shape the animation has, at a tenth of its rate —\n// with the first at ~100 ms rather than 1.38 s.\n//\n// What it costs, both halves measured in one `spike/core-exports` run on the\n// same machine: 4 settles and 4 renders against 290 refused-property frames\n// before, 4 settles + 38 landings and 42 renders against 294 after. Still an\n// order of magnitude under the per-frame layout write the refusal exists to\n// avoid, and no longer a promise that may never come due.\n// docs/research/animated-size.md §10.\nconst LANDING_INTERVAL_MS = 100\n\n// A landing that cannot move a widget is a render for nothing: GTK allocates\n// whole pixels, so a sub-pixel step changes no committed geometry. It is what\n// keeps a long animation over a short distance from spending ten renders a\n// second to travel one pixel — gorhom's `paddingBottom` crosses 26 px over the\n// same 1.38 s and lands 8 times, not 13.\nconst LANDING_EPSILON = 1\n\ntype Entry = {\n driver: AnimationDriver\n listenerId: string\n signature: string\n running: CompositeAnimation | null\n // The value React was last given for this key, and the moment it was given\n // — the two the cadence is measured against. They survive a restart on\n // purpose: what matters is how stale REACT's copy is, not how far the\n // animation currently in flight has come. A number for a scalar key, an\n // object/array for a shape one (AnimatableValue, animatable-value.ts).\n landedValue: AnimatableValue\n landedAt: number\n}\n\nexport type UpdaterAnimations = {\n /**\n * Resolves `source`'s animations and publishes the result. Called from the\n * mapper, so `source` is a fresh object every time.\n */\n run(source: UpdaterObject): void\n dispose(): void\n}\n\n/**\n * @param publish Receives the updater's object with every animated key\n * replaced by a number. Called once per mapper run and once per animation\n * frame.\n * @param onSettled Called when an animation on `key` reaches its target on its\n * own (never when it is cancelled or replaced). The style layer uses it for\n * the properties it refuses to drive at frame rate, whose contract is that\n * the value lands on the next React render — without it, that promise is\n * only kept when something else happens to re-render.\n * @param onLanding Called when `key`'s current value has to reach React\n * without its animation having reached a target. Two occasions: while an\n * animation runs, at most once per `LANDING_INTERVAL_MS` and only when the\n * value has moved far enough to change a committed layout (the cadence); and\n * when a plain number REPLACES a running animation, where upstream cancels\n * and snaps and a refused property cannot snap without a render. The snap is\n * deliberately not rate-limited — it is a state change rather than a step of\n * one, it happens once per mapper run that flips the key rather than once per\n * frame, and upstream pushes exactly the same number of `updateProps` for it.\n * Same job as `onSettled` and the same handling at the call site; it is a\n * separate callback because a settle and a value that has merely got to be\n * published are different claims, and only the first one is a promise kept.\n * Omit it and both cost nothing at all — a caller with no refused properties\n * (`useAnimatedProps`) does.\n */\nexport const createUpdaterAnimations = (\n engine: AnimationEngine,\n publish: (resolved: UpdaterObject) => void,\n onSettled?: (key: string) => void,\n onLanding?: (key: string) => void,\n): UpdaterAnimations => {\n const entries = new Map<string, Entry>()\n // The clock the animations themselves run on, never `Date` — the whole\n // layer is driven by one injected scheduler so a test can take it\n // (components/frame-scheduler.ts). A driver that only has frame stamps and\n // no off-frame `now` cannot have a cadence, and falls back to the settle.\n const hasClock = engine.scheduler.now !== undefined\n const now = (): number => engine.scheduler.now?.() ?? 0\n let lastSource: UpdaterObject | null = null\n // A frame of one key's animation republishes the WHOLE object, which\n // re-enters `resolve`. The guard keeps that from restarting anything: the\n // run in progress publishes the final state itself.\n let resolving = false\n\n /**\n * @param replacement The plain value that took the key over, or `undefined`\n * when the key left the updater's result altogether.\n *\n * A number here owes the caller a render, and `landedValue` is what decides\n * it: that field is this module's whole model of what REACT holds for the\n * key, and everything since the last landing or settle went to the widget\n * without passing through React. So a plain number that differs from it has\n * to be published or it never arrives — on a refused property the snap IS\n * a render, and neither the cadence nor a settle is ever going to come for\n * it. Compared without the cadence's one-pixel epsilon and for the same\n * reason a settle ignores it: this is a resting value rather than a step\n * towards one, and there is nothing after it to correct a skipped render.\n *\n * Not asked for otherwise. A key that VANISHED, or one replaced by a\n * percentage or a colour, changes the style's SHAPE, and the caller already\n * pays exactly one render for that (hooks.ts); a value equal to the one\n * React already has is a render for nothing.\n */\n const drop = (key: string, replacement: unknown): void => {\n const entry = entries.get(key)\n if (entry === undefined) {\n return\n }\n entries.delete(key)\n entry.running?.stop()\n entry.driver.removeListener(entry.listenerId)\n // A number originally; now also an object/array of numbers — either way\n // a resting AnimatableValue owes the caller a render if it differs from\n // what React was last given. `sameAnimatableValue` replaces the original\n // `Object.is`: a merged object is rebuilt fresh every frame\n // (rebuildAnimatableValue), so it is never the SAME reference as the\n // landed value even when every leaf agrees.\n if (\n isAnimatableValue(replacement) &&\n !sameAnimatableValue(replacement, entry.landedValue)\n ) {\n onLanding?.(key)\n }\n }\n\n const republish = (): void => {\n if (resolving || lastSource === null) {\n return\n }\n // No previous result on a frame: nothing can START an animation here — the\n // source has not changed — so there is nothing to look a starting point up\n // for.\n publish(resolve(lastSource, null))\n }\n\n /**\n * Records that `key`'s current value is the one React now holds. Called\n * wherever a render is asked for, so the cadence is measured from the last\n * value React was actually given rather than from the last frame.\n */\n const markLanded = (entry: Entry): void => {\n entry.landedValue = entry.driver.__getValue()\n entry.landedAt = now()\n }\n\n /**\n * The cadence: see LANDING_INTERVAL_MS. For a shape key (`{x, y}`, …) \"far\n * enough to change a committed layout\" is ANY leaf crossing the epsilon —\n * `maxAnimatableLeafDelta` is the largest single-leaf change, not a sum, so\n * a diagonal move is not double-counted into landing twice as often.\n */\n const considerLanding = (key: string, value: AnimatableValue): void => {\n if (onLanding === undefined || !hasClock) {\n return\n }\n const entry = entries.get(key)\n if (entry === undefined) {\n return\n }\n if (now() - entry.landedAt < LANDING_INTERVAL_MS) {\n return\n }\n if (maxAnimatableLeafDelta(value, entry.landedValue) < LANDING_EPSILON) {\n return\n }\n markLanded(entry)\n onLanding(key)\n }\n\n /** Aims `entry`'s driver at `spec`, replacing whatever it was running. */\n const start = (\n key: string,\n entry: Entry,\n spec: AnimationSpec,\n signature: string,\n ): Entry => {\n entry.signature = signature\n entry.running?.stop()\n const animation = buildAnimation(engine, entry.driver, spec)\n entry.running = animation\n animation.start((result) => {\n if (entry.running === animation) {\n entry.running = null\n }\n // `finished` only: a restart stops the previous animation, and reporting\n // that as a settle would publish through React on every frame the target\n // moves, which is the cost the refusal exists to avoid. A restart is\n // still covered — the cadence above is what carries a target that keeps\n // moving, and it does not care whose animation the frames belong to.\n if (result.finished) {\n markLanded(entry)\n onSettled?.(key)\n }\n })\n return entry\n }\n\n /**\n * Where an animation that is starting has to start FROM, for a key that was\n * not animating a moment ago: the value the PREVIOUS updater result held for\n * it, and only when that is a real number.\n *\n * This is `prepareAnimation(…, oldValues[key])` and nothing more —\n * `oldValues` is `state.last`, upstream's copy of the whole previous updater\n * result — so the second of the header's three rules costs no bookkeeping at\n * all. The previous result is an object this module was already holding for\n * `republish`; reading one key out of it is the entire mechanism, and the\n * entry does not have to carry a value across runs or outlive its animation.\n *\n * Numbers only, and `undefined` for everything else — which, now that a key\n * can hold an OBJECT too, is worth stating precisely: a plain object\n * (`{x: 5, y: 10}`) does NOT carry over here, on purpose, and it is\n * upstream's own rule, not a gap in this one. `prepareAnimation`'s branches\n * are `typeof lastValue === \"object\"` → was it a shared value (`.value`) or\n * an animation node (`.onFrame`)? Neither matches a plain data object, so\n * `value` is left at its default — the animation's own target. A key whose\n * previous value was a plain OBJECT is therefore seeded at the target\n * exactly like a key that was absent, and only a plain NUMBER ever carries\n * over as a starting point. This function already implemented that rule by\n * accident, before objects were legal at all; nothing here changed.\n */\n const startingPoint = (\n previous: UpdaterObject | null,\n key: string,\n ): number | undefined => {\n const value = previous?.[key]\n return typeof value === \"number\" && Number.isFinite(value)\n ? value\n : undefined\n }\n\n const ensure = (\n key: string,\n spec: AnimationSpec,\n previous: UpdaterObject | null,\n ): Entry => {\n const signature = animationSignature(spec)\n const existing = entries.get(key)\n if (existing !== undefined) {\n return existing.signature === signature\n ? existing\n : start(key, existing, spec, signature)\n }\n const from = startingPoint(previous, key)\n // The driver is seeded before anything is built on purpose: a spring reads\n // its origin off the value it is given (animation.ts), so this IS where\n // \"animate from the old number\" happens. makeAnimationDriver picks the\n // right driver kind for the seed — a number for a scalar key, a shape\n // (animatable-value.ts) for an object/array one.\n const driver = makeAnimationDriver(\n engine.api,\n from ?? targetOf(spec) ?? 0,\n `the \"${key}\" key of an updater's result`,\n )\n const entry: Entry = {\n driver,\n listenerId: driver.addListener(({ value }) => {\n republish()\n considerLanding(key, value)\n }),\n signature,\n running: null,\n // Whichever branch follows, React's copy of this key is the value the\n // driver starts at: a seed changes the style's SHAPE and the caller\n // already pays one render for that (hooks.ts), and a starting point that\n // came out of the previous result is a number React was already handed.\n // So the cadence starts here either way.\n landedValue: driver.__getValue(),\n landedAt: now(),\n }\n entries.set(key, entry)\n // Nothing to animate from: seeded at the target and not started at all,\n // which is the first of the header's three rules.\n return from === undefined ? entry : start(key, entry, spec, signature)\n }\n\n const resolve = (\n source: UpdaterObject,\n previous: UpdaterObject | null,\n ): UpdaterObject => {\n resolving = true\n try {\n let resolved: UpdaterObject | null = null\n for (const key of Object.keys(source)) {\n const value = source[key]\n if (!isAnimationSpec(value)) {\n // A plain value replacing an animation cancels it and snaps, exactly\n // as an assignment to a shared value does and exactly as upstream's\n // `delete animations[key]` + `updateProps` in the same run does. The\n // number itself is published below, by identity.\n drop(key, value)\n continue\n }\n resolved ??= { ...source }\n resolved[key] = ensure(key, value, previous).driver.__getValue()\n }\n for (const key of [...entries.keys()]) {\n if (!(key in source)) {\n drop(key, undefined)\n }\n }\n return resolved ?? source\n } finally {\n resolving = false\n }\n }\n\n return {\n run(source) {\n // `state.last`, upstream's name for it: the result this run's animations\n // measure themselves against. Held for one run only, and it was already\n // being held.\n const previous = lastSource\n lastSource = source\n publish(resolve(source, previous))\n },\n dispose() {\n for (const key of [...entries.keys()]) {\n // Nothing is owed a render: the component is going away.\n drop(key, undefined)\n }\n lastSource = null\n },\n }\n}\n"]}
|
package/dist/runner/host-dev.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createRequire, registerHooks } from "node:module";
|
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { pathToFileURL } from "node:url";
|
|
18
18
|
import vm from "node:vm";
|
|
19
|
-
import { HOST_MODULE_EXTERNALS } from "react-native-gtkx/metro";
|
|
19
|
+
import { HOST_MODULE_EXTERNALS, OPTIONAL_HOST_MODULE_EXTERNALS, } from "react-native-gtkx/metro";
|
|
20
20
|
const fail = (message) => {
|
|
21
21
|
console.error(`[react-native-gtkx] ${message}`);
|
|
22
22
|
process.exit(1);
|
|
@@ -109,6 +109,10 @@ for (const name of HOST_MODULE_EXTERNALS) {
|
|
|
109
109
|
globalThis.__hostModules[name] = await load(name);
|
|
110
110
|
}
|
|
111
111
|
catch (error) {
|
|
112
|
+
if (OPTIONAL_HOST_MODULE_EXTERNALS.has(name)) {
|
|
113
|
+
// See host.ts's identical guard — expected on the plain-GTK profile.
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
112
116
|
fail(`failed to load host module "${name}" — is the codegen store in ` +
|
|
113
117
|
`place (npx gtkx codegen) and GTK4/libadwaita installed?\n${String(error)}`);
|
|
114
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-dev.js","sourceRoot":"","sources":["../../src/runner/host-dev.ts"],"names":[],"mappings":"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","sourcesContent":["// The DEV host: fetch the dev bundle from a Metro dev server, execute it,\n// subscribe to HMR updates and let react-refresh re-render through the\n// @gtkx/react reconciler with component state preserved. Spawned by\n// `run-linux --dev` as `node dist/runner/host-dev.js <bundle-url>`; exits\n// with code 65 when a full refresh is required — the supervisor restarts.\n//\n// Division of labor (validated by spike/rn-platform, FINDINGS-dev.md):\n// metro-runtime's dev require scopes the react-refresh registration per\n// module and applies hot updates once global.__ReactRefresh exists; its\n// HMRClient speaks the /hot websocket protocol verbatim over Node's\n// global WebSocket. This file only adds the dev pieces on top of the\n// release host (./host.ts — deliberately self-contained twins: both must\n// stay runnable under bare Node, so they cannot share a relative\n// extensionless import).\nimport { createRequire, registerHooks } from \"node:module\"\nimport { join } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport vm from \"node:vm\"\nimport { HOST_MODULE_EXTERNALS } from \"react-native-gtkx/metro\"\n\ntype HostModule = Record<string, unknown>\n\nconst fail = (message: string): never => {\n console.error(`[react-native-gtkx] ${message}`)\n process.exit(1)\n}\n\nif (typeof registerHooks !== \"function\") {\n fail(\n `Node ${process.version} lacks module.registerHooks — the linux host needs Node >= 24.`,\n )\n}\n\n// react and the reconciler must run their DEV builds for react-refresh.\nprocess.env.NODE_ENV = process.env.NODE_ENV ?? \"development\"\n\nconst bundleUrl = process.argv[2]\nif (!bundleUrl) {\n fail(\"usage: node host-dev.js <metro-dev-bundle-url>\")\n}\n\n// Resolution anchors: react-refresh and metro-runtime ship with the APP's\n// react-native; @gtkx/* resolve as this package's dependencies; react must\n// be the exact instance the reconciler sees (see host.ts).\nconst fromApp = createRequire(join(process.cwd(), \"package.json\"))\nconst fromPackage = createRequire(import.meta.url)\nconst fromGtkxReact = createRequire(fromPackage.resolve(\"@gtkx/react\"))\n\n// --- react-refresh: inject BEFORE @gtkx/react loads (the reconciler\n// registers into the patched devtools hook).\nconst RefreshRuntime = fromApp(\"react-refresh/runtime\") as Record<\n string,\n unknown\n> & { injectIntoGlobalHook(target: unknown): void }\nRefreshRuntime.injectIntoGlobalHook(globalThis)\nglobalThis.$RefreshReg$ = () => {}\nglobalThis.$RefreshSig$ = () => (type: unknown) => type\nglobalThis.__ReactRefresh = {\n ...RefreshRuntime,\n performFullRefresh(reason: string) {\n console.warn(\n `[react-native-gtkx] full refresh required (${reason}) — restarting`,\n )\n process.exit(65)\n },\n}\n\n// --- virtual:gtkx-config (same mechanism and same module shape as the\n// release host — see host.ts for why each export is needed).\nconst CONFIG_REQUIRED =\n \"gtkx.config.ts with an applicationId is required in the app root \" +\n '(export default defineConfig({ applicationId: \"...\", libraries: [...] })).'\nconst { createConfigLoader } = await import(\n pathToFileURL(fromPackage.resolve(\"@gtkx/config/internal\")).href\n)\nconst gtkxConfig = await createConfigLoader()\n .resolve(process.cwd())\n .catch((error: unknown) => fail(`${CONFIG_REQUIRED}\\n${String(error)}`))\nif (!gtkxConfig.applicationId) {\n fail(CONFIG_REQUIRED)\n}\nif (gtkxConfig.elements !== null) {\n fail(\n \"gtkx.config.ts `elements.behaviors` is not supported on the linux \" +\n \"platform — react-native-gtkx owns element behaviors.\",\n )\n}\nconst configModuleUrl = new URL(\"./__virtual-gtkx-config.mjs\", import.meta.url)\n .href\nconst lazyElements = Object.fromEntries(\n gtkxConfig.lazyElements.map((type: string) => [type, { isLazy: true }]),\n)\nconst configModuleSource = [\n `export * from \"@gtkx/jsx/metadata\";`,\n `export const applicationId = ${JSON.stringify(gtkxConfig.applicationId)};`,\n `export const userEventSignals = ${JSON.stringify(gtkxConfig.userEventSignals)};`,\n `export const elements = ${JSON.stringify(lazyElements)};`,\n].join(\"\\n\")\nregisterHooks({\n resolve(specifier, context, nextResolve) {\n if (specifier === \"virtual:gtkx-config\") {\n return { url: configModuleUrl, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n },\n load(url, context, nextLoad) {\n if (url === configModuleUrl) {\n return {\n format: \"module\",\n source: configModuleSource,\n shortCircuit: true,\n }\n }\n return nextLoad(url, context)\n },\n})\n\n// --- host modules (same interop merging as the release host).\nconst resolveExternal = (name: string): string =>\n name === \"react\" || name.startsWith(\"react/\")\n ? fromGtkxReact.resolve(name)\n : fromPackage.resolve(name)\n\nconst load = async (name: string): Promise<HostModule> => {\n const namespace: HostModule = await import(\n pathToFileURL(resolveExternal(name)).href\n )\n const merged: HostModule = { __esModule: true, ...namespace }\n if (!(\"default\" in namespace)) {\n merged.default = namespace\n }\n return merged\n}\n\nglobalThis.__hostModules = {}\nfor (const name of HOST_MODULE_EXTERNALS) {\n try {\n globalThis.__hostModules[name] = await load(name)\n } catch (error) {\n fail(\n `failed to load host module \"${name}\" — is the codegen store in ` +\n `place (npx gtkx codegen) and GTK4/libadwaita installed?\\n${String(error)}`,\n )\n }\n}\nglobalThis.__hostRequire = createRequire(import.meta.url)\n\n// --- Dev Menu (Ctrl+Shift+D, the react-native-windows precedent).\n// Installed before the bundle runs so DevSettings.reload works from app\n// code; the key controller attaches once the app window exists.\nconst requestReload = (reason?: string): never => {\n console.warn(\n `[react-native-gtkx] reload requested${reason ? ` (${reason})` : \"\"} — restarting`,\n )\n return process.exit(65)\n}\nglobalThis.__rnGtkxDevHost = { reload: requestReload }\n\n// Structural slices of the GTK wrappers the menu touches — the host talks\n// to codegen namespaces through __hostModules, untyped by design.\ntype DevMenuWindow = { addController(controller: unknown): void }\ntype DevMenuDialog = {\n setHeading(heading: string): void\n setBody(body: string): void\n addResponse(id: string, label: string): void\n setResponseAppearance(id: string, appearance: unknown): void\n setDefaultResponse(id: string): void\n setCloseResponse(id: string): void\n choose(parent: DevMenuWindow): Promise<string>\n}\n\nconst installDevMenu = (): void => {\n const gtk = globalThis.__hostModules[\"@gtkx/gi/gtk\"] as {\n EventControllerKey: new () => {\n on(signal: string, handler: (...args: unknown[]) => unknown): void\n }\n }\n const gdk = globalThis.__hostModules[\"@gtkx/gi/gdk\"] as {\n ModifierType: { CONTROL_MASK: number; SHIFT_MASK: number }\n }\n const adw = globalThis.__hostModules[\"@gtkx/gi/adw\"] as {\n AlertDialog: new () => DevMenuDialog\n ResponseAppearance: { SUGGESTED: unknown }\n }\n const gio = globalThis.__hostModules[\"@gtkx/gi/gio\"] as {\n Application: {\n getDefault():\n { getActiveWindow?: () => DevMenuWindow | null } | null | undefined\n }\n }\n // GDK keysyms for latin letters equal their ASCII codes.\n const KEY_UPPER_D = 0x44\n const KEY_LOWER_D = 0x64\n\n let menuOpen = false\n const openDevMenu = async (window: DevMenuWindow): Promise<void> => {\n if (menuOpen) {\n return\n }\n menuOpen = true\n try {\n const items = globalThis.__rnGtkxDevMenuItems ?? []\n const dialog = new adw.AlertDialog()\n dialog.setHeading(\"React Native Dev Menu\")\n dialog.setBody(bundleUrl!)\n dialog.addResponse(\"cancel\", \"Cancel\")\n items.forEach((item, index) => {\n dialog.addResponse(`custom-${index}`, item.title)\n })\n dialog.addResponse(\"reload\", \"Reload\")\n dialog.setResponseAppearance(\"reload\", adw.ResponseAppearance.SUGGESTED)\n dialog.setDefaultResponse(\"reload\")\n dialog.setCloseResponse(\"cancel\")\n const response = await dialog.choose(window)\n if (response === \"reload\") {\n requestReload()\n }\n const custom = /^custom-(\\d+)$/.exec(response)\n if (custom) {\n items[Number(custom[1])]?.handler()\n }\n } catch (error) {\n console.error(`[react-native-gtkx] dev menu failed: ${String(error)}`)\n } finally {\n menuOpen = false\n }\n }\n\n const attach = (): void => {\n const window = gio.Application.getDefault()?.getActiveWindow?.()\n if (!window) {\n // The bundle opens the window asynchronously (application activate).\n setTimeout(attach, 200)\n return\n }\n const controller = new gtk.EventControllerKey()\n controller.on(\"key-pressed\", (keyval, _keycode, state) => {\n const modifiers = state as number\n const wanted =\n (modifiers & gdk.ModifierType.CONTROL_MASK) !== 0 &&\n (modifiers & gdk.ModifierType.SHIFT_MASK) !== 0 &&\n (keyval === KEY_UPPER_D || keyval === KEY_LOWER_D)\n if (wanted) {\n void openDevMenu(window)\n return true\n }\n return false\n })\n window.addController(controller)\n console.warn(\"[react-native-gtkx] dev menu ready (Ctrl+Shift+D)\")\n }\n attach()\n}\n\n// --- fetch and run the dev bundle.\nlet response: Response\ntry {\n response = await fetch(bundleUrl!)\n} catch {\n fail(`Metro dev server is not reachable (${bundleUrl}).`)\n throw new Error(\"unreachable\")\n}\nif (!response.ok) {\n fail(\n `Metro returned ${response.status} for the dev bundle:\\n${await response.text()}`,\n )\n}\nvm.runInThisContext(await response.text(), { filename: bundleUrl })\ninstallDevMenu()\n\n// --- HMR: metro-runtime's own client (ships with react-native).\ntype HmrErrorBody = { type?: string; message?: string }\ntype HmrClient = {\n send(message: string): void\n enable(): void\n on(event: string, listener: (body?: HmrErrorBody) => void): void\n}\nconst HMRClient = fromApp(\"metro-runtime/src/modules/HMRClient\") as new (\n url: string,\n) => HmrClient\nconst client = new HMRClient(\n bundleUrl!.replace(/^http/, \"ws\").replace(/\\/[^/]*$/, \"/hot\"),\n)\nclient.send(\n JSON.stringify({ type: \"register-entrypoints\", entryPoints: [bundleUrl] }),\n)\nclient.enable()\nclient.on(\"update-done\", () => {\n console.warn(\"[react-native-gtkx] hot update applied\")\n})\nclient.on(\"error\", (body) => {\n console.error(\n `[react-native-gtkx] Metro error (${body?.type ?? \"unknown\"}):\\n${body?.message ?? \"\"}`,\n )\n})\nclient.on(\"close\", () => {\n console.warn(\n \"[react-native-gtkx] HMR connection closed (the window keeps running without hot updates)\",\n )\n})\n"]}
|
|
1
|
+
{"version":3,"file":"host-dev.js","sourceRoot":"","sources":["../../src/runner/host-dev.ts"],"names":[],"mappings":"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","sourcesContent":["// The DEV host: fetch the dev bundle from a Metro dev server, execute it,\n// subscribe to HMR updates and let react-refresh re-render through the\n// @gtkx/react reconciler with component state preserved. Spawned by\n// `run-linux --dev` as `node dist/runner/host-dev.js <bundle-url>`; exits\n// with code 65 when a full refresh is required — the supervisor restarts.\n//\n// Division of labor (validated by spike/rn-platform, FINDINGS-dev.md):\n// metro-runtime's dev require scopes the react-refresh registration per\n// module and applies hot updates once global.__ReactRefresh exists; its\n// HMRClient speaks the /hot websocket protocol verbatim over Node's\n// global WebSocket. This file only adds the dev pieces on top of the\n// release host (./host.ts — deliberately self-contained twins: both must\n// stay runnable under bare Node, so they cannot share a relative\n// extensionless import).\nimport { createRequire, registerHooks } from \"node:module\"\nimport { join } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\nimport vm from \"node:vm\"\nimport {\n HOST_MODULE_EXTERNALS,\n OPTIONAL_HOST_MODULE_EXTERNALS,\n} from \"react-native-gtkx/metro\"\n\ntype HostModule = Record<string, unknown>\n\nconst fail = (message: string): never => {\n console.error(`[react-native-gtkx] ${message}`)\n process.exit(1)\n}\n\nif (typeof registerHooks !== \"function\") {\n fail(\n `Node ${process.version} lacks module.registerHooks — the linux host needs Node >= 24.`,\n )\n}\n\n// react and the reconciler must run their DEV builds for react-refresh.\nprocess.env.NODE_ENV = process.env.NODE_ENV ?? \"development\"\n\nconst bundleUrl = process.argv[2]\nif (!bundleUrl) {\n fail(\"usage: node host-dev.js <metro-dev-bundle-url>\")\n}\n\n// Resolution anchors: react-refresh and metro-runtime ship with the APP's\n// react-native; @gtkx/* resolve as this package's dependencies; react must\n// be the exact instance the reconciler sees (see host.ts).\nconst fromApp = createRequire(join(process.cwd(), \"package.json\"))\nconst fromPackage = createRequire(import.meta.url)\nconst fromGtkxReact = createRequire(fromPackage.resolve(\"@gtkx/react\"))\n\n// --- react-refresh: inject BEFORE @gtkx/react loads (the reconciler\n// registers into the patched devtools hook).\nconst RefreshRuntime = fromApp(\"react-refresh/runtime\") as Record<\n string,\n unknown\n> & { injectIntoGlobalHook(target: unknown): void }\nRefreshRuntime.injectIntoGlobalHook(globalThis)\nglobalThis.$RefreshReg$ = () => {}\nglobalThis.$RefreshSig$ = () => (type: unknown) => type\nglobalThis.__ReactRefresh = {\n ...RefreshRuntime,\n performFullRefresh(reason: string) {\n console.warn(\n `[react-native-gtkx] full refresh required (${reason}) — restarting`,\n )\n process.exit(65)\n },\n}\n\n// --- virtual:gtkx-config (same mechanism and same module shape as the\n// release host — see host.ts for why each export is needed).\nconst CONFIG_REQUIRED =\n \"gtkx.config.ts with an applicationId is required in the app root \" +\n '(export default defineConfig({ applicationId: \"...\", libraries: [...] })).'\nconst { createConfigLoader } = await import(\n pathToFileURL(fromPackage.resolve(\"@gtkx/config/internal\")).href\n)\nconst gtkxConfig = await createConfigLoader()\n .resolve(process.cwd())\n .catch((error: unknown) => fail(`${CONFIG_REQUIRED}\\n${String(error)}`))\nif (!gtkxConfig.applicationId) {\n fail(CONFIG_REQUIRED)\n}\nif (gtkxConfig.elements !== null) {\n fail(\n \"gtkx.config.ts `elements.behaviors` is not supported on the linux \" +\n \"platform — react-native-gtkx owns element behaviors.\",\n )\n}\nconst configModuleUrl = new URL(\"./__virtual-gtkx-config.mjs\", import.meta.url)\n .href\nconst lazyElements = Object.fromEntries(\n gtkxConfig.lazyElements.map((type: string) => [type, { isLazy: true }]),\n)\nconst configModuleSource = [\n `export * from \"@gtkx/jsx/metadata\";`,\n `export const applicationId = ${JSON.stringify(gtkxConfig.applicationId)};`,\n `export const userEventSignals = ${JSON.stringify(gtkxConfig.userEventSignals)};`,\n `export const elements = ${JSON.stringify(lazyElements)};`,\n].join(\"\\n\")\nregisterHooks({\n resolve(specifier, context, nextResolve) {\n if (specifier === \"virtual:gtkx-config\") {\n return { url: configModuleUrl, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n },\n load(url, context, nextLoad) {\n if (url === configModuleUrl) {\n return {\n format: \"module\",\n source: configModuleSource,\n shortCircuit: true,\n }\n }\n return nextLoad(url, context)\n },\n})\n\n// --- host modules (same interop merging as the release host).\nconst resolveExternal = (name: string): string =>\n name === \"react\" || name.startsWith(\"react/\")\n ? fromGtkxReact.resolve(name)\n : fromPackage.resolve(name)\n\nconst load = async (name: string): Promise<HostModule> => {\n const namespace: HostModule = await import(\n pathToFileURL(resolveExternal(name)).href\n )\n const merged: HostModule = { __esModule: true, ...namespace }\n if (!(\"default\" in namespace)) {\n merged.default = namespace\n }\n return merged\n}\n\nglobalThis.__hostModules = {}\nfor (const name of HOST_MODULE_EXTERNALS) {\n try {\n globalThis.__hostModules[name] = await load(name)\n } catch (error) {\n if (OPTIONAL_HOST_MODULE_EXTERNALS.has(name)) {\n // See host.ts's identical guard — expected on the plain-GTK profile.\n continue\n }\n fail(\n `failed to load host module \"${name}\" — is the codegen store in ` +\n `place (npx gtkx codegen) and GTK4/libadwaita installed?\\n${String(error)}`,\n )\n }\n}\nglobalThis.__hostRequire = createRequire(import.meta.url)\n\n// --- Dev Menu (Ctrl+Shift+D, the react-native-windows precedent).\n// Installed before the bundle runs so DevSettings.reload works from app\n// code; the key controller attaches once the app window exists.\nconst requestReload = (reason?: string): never => {\n console.warn(\n `[react-native-gtkx] reload requested${reason ? ` (${reason})` : \"\"} — restarting`,\n )\n return process.exit(65)\n}\nglobalThis.__rnGtkxDevHost = { reload: requestReload }\n\n// Structural slices of the GTK wrappers the menu touches — the host talks\n// to codegen namespaces through __hostModules, untyped by design.\ntype DevMenuWindow = { addController(controller: unknown): void }\ntype DevMenuDialog = {\n setHeading(heading: string): void\n setBody(body: string): void\n addResponse(id: string, label: string): void\n setResponseAppearance(id: string, appearance: unknown): void\n setDefaultResponse(id: string): void\n setCloseResponse(id: string): void\n choose(parent: DevMenuWindow): Promise<string>\n}\n\nconst installDevMenu = (): void => {\n const gtk = globalThis.__hostModules[\"@gtkx/gi/gtk\"] as {\n EventControllerKey: new () => {\n on(signal: string, handler: (...args: unknown[]) => unknown): void\n }\n }\n const gdk = globalThis.__hostModules[\"@gtkx/gi/gdk\"] as {\n ModifierType: { CONTROL_MASK: number; SHIFT_MASK: number }\n }\n const adw = globalThis.__hostModules[\"@gtkx/gi/adw\"] as {\n AlertDialog: new () => DevMenuDialog\n ResponseAppearance: { SUGGESTED: unknown }\n }\n const gio = globalThis.__hostModules[\"@gtkx/gi/gio\"] as {\n Application: {\n getDefault():\n { getActiveWindow?: () => DevMenuWindow | null } | null | undefined\n }\n }\n // GDK keysyms for latin letters equal their ASCII codes.\n const KEY_UPPER_D = 0x44\n const KEY_LOWER_D = 0x64\n\n let menuOpen = false\n const openDevMenu = async (window: DevMenuWindow): Promise<void> => {\n if (menuOpen) {\n return\n }\n menuOpen = true\n try {\n const items = globalThis.__rnGtkxDevMenuItems ?? []\n const dialog = new adw.AlertDialog()\n dialog.setHeading(\"React Native Dev Menu\")\n dialog.setBody(bundleUrl!)\n dialog.addResponse(\"cancel\", \"Cancel\")\n items.forEach((item, index) => {\n dialog.addResponse(`custom-${index}`, item.title)\n })\n dialog.addResponse(\"reload\", \"Reload\")\n dialog.setResponseAppearance(\"reload\", adw.ResponseAppearance.SUGGESTED)\n dialog.setDefaultResponse(\"reload\")\n dialog.setCloseResponse(\"cancel\")\n const response = await dialog.choose(window)\n if (response === \"reload\") {\n requestReload()\n }\n const custom = /^custom-(\\d+)$/.exec(response)\n if (custom) {\n items[Number(custom[1])]?.handler()\n }\n } catch (error) {\n console.error(`[react-native-gtkx] dev menu failed: ${String(error)}`)\n } finally {\n menuOpen = false\n }\n }\n\n const attach = (): void => {\n const window = gio.Application.getDefault()?.getActiveWindow?.()\n if (!window) {\n // The bundle opens the window asynchronously (application activate).\n setTimeout(attach, 200)\n return\n }\n const controller = new gtk.EventControllerKey()\n controller.on(\"key-pressed\", (keyval, _keycode, state) => {\n const modifiers = state as number\n const wanted =\n (modifiers & gdk.ModifierType.CONTROL_MASK) !== 0 &&\n (modifiers & gdk.ModifierType.SHIFT_MASK) !== 0 &&\n (keyval === KEY_UPPER_D || keyval === KEY_LOWER_D)\n if (wanted) {\n void openDevMenu(window)\n return true\n }\n return false\n })\n window.addController(controller)\n console.warn(\"[react-native-gtkx] dev menu ready (Ctrl+Shift+D)\")\n }\n attach()\n}\n\n// --- fetch and run the dev bundle.\nlet response: Response\ntry {\n response = await fetch(bundleUrl!)\n} catch {\n fail(`Metro dev server is not reachable (${bundleUrl}).`)\n throw new Error(\"unreachable\")\n}\nif (!response.ok) {\n fail(\n `Metro returned ${response.status} for the dev bundle:\\n${await response.text()}`,\n )\n}\nvm.runInThisContext(await response.text(), { filename: bundleUrl })\ninstallDevMenu()\n\n// --- HMR: metro-runtime's own client (ships with react-native).\ntype HmrErrorBody = { type?: string; message?: string }\ntype HmrClient = {\n send(message: string): void\n enable(): void\n on(event: string, listener: (body?: HmrErrorBody) => void): void\n}\nconst HMRClient = fromApp(\"metro-runtime/src/modules/HMRClient\") as new (\n url: string,\n) => HmrClient\nconst client = new HMRClient(\n bundleUrl!.replace(/^http/, \"ws\").replace(/\\/[^/]*$/, \"/hot\"),\n)\nclient.send(\n JSON.stringify({ type: \"register-entrypoints\", entryPoints: [bundleUrl] }),\n)\nclient.enable()\nclient.on(\"update-done\", () => {\n console.warn(\"[react-native-gtkx] hot update applied\")\n})\nclient.on(\"error\", (body) => {\n console.error(\n `[react-native-gtkx] Metro error (${body?.type ?? \"unknown\"}):\\n${body?.message ?? \"\"}`,\n )\n})\nclient.on(\"close\", () => {\n console.warn(\n \"[react-native-gtkx] HMR connection closed (the window keeps running without hot updates)\",\n )\n})\n"]}
|
package/dist/runner/host.js
CHANGED
|
@@ -10,7 +10,7 @@ import { readFileSync } from "node:fs";
|
|
|
10
10
|
import { createRequire, registerHooks } from "node:module";
|
|
11
11
|
import { pathToFileURL } from "node:url";
|
|
12
12
|
import vm from "node:vm";
|
|
13
|
-
import { HOST_MODULE_EXTERNALS } from "react-native-gtkx/metro";
|
|
13
|
+
import { HOST_MODULE_EXTERNALS, OPTIONAL_HOST_MODULE_EXTERNALS, } from "react-native-gtkx/metro";
|
|
14
14
|
const fail = (message) => {
|
|
15
15
|
console.error(`[react-native-gtkx] ${message}`);
|
|
16
16
|
process.exit(1);
|
|
@@ -96,6 +96,14 @@ for (const name of HOST_MODULE_EXTERNALS) {
|
|
|
96
96
|
globalThis.__hostModules[name] = await load(name);
|
|
97
97
|
}
|
|
98
98
|
catch (error) {
|
|
99
|
+
if (OPTIONAL_HOST_MODULE_EXTERNALS.has(name)) {
|
|
100
|
+
// Expected and fine: this app's gtkx.config.ts never declared the GIR
|
|
101
|
+
// library this module binds (e.g. "Adw-1" — the plain-GTK profile,
|
|
102
|
+
// see .claude/epics/adw-optional/001.md). Left unset in
|
|
103
|
+
// __hostModules; a feature that actually needs it throws its own
|
|
104
|
+
// named error when it is reached, not here at startup.
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
99
107
|
fail(`failed to load host module "${name}" — is the codegen store in ` +
|
|
100
108
|
`place (npx gtkx codegen) and GTK4/libadwaita installed?\n${String(error)}`);
|
|
101
109
|
}
|
package/dist/runner/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/runner/host.ts"],"names":[],"mappings":"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","sourcesContent":["// The Node+GTK host: executes a Metro jsbundle built for the linux\n// platform. Spawned by the run-linux command as\n// `node dist/runner/host.js <bundle>`; the bundle's externalized proxies\n// (see ../metro) read the modules injected here.\n//\n// This file must stay runnable under BARE Node: no extensionless relative\n// imports — only builtins, bare dependency specifiers and the package\n// self-reference (resolved through the exports map).\nimport { readFileSync } from \"node:fs\"\nimport { createRequire, registerHooks } from \"node:module\"\nimport { pathToFileURL } from \"node:url\"\nimport vm from \"node:vm\"\nimport { HOST_MODULE_EXTERNALS } from \"react-native-gtkx/metro\"\n\ntype HostModule = Record<string, unknown>\n\nconst fail = (message: string): never => {\n console.error(`[react-native-gtkx] ${message}`)\n process.exit(1)\n}\n\nif (typeof registerHooks !== \"function\") {\n fail(\n `Node ${process.version} lacks module.registerHooks — the linux host needs Node >= 24.`,\n )\n}\n\n// Resolution anchors. @gtkx/* and @gtkx/config resolve as THIS package's\n// dependencies; react resolves FROM @gtkx/react's real location — the\n// reconciler and the app must share one React instance, and anchoring at\n// the app could pick up a second copy installed by npm peer auto-install.\nconst fromPackage = createRequire(import.meta.url)\nconst fromGtkxReact = createRequire(fromPackage.resolve(\"@gtkx/react\"))\n\nconst resolveExternal = (name: string): string =>\n name === \"react\" || name.startsWith(\"react/\")\n ? fromGtkxReact.resolve(name)\n : fromPackage.resolve(name)\n\n// import() handles both ESM and CJS; merge a `default` for Babel's\n// default-import interop when the module has none of its own.\nconst load = async (name: string): Promise<HostModule> => {\n const namespace: HostModule = await import(\n pathToFileURL(resolveExternal(name)).href\n )\n const merged: HostModule = { __esModule: true, ...namespace }\n if (!(\"default\" in namespace)) {\n merged.default = namespace\n }\n return merged\n}\n\n// @gtkx/react imports `virtual:gtkx-config` — JSX metadata plus the values\n// resolved from gtkx.config.ts (applicationId for the GApplication, the\n// userEventSignals suppressed during commits and the elements config the\n// reconciler registry is primed with). In the vite path the gtkx CLI plugin\n// serves it; replicate it with a loader hook, mirroring @gtkx/config's own\n// renderConfigModule. The fake module URL is anchored inside this package so\n// the re-exported bare specifier resolves through node_modules.\nconst CONFIG_REQUIRED =\n \"gtkx.config.ts with an applicationId is required in the app root \" +\n '(export default defineConfig({ applicationId: \"...\", libraries: [...] })).'\nconst { createConfigLoader } = await import(\"@gtkx/config/internal\")\n// The loader validates the config, so a missing or malformed one throws.\n// rc.3 turned the loader from a bare function into `{ load, resolve }`;\n// `resolve` is the runtime projection this module needs.\nconst gtkxConfig = await createConfigLoader()\n .resolve(process.cwd())\n .catch((error: unknown) => fail(`${CONFIG_REQUIRED}\\n${String(error)}`))\nif (!gtkxConfig.applicationId) {\n fail(CONFIG_REQUIRED)\n}\n// A behaviors module would have to be bundled and merged through\n// @gtkx/react/config; the react-native surface owns element behaviors, so no\n// app declares one — refuse rather than silently drop it.\nif (gtkxConfig.elements !== null) {\n fail(\n \"gtkx.config.ts `elements.behaviors` is not supported on the linux \" +\n \"platform — react-native-gtkx owns element behaviors.\",\n )\n}\nconst configModuleUrl = new URL(\"./__virtual-gtkx-config.mjs\", import.meta.url)\n .href\nconst lazyElements = Object.fromEntries(\n gtkxConfig.lazyElements.map((type) => [type, { isLazy: true }]),\n)\nconst configModuleSource = [\n `export * from \"@gtkx/jsx/metadata\";`,\n `export const applicationId = ${JSON.stringify(gtkxConfig.applicationId)};`,\n `export const userEventSignals = ${JSON.stringify(gtkxConfig.userEventSignals)};`,\n `export const elements = ${JSON.stringify(lazyElements)};`,\n].join(\"\\n\")\nregisterHooks({\n resolve(specifier, context, nextResolve) {\n if (specifier === \"virtual:gtkx-config\") {\n return { url: configModuleUrl, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n },\n load(url, context, nextLoad) {\n if (url === configModuleUrl) {\n return {\n format: \"module\",\n source: configModuleSource,\n shortCircuit: true,\n }\n }\n return nextLoad(url, context)\n },\n})\n\nglobalThis.__hostModules = {}\nfor (const name of HOST_MODULE_EXTERNALS) {\n try {\n globalThis.__hostModules[name] = await load(name)\n } catch (error) {\n fail(\n `failed to load host module \"${name}\" — is the codegen store in ` +\n `place (npx gtkx codegen) and GTK4/libadwaita installed?\\n${String(error)}`,\n )\n }\n}\n// Node builtin proxies resolve lazily through the host's own require.\nglobalThis.__hostRequire = createRequire(import.meta.url)\n\nconst bundlePath = process.argv[2]\nif (!bundlePath) {\n fail(\"usage: node host.js <path/to/main.jsbundle>\")\n}\nlet source: string\ntry {\n source = readFileSync(bundlePath!, \"utf8\")\n} catch {\n fail(`bundle not found at ${bundlePath} — run react-native run-linux.`)\n throw new Error(\"unreachable\")\n}\n// The entry calls AppRegistry.runApplication itself (the react-native-web\n// pattern); the GLib main loop keeps the process alive afterwards.\nvm.runInThisContext(source, { filename: bundlePath })\n"]}
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/runner/host.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,gDAAgD;AAChD,yEAAyE;AACzE,iDAAiD;AACjD,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,qDAAqD;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EACL,qBAAqB,EACrB,8BAA8B,GAC/B,MAAM,yBAAyB,CAAA;AAIhC,MAAM,IAAI,GAAG,CAAC,OAAe,EAAS,EAAE;IACtC,OAAO,CAAC,KAAK,CAAC,uBAAuB,OAAO,EAAE,CAAC,CAAA;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAA;AAED,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE,CAAC;IACxC,IAAI,CACF,QAAQ,OAAO,CAAC,OAAO,gEAAgE,CACxF,CAAA;AACH,CAAC;AAED,yEAAyE;AACzE,sEAAsE;AACtE,yEAAyE;AACzE,0EAA0E;AAC1E,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAClD,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAA;AAEvE,MAAM,eAAe,GAAG,CAAC,IAAY,EAAU,EAAE,CAC/C,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;IAC3C,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC;IAC7B,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AAE/B,mEAAmE;AACnE,8DAA8D;AAC9D,MAAM,IAAI,GAAG,KAAK,EAAE,IAAY,EAAuB,EAAE;IACvD,MAAM,SAAS,GAAe,MAAM,MAAM,CACxC,aAAa,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAC1C,CAAA;IACD,MAAM,MAAM,GAAe,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,SAAS,EAAE,CAAA;IAC7D,IAAI,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,OAAO,GAAG,SAAS,CAAA;IAC5B,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,2EAA2E;AAC3E,wEAAwE;AACxE,yEAAyE;AACzE,4EAA4E;AAC5E,2EAA2E;AAC3E,6EAA6E;AAC7E,gEAAgE;AAChE,MAAM,eAAe,GACnB,mEAAmE;IACnE,4EAA4E,CAAA;AAC9E,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAA;AACpE,yEAAyE;AACzE,wEAAwE;AACxE,yDAAyD;AACzD,MAAM,UAAU,GAAG,MAAM,kBAAkB,EAAE;KAC1C,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;KACtB,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,eAAe,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;AAC1E,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,eAAe,CAAC,CAAA;AACvB,CAAC;AACD,iEAAiE;AACjE,6EAA6E;AAC7E,0DAA0D;AAC1D,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;IACjC,IAAI,CACF,oEAAoE;QAClE,sDAAsD,CACzD,CAAA;AACH,CAAC;AACD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,6BAA6B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;KAC5E,IAAI,CAAA;AACP,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW,CACrC,UAAU,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAChE,CAAA;AACD,MAAM,kBAAkB,GAAG;IACzB,qCAAqC;IACrC,gCAAgC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,GAAG;IAC3E,mCAAmC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,gBAAgB,CAAC,GAAG;IACjF,2BAA2B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG;CAC3D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACZ,aAAa,CAAC;IACZ,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW;QACrC,IAAI,SAAS,KAAK,qBAAqB,EAAE,CAAC;YACxC,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,YAAY,EAAE,IAAI,EAAE,CAAA;QACrD,CAAC;QACD,OAAO,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACxC,CAAC;IACD,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ;QACzB,IAAI,GAAG,KAAK,eAAe,EAAE,CAAC;YAC5B,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,kBAAkB;gBAC1B,YAAY,EAAE,IAAI;aACnB,CAAA;QACH,CAAC;QACD,OAAO,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAC/B,CAAC;CACF,CAAC,CAAA;AAEF,UAAU,CAAC,aAAa,GAAG,EAAE,CAAA;AAC7B,KAAK,MAAM,IAAI,IAAI,qBAAqB,EAAE,CAAC;IACzC,IAAI,CAAC;QACH,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAA;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,8BAA8B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,sEAAsE;YACtE,mEAAmE;YACnE,wDAAwD;YACxD,iEAAiE;YACjE,uDAAuD;YACvD,SAAQ;QACV,CAAC;QACD,IAAI,CACF,+BAA+B,IAAI,8BAA8B;YAC/D,4DAA4D,MAAM,CAAC,KAAK,CAAC,EAAE,CAC9E,CAAA;IACH,CAAC;AACH,CAAC;AACD,sEAAsE;AACtE,UAAU,CAAC,aAAa,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAEzD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAClC,IAAI,CAAC,UAAU,EAAE,CAAC;IAChB,IAAI,CAAC,6CAA6C,CAAC,CAAA;AACrD,CAAC;AACD,IAAI,MAAc,CAAA;AAClB,IAAI,CAAC;IACH,MAAM,GAAG,YAAY,CAAC,UAAW,EAAE,MAAM,CAAC,CAAA;AAC5C,CAAC;AAAC,MAAM,CAAC;IACP,IAAI,CAAC,uBAAuB,UAAU,gCAAgC,CAAC,CAAA;IACvE,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAA;AAChC,CAAC;AACD,0EAA0E;AAC1E,mEAAmE;AACnE,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAA","sourcesContent":["// The Node+GTK host: executes a Metro jsbundle built for the linux\n// platform. Spawned by the run-linux command as\n// `node dist/runner/host.js <bundle>`; the bundle's externalized proxies\n// (see ../metro) read the modules injected here.\n//\n// This file must stay runnable under BARE Node: no extensionless relative\n// imports — only builtins, bare dependency specifiers and the package\n// self-reference (resolved through the exports map).\nimport { readFileSync } from \"node:fs\"\nimport { createRequire, registerHooks } from \"node:module\"\nimport { pathToFileURL } from \"node:url\"\nimport vm from \"node:vm\"\nimport {\n HOST_MODULE_EXTERNALS,\n OPTIONAL_HOST_MODULE_EXTERNALS,\n} from \"react-native-gtkx/metro\"\n\ntype HostModule = Record<string, unknown>\n\nconst fail = (message: string): never => {\n console.error(`[react-native-gtkx] ${message}`)\n process.exit(1)\n}\n\nif (typeof registerHooks !== \"function\") {\n fail(\n `Node ${process.version} lacks module.registerHooks — the linux host needs Node >= 24.`,\n )\n}\n\n// Resolution anchors. @gtkx/* and @gtkx/config resolve as THIS package's\n// dependencies; react resolves FROM @gtkx/react's real location — the\n// reconciler and the app must share one React instance, and anchoring at\n// the app could pick up a second copy installed by npm peer auto-install.\nconst fromPackage = createRequire(import.meta.url)\nconst fromGtkxReact = createRequire(fromPackage.resolve(\"@gtkx/react\"))\n\nconst resolveExternal = (name: string): string =>\n name === \"react\" || name.startsWith(\"react/\")\n ? fromGtkxReact.resolve(name)\n : fromPackage.resolve(name)\n\n// import() handles both ESM and CJS; merge a `default` for Babel's\n// default-import interop when the module has none of its own.\nconst load = async (name: string): Promise<HostModule> => {\n const namespace: HostModule = await import(\n pathToFileURL(resolveExternal(name)).href\n )\n const merged: HostModule = { __esModule: true, ...namespace }\n if (!(\"default\" in namespace)) {\n merged.default = namespace\n }\n return merged\n}\n\n// @gtkx/react imports `virtual:gtkx-config` — JSX metadata plus the values\n// resolved from gtkx.config.ts (applicationId for the GApplication, the\n// userEventSignals suppressed during commits and the elements config the\n// reconciler registry is primed with). In the vite path the gtkx CLI plugin\n// serves it; replicate it with a loader hook, mirroring @gtkx/config's own\n// renderConfigModule. The fake module URL is anchored inside this package so\n// the re-exported bare specifier resolves through node_modules.\nconst CONFIG_REQUIRED =\n \"gtkx.config.ts with an applicationId is required in the app root \" +\n '(export default defineConfig({ applicationId: \"...\", libraries: [...] })).'\nconst { createConfigLoader } = await import(\"@gtkx/config/internal\")\n// The loader validates the config, so a missing or malformed one throws.\n// rc.3 turned the loader from a bare function into `{ load, resolve }`;\n// `resolve` is the runtime projection this module needs.\nconst gtkxConfig = await createConfigLoader()\n .resolve(process.cwd())\n .catch((error: unknown) => fail(`${CONFIG_REQUIRED}\\n${String(error)}`))\nif (!gtkxConfig.applicationId) {\n fail(CONFIG_REQUIRED)\n}\n// A behaviors module would have to be bundled and merged through\n// @gtkx/react/config; the react-native surface owns element behaviors, so no\n// app declares one — refuse rather than silently drop it.\nif (gtkxConfig.elements !== null) {\n fail(\n \"gtkx.config.ts `elements.behaviors` is not supported on the linux \" +\n \"platform — react-native-gtkx owns element behaviors.\",\n )\n}\nconst configModuleUrl = new URL(\"./__virtual-gtkx-config.mjs\", import.meta.url)\n .href\nconst lazyElements = Object.fromEntries(\n gtkxConfig.lazyElements.map((type) => [type, { isLazy: true }]),\n)\nconst configModuleSource = [\n `export * from \"@gtkx/jsx/metadata\";`,\n `export const applicationId = ${JSON.stringify(gtkxConfig.applicationId)};`,\n `export const userEventSignals = ${JSON.stringify(gtkxConfig.userEventSignals)};`,\n `export const elements = ${JSON.stringify(lazyElements)};`,\n].join(\"\\n\")\nregisterHooks({\n resolve(specifier, context, nextResolve) {\n if (specifier === \"virtual:gtkx-config\") {\n return { url: configModuleUrl, shortCircuit: true }\n }\n return nextResolve(specifier, context)\n },\n load(url, context, nextLoad) {\n if (url === configModuleUrl) {\n return {\n format: \"module\",\n source: configModuleSource,\n shortCircuit: true,\n }\n }\n return nextLoad(url, context)\n },\n})\n\nglobalThis.__hostModules = {}\nfor (const name of HOST_MODULE_EXTERNALS) {\n try {\n globalThis.__hostModules[name] = await load(name)\n } catch (error) {\n if (OPTIONAL_HOST_MODULE_EXTERNALS.has(name)) {\n // Expected and fine: this app's gtkx.config.ts never declared the GIR\n // library this module binds (e.g. \"Adw-1\" — the plain-GTK profile,\n // see .claude/epics/adw-optional/001.md). Left unset in\n // __hostModules; a feature that actually needs it throws its own\n // named error when it is reached, not here at startup.\n continue\n }\n fail(\n `failed to load host module \"${name}\" — is the codegen store in ` +\n `place (npx gtkx codegen) and GTK4/libadwaita installed?\\n${String(error)}`,\n )\n }\n}\n// Node builtin proxies resolve lazily through the host's own require.\nglobalThis.__hostRequire = createRequire(import.meta.url)\n\nconst bundlePath = process.argv[2]\nif (!bundlePath) {\n fail(\"usage: node host.js <path/to/main.jsbundle>\")\n}\nlet source: string\ntry {\n source = readFileSync(bundlePath!, \"utf8\")\n} catch {\n fail(`bundle not found at ${bundlePath} — run react-native run-linux.`)\n throw new Error(\"unreachable\")\n}\n// The entry calls AppRegistry.runApplication itself (the react-native-web\n// pattern); the GLib main loop keeps the process alive afterwards.\nvm.runInThisContext(source, { filename: bundlePath })\n"]}
|
package/dist/sea/bundle.js
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
// resolved once here, at bundle time (see ./gtkx-config-module.ts).
|
|
39
39
|
//
|
|
40
40
|
// Not attempted here: the vite path. Investigated, not a drop-in of this
|
|
41
|
-
// same technique — see docs/
|
|
41
|
+
// same technique — see docs/guide/packaging.md's "Choosing an artifact"
|
|
42
42
|
// section for the concrete blocker found empirically while building this:
|
|
43
43
|
// the vite bundle loads the native addon through a DYNAMICALLY obtained
|
|
44
44
|
// require (`createRequire(import.meta.url)("./gtkx.node")`), which a
|
package/dist/sea/bundle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/sea/bundle.ts"],"names":[],"mappings":"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","sourcesContent":["// Bundles a Metro-built react-native-gtkx app (main.jsbundle) into a\n// single CJS file suitable for Node's --experimental-sea-config, undoing\n// exactly the externalization HOST_MODULE_EXTERNALS performs at Metro\n// build time.\n//\n// Design, and where this follows/diverges from gtkx's own tutorial\n// (gtkx-org/gtkx examples/tutorial/scripts/bundle.ts and\n// bundle-postject.ts — read before any of this was written):\n//\n// - Bundling mechanics (bundle → single minified CJS file) and the\n// SEA/postject pipeline itself (./assemble.ts) follow the tutorial\n// closely. The bundler is rolldown rather than the tutorial's esbuild —\n// NOT a preference: rolldown is vite's own engine (vite 8 depends on it\n// outright, esbuild is only an optional peer there), and vite is a hard\n// dependency of @gtkx/cli, which is a hard dependency of this package.\n// So rolldown is already installed for every consumer, and esbuild would\n// have been the one genuinely new bundler in the tree.\n// - Native addon: the tutorial's shim assumes the addon file sits BESIDE\n// the built executable (require resolved from\n// dirname(process.execPath)). That is two files, not one — the actual\n// central design decision this epic exists to make — so this diverges:\n// the addon is embedded IN the artifact (as a SEA asset, or as a base64\n// literal for the --standalone .cjs) and extracted to a per-user cache\n// directory on first run. See ./native-shim.ts's header for the full\n// reasoning.\n// - Metro's externals: the tutorial has no equivalent problem, its source\n// is already a single vite/rollup bundle. Metro deliberately\n// externalizes @gtkx/*, react and yoga-layout (../metro/index.ts,\n// HOST_MODULE_EXTERNALS) so main.jsbundle expects\n// globalThis.__hostModules/__hostRequire to exist before it runs (see\n// ../runner/host.ts). This module is a third host implementation\n// (host.ts and host-dev.ts are the other two — see host-dev.ts's header\n// for why they don't share code) that builds those globals from real,\n// STATICALLY imported modules — every name in HOST_MODULE_EXTERNALS\n// becomes a literal `import * as` statement in the generated entry — so\n// the bundler can inline them instead of the app needing a runtime\n// node_modules to dynamically load them from. gtkx.config.ts is also\n// resolved once here, at bundle time (see ./gtkx-config-module.ts).\n//\n// Not attempted here: the vite path. Investigated, not a drop-in of this\n// same technique — see docs/getting-started.md's \"Shipping an app\"\n// section for the concrete blocker found empirically while building this:\n// the vite bundle loads the native addon through a DYNAMICALLY obtained\n// require (`createRequire(import.meta.url)(\"./gtkx.node\")`), which a\n// bundler does not intercept the way it intercepts a static import\n// (verified: the resolve hook never fires for it), and which is unlikely\n// to resolve at all once running from inside a real SEA blob, where\n// import.meta.url no longer points at a file with real siblings on disk.\nimport { mkdirSync, readdirSync, readFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join } from \"node:path\"\nimport { rolldown } from \"rolldown\"\nimport { HOST_MODULE_EXTERNALS } from \"../metro/index.js\"\nimport {\n buildGtkxConfigModule,\n virtualConfigModulePlugin,\n} from \"./gtkx-config-module.js\"\nimport {\n NATIVE_ASSET_KEY,\n nativeAddonShimPlugin,\n type NativeAddonSource,\n} from \"./native-shim.js\"\nimport { reactAnchorPlugin } from \"./react-anchor-plugin.js\"\n\nexport type MetroSeaBundleOptions = {\n /** The app root — where gtkx.config.ts lives and package.json resolves\n * HOST_MODULE_EXTERNALS' bare specifiers (react, @gtkx/*, ...) from. */\n appRoot: string\n /** Path to the release jsbundle produced by `build-linux`. */\n jsbundlePath: string\n /** Where to write the bundled CJS entry (e.g. dist/bundle.cjs). */\n outFile: string\n /** Where the native addon's bytes come from at runtime — \"sea-asset\"\n * for a real single executable, \"inline\" for a self-contained .cjs run\n * by a system Node (default: \"sea-asset\"). */\n nativeAddonSource?: NativeAddonSource\n}\n\nexport type NativeAddonAsset = {\n /** Absolute path to the platform's @gtkx/native-<platform>-<libc>.node\n * that npm installed for this machine. */\n path: string\n /** The SEA asset key it must be embedded under. */\n key: string\n}\n\n/**\n * Locates the ONE native addon file npm installed for this machine.\n * @gtkx/native picks exactly one optionalDependency per platform/libc at\n * install time (see @gtkx/native/index.js's NAPI-RS-generated loader) —\n * failing loudly on anything other than exactly one match matters here:\n * silently picking the wrong one would embed an addon for the wrong\n * architecture with no error until the app actually launches.\n */\nexport const resolveNativeAddon = (appRoot: string): NativeAddonAsset => {\n const appRequire = createRequire(join(appRoot, \"package.json\"))\n const nativePackageJson = appRequire.resolve(\"@gtkx/native/package.json\")\n // .../node_modules/@gtkx/native/package.json -> .../node_modules/@gtkx\n const scopeDir = dirname(dirname(nativePackageJson))\n const candidates = readdirSync(scopeDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && entry.name.startsWith(\"native-\"))\n .flatMap((entry) => {\n const dir = join(scopeDir, entry.name)\n return readdirSync(dir)\n .filter((file) => file.endsWith(\".node\"))\n .map((file) => join(dir, file))\n })\n const [first, ...rest] = candidates\n if (!first || rest.length > 0) {\n throw new Error(\n `expected exactly one @gtkx/native-*/*.node under ${scopeDir}, found ` +\n `${candidates.length} (${candidates.join(\", \") || \"none\"}) — ` +\n \"reinstall targeting a single platform.\",\n )\n }\n return { path: first, key: NATIVE_ASSET_KEY }\n}\n\n/**\n * Builds the synthetic SEA entry source: every HOST_MODULE_EXTERNALS name\n * loaded through a dynamic `import()` (mirroring runner/host.ts's own\n * `load()`, which does the same against a real node_modules at runtime),\n * assembled into globalThis.__hostModules, then the jsbundle text executed\n * the same way host.ts does.\n *\n * Dynamic import, not static, throughout: a Node SEA's main script is\n * CommonJS only — confirmed empirically, Node 24 executes the embedded\n * main as CJS regardless of a \"type\": \"module\" field in sea-config.json\n * or an .mjs extension — and top-level await is a hard error under a \"cjs\"\n * output format REGARDLESS of whether the module is reached via a static\n * or dynamic import (the restriction is format-wide, not per-module —\n * verified: switching yoga-layout's static import to a dynamic one alone\n * did not clear the error). yoga-layout's own entry point has exactly this\n * top-level await (it loads its WASM binary asynchronously — see\n * node_modules/yoga-layout/dist/src/index.js: `const Yoga =\n * wrapAssembly(await loadYoga())` at module scope). The actual fix: import\n * \"yoga-layout/load\" instead, which exports the same `await`, just inside\n * an async FUNCTION (`export async function loadYoga()`), not at the\n * file's top level — bundleable, and awaited here like everything else.\n */\nexport const buildEntrySource = (jsbundlePath: string): string => {\n const jsbundleSource = readFileSync(jsbundlePath, \"utf8\")\n const loadLines = HOST_MODULE_EXTERNALS.map((name) => {\n if (name === \"yoga-layout\") {\n return [\n ` {`,\n ` const { loadYoga, ...rest } = await import(\"yoga-layout/load\");`,\n ` hostModules[\"yoga-layout\"] = __interopHostModule({ ...rest, default: await loadYoga() });`,\n ` }`,\n ].join(\"\\n\")\n }\n return ` hostModules[${JSON.stringify(name)}] = __interopHostModule(await import(${JSON.stringify(name)}));`\n })\n return [\n `async function __gtkxSeaMain() {`,\n // Mirrors runner/host.ts's `load()` interop: import() handles both ESM\n // and CJS, merge a `default` for Babel's default-import interop when\n // the module has none of its own.\n ` const __interopHostModule = (ns) => {`,\n ` const merged = Object.assign({ __esModule: true }, ns);`,\n ` if (!(\"default\" in ns)) merged.default = ns;`,\n ` return merged;`,\n ` };`,\n ` const hostModules = {};`,\n ...loadLines,\n ` globalThis.__hostModules = hostModules;`,\n // Node builtin proxies resolve lazily through the host's own require —\n // real here, no createRequire indirection needed (see host.ts for why\n // it needs one: this file's import.meta.url is never used for\n // anything relative, only require() of bare/builtin specifiers).\n ` globalThis.__hostRequire = require;`,\n ` require(\"node:vm\").runInThisContext(${JSON.stringify(jsbundleSource)}, { filename: ${JSON.stringify(jsbundlePath)} });`,\n `}`,\n `__gtkxSeaMain().catch((error) => { console.error(error); process.exitCode = 1; });`,\n ].join(\"\\n\")\n}\n\n/**\n * Produces `outFile`, a single CJS file with the jsbundle, its\n * HOST_MODULE_EXTERNALS and gtkx.config.ts's resolved values all inlined,\n * and the native addon import redirected to the SEA-asset extraction\n * shim. Returns the native addon that must be embedded as a SEA asset\n * under {@link NativeAddonAsset.key} for the result to run.\n */\nexport const bundleMetroSea = async (\n options: MetroSeaBundleOptions,\n): Promise<NativeAddonAsset> => {\n const { appRoot, jsbundlePath, outFile } = options\n const nativeAddonSource = options.nativeAddonSource ?? \"sea-asset\"\n const configModuleSource = await buildGtkxConfigModule(appRoot)\n const nativeAddon = resolveNativeAddon(appRoot)\n const entrySource = buildEntrySource(jsbundlePath)\n\n mkdirSync(dirname(outFile), { recursive: true })\n\n // The entry is given a path inside appRoot that does not exist on disk,\n // rather than a `\\0`-prefixed virtual id: every bare specifier it\n // imports (react, @gtkx/*, yoga-layout) must resolve from the app's own\n // node_modules, and a real directory is what makes that happen.\n const entryId = join(appRoot, \"__gtkx-sea-entry.js\")\n\n const bundle = await rolldown({\n input: entryId,\n cwd: appRoot,\n platform: \"node\",\n plugins: [\n {\n name: \"gtkx-sea-entry\",\n resolveId: (source) => (source === entryId ? entryId : null),\n load: (id) => (id === entryId ? entrySource : null),\n },\n reactAnchorPlugin(appRoot),\n nativeAddonShimPlugin({\n specifier: \"@gtkx/native\",\n appRoot,\n source: nativeAddonSource,\n assetKey: nativeAddon.key,\n addonBytes:\n nativeAddonSource === \"inline\"\n ? readFileSync(nativeAddon.path)\n : undefined,\n }),\n virtualConfigModulePlugin(configModuleSource, appRoot),\n ],\n })\n try {\n await bundle.write({\n file: outFile,\n format: \"cjs\",\n minify: true,\n // Every HOST_MODULE_EXTERNALS name is reached through a dynamic\n // import; without this each one becomes its own chunk and the\n // \"single file\" the whole build exists to produce is a directory.\n codeSplitting: false,\n })\n } finally {\n await bundle.close()\n }\n\n return nativeAddon\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/sea/bundle.ts"],"names":[],"mappings":"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","sourcesContent":["// Bundles a Metro-built react-native-gtkx app (main.jsbundle) into a\n// single CJS file suitable for Node's --experimental-sea-config, undoing\n// exactly the externalization HOST_MODULE_EXTERNALS performs at Metro\n// build time.\n//\n// Design, and where this follows/diverges from gtkx's own tutorial\n// (gtkx-org/gtkx examples/tutorial/scripts/bundle.ts and\n// bundle-postject.ts — read before any of this was written):\n//\n// - Bundling mechanics (bundle → single minified CJS file) and the\n// SEA/postject pipeline itself (./assemble.ts) follow the tutorial\n// closely. The bundler is rolldown rather than the tutorial's esbuild —\n// NOT a preference: rolldown is vite's own engine (vite 8 depends on it\n// outright, esbuild is only an optional peer there), and vite is a hard\n// dependency of @gtkx/cli, which is a hard dependency of this package.\n// So rolldown is already installed for every consumer, and esbuild would\n// have been the one genuinely new bundler in the tree.\n// - Native addon: the tutorial's shim assumes the addon file sits BESIDE\n// the built executable (require resolved from\n// dirname(process.execPath)). That is two files, not one — the actual\n// central design decision this epic exists to make — so this diverges:\n// the addon is embedded IN the artifact (as a SEA asset, or as a base64\n// literal for the --standalone .cjs) and extracted to a per-user cache\n// directory on first run. See ./native-shim.ts's header for the full\n// reasoning.\n// - Metro's externals: the tutorial has no equivalent problem, its source\n// is already a single vite/rollup bundle. Metro deliberately\n// externalizes @gtkx/*, react and yoga-layout (../metro/index.ts,\n// HOST_MODULE_EXTERNALS) so main.jsbundle expects\n// globalThis.__hostModules/__hostRequire to exist before it runs (see\n// ../runner/host.ts). This module is a third host implementation\n// (host.ts and host-dev.ts are the other two — see host-dev.ts's header\n// for why they don't share code) that builds those globals from real,\n// STATICALLY imported modules — every name in HOST_MODULE_EXTERNALS\n// becomes a literal `import * as` statement in the generated entry — so\n// the bundler can inline them instead of the app needing a runtime\n// node_modules to dynamically load them from. gtkx.config.ts is also\n// resolved once here, at bundle time (see ./gtkx-config-module.ts).\n//\n// Not attempted here: the vite path. Investigated, not a drop-in of this\n// same technique — see docs/guide/packaging.md's \"Choosing an artifact\"\n// section for the concrete blocker found empirically while building this:\n// the vite bundle loads the native addon through a DYNAMICALLY obtained\n// require (`createRequire(import.meta.url)(\"./gtkx.node\")`), which a\n// bundler does not intercept the way it intercepts a static import\n// (verified: the resolve hook never fires for it), and which is unlikely\n// to resolve at all once running from inside a real SEA blob, where\n// import.meta.url no longer points at a file with real siblings on disk.\nimport { mkdirSync, readdirSync, readFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, join } from \"node:path\"\nimport { rolldown } from \"rolldown\"\nimport { HOST_MODULE_EXTERNALS } from \"../metro/index.js\"\nimport {\n buildGtkxConfigModule,\n virtualConfigModulePlugin,\n} from \"./gtkx-config-module.js\"\nimport {\n NATIVE_ASSET_KEY,\n nativeAddonShimPlugin,\n type NativeAddonSource,\n} from \"./native-shim.js\"\nimport { reactAnchorPlugin } from \"./react-anchor-plugin.js\"\n\nexport type MetroSeaBundleOptions = {\n /** The app root — where gtkx.config.ts lives and package.json resolves\n * HOST_MODULE_EXTERNALS' bare specifiers (react, @gtkx/*, ...) from. */\n appRoot: string\n /** Path to the release jsbundle produced by `build-linux`. */\n jsbundlePath: string\n /** Where to write the bundled CJS entry (e.g. dist/bundle.cjs). */\n outFile: string\n /** Where the native addon's bytes come from at runtime — \"sea-asset\"\n * for a real single executable, \"inline\" for a self-contained .cjs run\n * by a system Node (default: \"sea-asset\"). */\n nativeAddonSource?: NativeAddonSource\n}\n\nexport type NativeAddonAsset = {\n /** Absolute path to the platform's @gtkx/native-<platform>-<libc>.node\n * that npm installed for this machine. */\n path: string\n /** The SEA asset key it must be embedded under. */\n key: string\n}\n\n/**\n * Locates the ONE native addon file npm installed for this machine.\n * @gtkx/native picks exactly one optionalDependency per platform/libc at\n * install time (see @gtkx/native/index.js's NAPI-RS-generated loader) —\n * failing loudly on anything other than exactly one match matters here:\n * silently picking the wrong one would embed an addon for the wrong\n * architecture with no error until the app actually launches.\n */\nexport const resolveNativeAddon = (appRoot: string): NativeAddonAsset => {\n const appRequire = createRequire(join(appRoot, \"package.json\"))\n const nativePackageJson = appRequire.resolve(\"@gtkx/native/package.json\")\n // .../node_modules/@gtkx/native/package.json -> .../node_modules/@gtkx\n const scopeDir = dirname(dirname(nativePackageJson))\n const candidates = readdirSync(scopeDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && entry.name.startsWith(\"native-\"))\n .flatMap((entry) => {\n const dir = join(scopeDir, entry.name)\n return readdirSync(dir)\n .filter((file) => file.endsWith(\".node\"))\n .map((file) => join(dir, file))\n })\n const [first, ...rest] = candidates\n if (!first || rest.length > 0) {\n throw new Error(\n `expected exactly one @gtkx/native-*/*.node under ${scopeDir}, found ` +\n `${candidates.length} (${candidates.join(\", \") || \"none\"}) — ` +\n \"reinstall targeting a single platform.\",\n )\n }\n return { path: first, key: NATIVE_ASSET_KEY }\n}\n\n/**\n * Builds the synthetic SEA entry source: every HOST_MODULE_EXTERNALS name\n * loaded through a dynamic `import()` (mirroring runner/host.ts's own\n * `load()`, which does the same against a real node_modules at runtime),\n * assembled into globalThis.__hostModules, then the jsbundle text executed\n * the same way host.ts does.\n *\n * Dynamic import, not static, throughout: a Node SEA's main script is\n * CommonJS only — confirmed empirically, Node 24 executes the embedded\n * main as CJS regardless of a \"type\": \"module\" field in sea-config.json\n * or an .mjs extension — and top-level await is a hard error under a \"cjs\"\n * output format REGARDLESS of whether the module is reached via a static\n * or dynamic import (the restriction is format-wide, not per-module —\n * verified: switching yoga-layout's static import to a dynamic one alone\n * did not clear the error). yoga-layout's own entry point has exactly this\n * top-level await (it loads its WASM binary asynchronously — see\n * node_modules/yoga-layout/dist/src/index.js: `const Yoga =\n * wrapAssembly(await loadYoga())` at module scope). The actual fix: import\n * \"yoga-layout/load\" instead, which exports the same `await`, just inside\n * an async FUNCTION (`export async function loadYoga()`), not at the\n * file's top level — bundleable, and awaited here like everything else.\n */\nexport const buildEntrySource = (jsbundlePath: string): string => {\n const jsbundleSource = readFileSync(jsbundlePath, \"utf8\")\n const loadLines = HOST_MODULE_EXTERNALS.map((name) => {\n if (name === \"yoga-layout\") {\n return [\n ` {`,\n ` const { loadYoga, ...rest } = await import(\"yoga-layout/load\");`,\n ` hostModules[\"yoga-layout\"] = __interopHostModule({ ...rest, default: await loadYoga() });`,\n ` }`,\n ].join(\"\\n\")\n }\n return ` hostModules[${JSON.stringify(name)}] = __interopHostModule(await import(${JSON.stringify(name)}));`\n })\n return [\n `async function __gtkxSeaMain() {`,\n // Mirrors runner/host.ts's `load()` interop: import() handles both ESM\n // and CJS, merge a `default` for Babel's default-import interop when\n // the module has none of its own.\n ` const __interopHostModule = (ns) => {`,\n ` const merged = Object.assign({ __esModule: true }, ns);`,\n ` if (!(\"default\" in ns)) merged.default = ns;`,\n ` return merged;`,\n ` };`,\n ` const hostModules = {};`,\n ...loadLines,\n ` globalThis.__hostModules = hostModules;`,\n // Node builtin proxies resolve lazily through the host's own require —\n // real here, no createRequire indirection needed (see host.ts for why\n // it needs one: this file's import.meta.url is never used for\n // anything relative, only require() of bare/builtin specifiers).\n ` globalThis.__hostRequire = require;`,\n ` require(\"node:vm\").runInThisContext(${JSON.stringify(jsbundleSource)}, { filename: ${JSON.stringify(jsbundlePath)} });`,\n `}`,\n `__gtkxSeaMain().catch((error) => { console.error(error); process.exitCode = 1; });`,\n ].join(\"\\n\")\n}\n\n/**\n * Produces `outFile`, a single CJS file with the jsbundle, its\n * HOST_MODULE_EXTERNALS and gtkx.config.ts's resolved values all inlined,\n * and the native addon import redirected to the SEA-asset extraction\n * shim. Returns the native addon that must be embedded as a SEA asset\n * under {@link NativeAddonAsset.key} for the result to run.\n */\nexport const bundleMetroSea = async (\n options: MetroSeaBundleOptions,\n): Promise<NativeAddonAsset> => {\n const { appRoot, jsbundlePath, outFile } = options\n const nativeAddonSource = options.nativeAddonSource ?? \"sea-asset\"\n const configModuleSource = await buildGtkxConfigModule(appRoot)\n const nativeAddon = resolveNativeAddon(appRoot)\n const entrySource = buildEntrySource(jsbundlePath)\n\n mkdirSync(dirname(outFile), { recursive: true })\n\n // The entry is given a path inside appRoot that does not exist on disk,\n // rather than a `\\0`-prefixed virtual id: every bare specifier it\n // imports (react, @gtkx/*, yoga-layout) must resolve from the app's own\n // node_modules, and a real directory is what makes that happen.\n const entryId = join(appRoot, \"__gtkx-sea-entry.js\")\n\n const bundle = await rolldown({\n input: entryId,\n cwd: appRoot,\n platform: \"node\",\n plugins: [\n {\n name: \"gtkx-sea-entry\",\n resolveId: (source) => (source === entryId ? entryId : null),\n load: (id) => (id === entryId ? entrySource : null),\n },\n reactAnchorPlugin(appRoot),\n nativeAddonShimPlugin({\n specifier: \"@gtkx/native\",\n appRoot,\n source: nativeAddonSource,\n assetKey: nativeAddon.key,\n addonBytes:\n nativeAddonSource === \"inline\"\n ? readFileSync(nativeAddon.path)\n : undefined,\n }),\n virtualConfigModulePlugin(configModuleSource, appRoot),\n ],\n })\n try {\n await bundle.write({\n file: outFile,\n format: \"cjs\",\n minify: true,\n // Every HOST_MODULE_EXTERNALS name is reached through a dynamic\n // import; without this each one becomes its own chunk and the\n // \"single file\" the whole build exists to produce is a directory.\n codeSplitting: false,\n })\n } finally {\n await bundle.close()\n }\n\n return nativeAddon\n}\n"]}
|
package/dist/sea/native-shim.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// The gtkx native addon (@gtkx/native-<platform>-<libc>, a Rust/NAPI addon
|
|
2
|
-
// loaded through dlopen — see docs/
|
|
3
|
-
//
|
|
2
|
+
// loaded through dlopen — see docs/guide/packaging.md for the shipped
|
|
3
|
+
// behavior, and this file's own comments below for the full design
|
|
4
|
+
// reasoning) cannot be embedded as bundled JS:
|
|
4
5
|
// a Node SEA is a V8 code cache blob, and dlopen needs a real file on a
|
|
5
6
|
// real filesystem, not bytes inside the executable.
|
|
6
7
|
//
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-shim.js","sourceRoot":"","sources":["../../src/sea/native-shim.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,
|
|
1
|
+
{"version":3,"file":"native-shim.js","sourceRoot":"","sources":["../../src/sea/native-shim.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sEAAsE;AACtE,mEAAmE;AACnE,+CAA+C;AAC/C,wEAAwE;AACxE,oDAAoD;AACpD,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,iCAAiC;AACjC,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,uEAAuE;AACvE,2EAA2E;AAC3E,uEAAuE;AACvE,qBAAqB;AACrB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGhC,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;AAgBlD,MAAM,eAAe,GAAG,CACtB,MAAyB,EACzB,QAAgB,EACR,EAAE,CACV,MAAM,KAAK,WAAW;IACpB,CAAC,CAAC,4CAA4C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;IAC1E,CAAC,CAAC,2CAA2C,CAAA;AAEjD,MAAM,UAAU,GAAG,CACjB,MAAyB,EACzB,QAAgB,EAChB,MAAc,EACN,EAAE,CAAC;;;;;EAKX,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;;gBAEpE,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDhD,CAAA;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAA+B,EACvB,EAAE;IACV,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAC9C,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,UAAU,CACzB,MAAM,EACN,OAAO,CAAC,QAAQ,IAAI,gBAAgB,EACpC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAC7C,CAAA;IACD,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;KAChD,CAAA;AACH,CAAC,CAAA","sourcesContent":["// The gtkx native addon (@gtkx/native-<platform>-<libc>, a Rust/NAPI addon\n// loaded through dlopen — see docs/guide/packaging.md for the shipped\n// behavior, and this file's own comments below for the full design\n// reasoning) cannot be embedded as bundled JS:\n// a Node SEA is a V8 code cache blob, and dlopen needs a real file on a\n// real filesystem, not bytes inside the executable.\n//\n// So it has to be carried as bytes and written back to disk before use.\n// This plugin swaps every import of a given specifier (in practice,\n// \"@gtkx/native\" — see ../sea/bundle.ts) for a shim that does exactly\n// that: extract to a per-user cache directory (content-hash-keyed, so\n// repeat launches reuse the file instead of re-extracting ~1.6 MB every\n// start) and dlopen it from there. Where the bytes come from is the one\n// difference between the two single-file artifacts — Node's SEA \"assets\"\n// mechanism for the executable, a base64 literal for the standalone .cjs;\n// see {@link NativeAddonSource}.\n//\n// Cache location: XDG_CACHE_HOME (or ~/.cache) first, os.tmpdir() as a\n// fallback for a read-only $HOME. Deliberately NOT the SEA executable's\n// own directory — that may be read-only (a mounted image, a root-owned\n// install under /opt) or simply a different place than where the addon\n// was extracted the first time, if the binary moved. This is the same\n// question gtkx's own tutorial answers by placing the addon BESIDE the\n// executable (dist/gtkx.node next to dist/app) — a real divergence, not an\n// oversight: that keeps two files, which is exactly what this epic set\n// out to stop doing.\nimport { join } from \"node:path\"\nimport type { Plugin } from \"rolldown\"\n\nexport const NATIVE_ASSET_KEY = \"gtkx-native.node\"\n\n/**\n * Where the shim gets the addon's bytes from — the only difference between\n * the two single-file artifacts:\n *\n * - `\"sea-asset\"`: from the SEA blob, via node:sea. Only works inside a\n * real single executable.\n * - `\"inline\"`: from a base64 literal in the bundle itself, so the CJS\n * file is self-contained under a plain `node app.cjs` — the same\n * artifact minus the embedded Node runtime. Costs ~4/3 of the addon's\n * size in source text (the addon is ~1.6 MB) and is parsed once at\n * startup; the extraction and dlopen below are shared.\n */\nexport type NativeAddonSource = \"sea-asset\" | \"inline\"\n\nconst bytesExpression = (\n source: NativeAddonSource,\n assetKey: string,\n): string =>\n source === \"sea-asset\"\n ? `Buffer.from(require(\"node:sea\").getAsset(${JSON.stringify(assetKey)}))`\n : `Buffer.from(__gtkxNativeBase64, \"base64\")`\n\nconst shimSource = (\n source: NativeAddonSource,\n assetKey: string,\n base64: string,\n): string => `\nconst { createHash } = require(\"node:crypto\");\nconst { mkdirSync, existsSync, writeFileSync, renameSync } = require(\"node:fs\");\nconst { join } = require(\"node:path\");\nconst os = require(\"node:os\");\n${source === \"inline\" ? `const __gtkxNativeBase64 = ${JSON.stringify(base64)};` : \"\"}\n\nconst bytes = ${bytesExpression(source, assetKey)};\nconst hash = createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16);\nconst fileName = \"native-\" + hash + \".node\";\n\nconst candidateDirs = [\n join(process.env.XDG_CACHE_HOME || join(os.homedir(), \".cache\"), \"react-native-gtkx-sea\"),\n join(os.tmpdir(), \"react-native-gtkx-sea\"),\n];\n\nlet target;\nlet lastError;\nfor (const dir of candidateDirs) {\n try {\n mkdirSync(dir, { recursive: true });\n const candidate = join(dir, fileName);\n if (!existsSync(candidate)) {\n const tmp = candidate + \".\" + process.pid + \".tmp\";\n writeFileSync(tmp, bytes, { mode: 0o755 });\n renameSync(tmp, candidate);\n }\n target = candidate;\n break;\n } catch (error) {\n lastError = error;\n }\n}\nif (!target) {\n throw new Error(\n \"react-native-gtkx: could not extract the native addon to a writable \" +\n \"cache directory (tried \" + candidateDirs.join(\", \") + \"): \" + String(lastError),\n );\n}\n\n// Not require(target): the require() available to a SEA's main script is\n// restricted to built-ins and embedded assets (verified — requiring an\n// arbitrary absolute path from here throws ERR_UNKNOWN_BUILTIN_MODULE, the\n// same error Node throws for an unrecognized built-in specifier). That\n// restriction doesn't apply to process.dlopen(), the lower-level\n// primitive require() itself uses for \".node\" files (Module._extensions\n// [\".node\"]) — it works on any real path, which is exactly what makes\n// this shim work at all. The \"inline\" build could use a plain require()\n// here, but shares dlopen() rather than branching: one code path that is\n// proven by both artifacts beats two, one of which is rarely exercised.\nconst nativeModule = { exports: {} };\nprocess.dlopen(nativeModule, target);\n// The vite path's own build (dist/bundle.js, gtkx's CLI plugin) calls\n// this immediately after loading the addon, before anything else touches\n// it; @gtkx/runtime's compiled JS never calls it itself (confirmed by\n// inspecting its dist — it only re-exports a handful of low-level\n// functions), so ordinary require() of the addon through Node's own\n// napi module registration must be relying on something dlopen() alone\n// does not trigger. Without this call the addon has no thread registered\n// as \"the Node environment's owner\" and the first GTK-driven callback\n// into JS panics: \"the Node environment was accessed from a thread it is\n// not installed on\" (reproduced and fixed by adding this line).\nnativeModule.exports.init();\nmodule.exports = nativeModule.exports;\n`\n\nexport type NativeAddonShimOptions = {\n /** The bare specifier to replace, in practice \"@gtkx/native\". */\n specifier: string\n /** The app root — the shim's id lives here, see above. */\n appRoot: string\n /** Where the addon's bytes come from at runtime. */\n source: NativeAddonSource\n /** SEA asset key; unused when `source` is \"inline\". */\n assetKey?: string\n /** The addon's bytes; required when `source` is \"inline\". */\n addonBytes?: Buffer\n}\n\n/**\n * Intercepts every import/require of `specifier` (an exact bare module\n * specifier, e.g. \"@gtkx/native\") and replaces it with the extraction shim\n * above. The real module never reaches the bundle graph — only its\n * re-exported surface, loaded from the extracted file at runtime.\n *\n * The shim is given an id inside `appRoot` with a `.cjs` extension rather\n * than a `\\0`-prefixed virtual one: the shim IS CommonJS (it assigns\n * `module.exports` so consumers' NAMED imports of the addon still resolve\n * through the bundler's interop), and a real directory is what lets any\n * specifier inside it resolve normally.\n */\nexport const nativeAddonShimPlugin = (\n options: NativeAddonShimOptions,\n): Plugin => {\n const { specifier, appRoot, source } = options\n if (source === \"inline\" && !options.addonBytes) {\n throw new Error('the \"inline\" native addon source needs addonBytes')\n }\n const shimId = join(appRoot, \"__gtkx-sea-native-shim.cjs\")\n const contents = shimSource(\n source,\n options.assetKey ?? NATIVE_ASSET_KEY,\n options.addonBytes?.toString(\"base64\") ?? \"\",\n )\n return {\n name: \"gtkx-native-addon-shim\",\n resolveId: (id) => (id === specifier ? shimId : null),\n load: (id) => (id === shimId ? contents : null),\n }\n}\n"]}
|