react-native-gtkx 0.4.0-alpha.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apis/host.gtkx.js.map +1 -1
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/modal.js +16 -8
- package/dist/components/modal.js.map +1 -1
- package/dist/gtkx/bridge/adw.js.map +1 -1
- package/dist/gtkx/bridge/core.d.ts +1 -2
- package/dist/gtkx/bridge/core.js +2 -5
- package/dist/gtkx/bridge/core.js.map +1 -1
- package/dist/gtkx/bridge/geometry.js +1 -1
- package/dist/gtkx/bridge/geometry.js.map +1 -1
- package/dist/gtkx/bridge/layout-manager.js +18 -5
- package/dist/gtkx/bridge/layout-manager.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.js.map +1 -1
- package/dist/gtkx/bridge/svg-node.js +13 -6
- package/dist/gtkx/bridge/svg-node.js.map +1 -1
- package/dist/gtkx/bridge/view-box.js +18 -9
- package/dist/gtkx/bridge/view-box.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.adw.js +1 -1
- package/dist/gtkx/bridge/widgets.generated.adw.js.map +1 -1
- package/dist/mcp/data/generated.d.ts +58 -42
- package/dist/mcp/data/generated.js +63 -43
- package/dist/mcp/data/generated.js.map +1 -1
- package/dist/runner/host-dev.js +5 -0
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +15 -0
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.d.ts +27 -0
- package/dist/runner/index.js +206 -1
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/assemble.d.ts +9 -0
- package/dist/sea/assemble.js +16 -3
- package/dist/sea/assemble.js.map +1 -1
- package/dist/sea/bundle.d.ts +37 -2
- package/dist/sea/bundle.js +76 -4
- package/dist/sea/bundle.js.map +1 -1
- package/dist/sea/native-shim.js +1 -1
- package/dist/sea/native-shim.js.map +1 -1
- package/dist/testing/index.js +28 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/vite/index.js +3 -3
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +1 -1
- package/dist/vitest/index.js.map +1 -1
- package/package.json +9 -9
- package/dist/gtkx/bridge/use-signal.d.ts +0 -2
- package/dist/gtkx/bridge/use-signal.js +0 -41
- package/dist/gtkx/bridge/use-signal.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
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.
|
|
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.mjs, 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"]}
|
package/dist/testing/index.js
CHANGED
|
@@ -5,6 +5,34 @@ import { render, renderHook } from "@gtkx/testing";
|
|
|
5
5
|
// on a `Pressable` already work with no RN-flavoured wrapper: this subpath
|
|
6
6
|
// re-exports that surface as-is, one import source for a consumer, plus the
|
|
7
7
|
// one genuinely repeated workaround (see below).
|
|
8
|
+
//
|
|
9
|
+
// @gtkx/testing 1.0 semantics worth knowing before writing a new gtk test
|
|
10
|
+
// (audited against the whole suite; nothing here needed a source change,
|
|
11
|
+
// so it is recorded here rather than fixed):
|
|
12
|
+
//
|
|
13
|
+
// - Every query (`getByText`, `getByRole`, `screen.*`, …) only ever matches
|
|
14
|
+
// a MAPPED widget (`widget.getMapped()`) — a covered stack/sidebar page,
|
|
15
|
+
// an unpresented window, or anything with `visible={false}` is invisible
|
|
16
|
+
// to a query even though it is still in the tree. `{ hidden: true }`
|
|
17
|
+
// does NOT override this: it only widens the SEPARATE accessible-tree
|
|
18
|
+
// "hidden" filter `getByRole` applies on top. A widget that stays
|
|
19
|
+
// mounted-but-covered (createStackNavigator's inactive pages, an
|
|
20
|
+
// AdwNavigationSplitView pane hidden by collapse) is exactly this case —
|
|
21
|
+
// query only the active/visible one, the way every current test already
|
|
22
|
+
// does.
|
|
23
|
+
// - `userEvent.click`/`dblClick`/`tripleClick`/`pointer` now walk outward
|
|
24
|
+
// from the named widget through GTK's real claiming chain (the first
|
|
25
|
+
// Gtk.Button, list-box/flow-box row, or authored click gesture) instead
|
|
26
|
+
// of rc.4's "activate, else the nearest ancestor with any GestureClick".
|
|
27
|
+
// Unused anywhere in this repo's own tests today — every gesture/press
|
|
28
|
+
// test drives a real `zwlr_virtual_pointer_v1` or emits the GtkGesture
|
|
29
|
+
// signal directly (`fireEvent`) precisely because @gtkx/testing's click
|
|
30
|
+
// helpers still never produce a GdkEvent, targeting change or not.
|
|
31
|
+
// - Accessible reads (`toHaveAccessibleName`, `getByRole`'s `name`/`value`
|
|
32
|
+
// options, …) go through `gtk_test_accessible_check_*` with WAI-ARIA
|
|
33
|
+
// naming precedence and GTK mnemonic markers (`_`) stripped from any
|
|
34
|
+
// label with `use-underline` set. Not a concern for react-native-gtkx's
|
|
35
|
+
// own widgets today — nothing in the bridge sets `use-underline`.
|
|
8
36
|
export * from "@gtkx/testing";
|
|
9
37
|
export async function renderHookWithWindow(callback, options) {
|
|
10
38
|
const { container: window } = await render(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAOlD,2EAA2E;AAC3E,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,iDAAiD;AACjD,cAAc,eAAe,CAAA;AA2B7B,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmC,EACnC,OAAkC;IAIlC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,QAAoC,EACpC,OAAmC,CACpC,CAAA;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAA;AAC5B,CAAC","sourcesContent":["import { render, renderHook } from \"@gtkx/testing\"\nimport type {\n RenderHookOptions,\n RenderHookResult,\n RenderResult,\n} from \"@gtkx/testing\"\n\n// react-native-gtkx/testing — @gtkx/testing's queries, render, renderHook,\n// fireEvent and userEvent already operate on the real GTK widget tree our\n// RN components render into, so `getByText` on a `Text` or `userEvent.click`\n// on a `Pressable` already work with no RN-flavoured wrapper: this subpath\n// re-exports that surface as-is, one import source for a consumer, plus the\n// one genuinely repeated workaround (see below).\nexport * from \"@gtkx/testing\"\n\n//
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAOlD,2EAA2E;AAC3E,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,4EAA4E;AAC5E,iDAAiD;AACjD,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,6CAA6C;AAC7C,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,uEAAuE;AACvE,wEAAwE;AACxE,oEAAoE;AACpE,mEAAmE;AACnE,2EAA2E;AAC3E,0EAA0E;AAC1E,UAAU;AACV,0EAA0E;AAC1E,uEAAuE;AACvE,0EAA0E;AAC1E,2EAA2E;AAC3E,yEAAyE;AACzE,yEAAyE;AACzE,0EAA0E;AAC1E,qEAAqE;AACrE,2EAA2E;AAC3E,uEAAuE;AACvE,uEAAuE;AACvE,0EAA0E;AAC1E,oEAAoE;AACpE,cAAc,eAAe,CAAA;AA2B7B,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmC,EACnC,OAAkC;IAIlC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,QAAoC,EACpC,OAAmC,CACpC,CAAA;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAA;AAC5B,CAAC","sourcesContent":["import { render, renderHook } from \"@gtkx/testing\"\nimport type {\n RenderHookOptions,\n RenderHookResult,\n RenderResult,\n} from \"@gtkx/testing\"\n\n// react-native-gtkx/testing — @gtkx/testing's queries, render, renderHook,\n// fireEvent and userEvent already operate on the real GTK widget tree our\n// RN components render into, so `getByText` on a `Text` or `userEvent.click`\n// on a `Pressable` already work with no RN-flavoured wrapper: this subpath\n// re-exports that surface as-is, one import source for a consumer, plus the\n// one genuinely repeated workaround (see below).\n//\n// @gtkx/testing 1.0 semantics worth knowing before writing a new gtk test\n// (audited against the whole suite; nothing here needed a source change,\n// so it is recorded here rather than fixed):\n//\n// - Every query (`getByText`, `getByRole`, `screen.*`, …) only ever matches\n// a MAPPED widget (`widget.getMapped()`) — a covered stack/sidebar page,\n// an unpresented window, or anything with `visible={false}` is invisible\n// to a query even though it is still in the tree. `{ hidden: true }`\n// does NOT override this: it only widens the SEPARATE accessible-tree\n// \"hidden\" filter `getByRole` applies on top. A widget that stays\n// mounted-but-covered (createStackNavigator's inactive pages, an\n// AdwNavigationSplitView pane hidden by collapse) is exactly this case —\n// query only the active/visible one, the way every current test already\n// does.\n// - `userEvent.click`/`dblClick`/`tripleClick`/`pointer` now walk outward\n// from the named widget through GTK's real claiming chain (the first\n// Gtk.Button, list-box/flow-box row, or authored click gesture) instead\n// of rc.4's \"activate, else the nearest ancestor with any GestureClick\".\n// Unused anywhere in this repo's own tests today — every gesture/press\n// test drives a real `zwlr_virtual_pointer_v1` or emits the GtkGesture\n// signal directly (`fireEvent`) precisely because @gtkx/testing's click\n// helpers still never produce a GdkEvent, targeting change or not.\n// - Accessible reads (`toHaveAccessibleName`, `getByRole`'s `name`/`value`\n// options, …) go through `gtk_test_accessible_check_*` with WAI-ARIA\n// naming precedence and GTK mnemonic markers (`_`) stripped from any\n// label with `use-underline` set. Not a concern for react-native-gtkx's\n// own widgets today — nothing in the bridge sets `use-underline`.\nexport * from \"@gtkx/testing\"\n\n// 1.2-WORKAROUND(renderhook-no-window): see docs/gtkx-1.2-notes.md\n// @gtkx/testing's renderHook() always mounts its test component into a bare\n// Gtk.Box, so a hook that reads the active toplevel (useWindowDimensions,\n// anything through Gtk.Window.getToplevels()) has nothing to read. render()\n// always creates and presents a harness window when no container is given,\n// regardless of what it renders — rendering null is enough to get one.\n\n/**\n * Like `renderHook`, but first creates and presents a harness window (via\n * `render(null)`), so window-dependent hooks have a toplevel to read.\n * Cleanup is automatic — @gtkx/testing tears down every active render\n * (including the harness window) after each test.\n */\nexport function renderHookWithWindow<Result>(\n callback: () => Result,\n options?: RenderHookOptions<undefined>,\n): Promise<\n RenderHookResult<Result, undefined> & { window: RenderResult[\"container\"] }\n>\nexport function renderHookWithWindow<Result, Props>(\n callback: (props: Props) => Result,\n options: RenderHookOptions<Props>,\n): Promise<\n RenderHookResult<Result, Props> & { window: RenderResult[\"container\"] }\n>\nexport async function renderHookWithWindow<Result, Props>(\n callback: (props?: Props) => Result,\n options?: RenderHookOptions<Props>,\n): Promise<\n RenderHookResult<Result, Props> & { window: RenderResult[\"container\"] }\n> {\n const { container: window } = await render(null)\n const hook = await renderHook(\n callback as (props: Props) => Result,\n options as RenderHookOptions<Props>,\n )\n return { ...hook, window }\n}\n"]}
|
package/dist/vite/index.js
CHANGED
|
@@ -105,7 +105,7 @@ export const resolvePlatformSpecifier = (source, importer, exists, options = {})
|
|
|
105
105
|
// runtime-only check (a bare specifier assembled from string parts, so
|
|
106
106
|
// Rollup's build-time graph walk never reaches it) is invisible to the
|
|
107
107
|
// STATIC machinery every other gtkx import gets for free: `resolve.dedupe`
|
|
108
|
-
// (
|
|
108
|
+
// (1.2-WORKAROUND(runtime-dedupe) above) and `gtkx build`'s own asset
|
|
109
109
|
// pipeline for the native addon (`@gtkx/cli`'s `gtkx:native` plugin, which
|
|
110
110
|
// rewrites every STATICALLY reachable `@gtkx/native` import onto the single
|
|
111
111
|
// `dist/gtkx.node` it emits) both only ever see specifiers Rollup's graph
|
|
@@ -272,7 +272,7 @@ export const reactNativeGtkx = (options = {}) => {
|
|
|
272
272
|
],
|
|
273
273
|
},
|
|
274
274
|
resolve: {
|
|
275
|
-
//
|
|
275
|
+
// 1.2-WORKAROUND(runtime-dedupe): see docs/gtkx-1.2-notes.md
|
|
276
276
|
// The gtkx runtime and react are single-instance hosts: when the app
|
|
277
277
|
// and react-native-gtkx resolve them from different node_modules
|
|
278
278
|
// (file:-installed package, nested installs), two bundled copies
|
|
@@ -303,7 +303,7 @@ export const reactNativeGtkx = (options = {}) => {
|
|
|
303
303
|
// `require("./gtkx.node")` — a path RELATIVE TO WHATEVER FILE
|
|
304
304
|
// THE REWRITE LANDS IN, correct only when that file sits next to
|
|
305
305
|
// the emitted `dist/gtkx.node` asset. The entry chunk is pinned
|
|
306
|
-
// there already (`entryFileNames: "bundle.
|
|
306
|
+
// there already (`entryFileNames: "bundle.mjs"`, set by
|
|
307
307
|
// `@gtkx/cli`'s own builder); a chunk placed one level down in
|
|
308
308
|
// `assets/` breaks the SAME relative path
|
|
309
309
|
// ("Cannot find module './gtkx.node'" — reproduced building
|
package/dist/vite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"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","sourcesContent":["// react-native-gtkx/vite — makes a gtkx project React Native compatible.\n// The gtkx CLI (dev and build) starts vite with an inline config that never\n// sets `configFile: false`, so vite picks up the project's vite.config.ts\n// from the root and merges it beneath the CLI config; this preset plugs in\n// there as a single plugin.\n//\n// vite loads the config's imported packages with BARE Node, so this subpath\n// (like ./metro and ./runner) must not contain EXTENSIONLESS relative imports\n// — the pure resolution helpers live here rather than in a sibling module,\n// and the one import that does cross a file boundary spells out its `.js`.\nimport { existsSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, extname, isAbsolute, resolve } from \"node:path\"\nimport type { Plugin } from \"vite\"\nimport {\n applyAliases,\n compileAliases,\n DEFAULT_ALIAS_TABLE,\n type AliasOverrides,\n} from \"../aliases/index.js\"\n\n// Re-exported so an app configuring `aliases` never has to reach past the\n// preset subpath it already imports.\nexport {\n CONFIGURABLE_ALIASES,\n DEFAULT_ALIASES,\n PLATFORM_ALIAS,\n} from \"../aliases/index.js\"\nexport type {\n AliasOverride,\n AliasOverrides,\n AliasPattern,\n AliasTable,\n CompiledAlias,\n} from \"../aliases/index.js\"\n\n// --- pure resolution logic (unit-tested directly) -----------------------\n\n/** Options shared by the preset factory and the pure resolver helpers. */\nexport type PlatformResolutionOptions = {\n /** Platform suffix priority, most specific first. Default: [\"linux\", \"native\"]. */\n platforms?: readonly string[]\n /** Source extensions tried for every platform suffix. Default: [\"tsx\", \"ts\", \"jsx\", \"js\"]. */\n extensions?: readonly string[]\n}\n\n/** Predicate the plugin injects as fs.existsSync; tests inject fakes. */\nexport type FileExists = (filePath: string) => boolean\n\nexport const DEFAULT_PLATFORMS: readonly string[] = [\"linux\", \"native\"]\nexport const DEFAULT_EXTENSIONS: readonly string[] = [\"tsx\", \"ts\", \"jsx\", \"js\"]\n\nconst REACT_NATIVE_GTKX = \"react-native-gtkx\"\n\n/**\n * Applies the DEFAULT alias table (../aliases/index.ts) to one specifier:\n * `react-native` and its subpaths onto `react-native-gtkx`, and the five\n * package substitutions onto their compat subpaths. Returns null for every\n * other specifier, including `react-native-gtkx` itself.\n *\n * Exported for tests and for apps driving vite by hand. The plugin does NOT\n * call it — the plugin resolves through the table its own `aliases` option\n * produced, so an app's deltas apply.\n */\nexport const rewriteReactNativeImport = (source: string): string | null =>\n applyAliases(DEFAULT_ALIAS_TABLE, source)\n\n/** Splits a vite specifier into its path part and its `?query` suffix. */\nexport const splitQuery = (\n source: string,\n): { specifier: string; query: string } => {\n const index = source.indexOf(\"?\")\n if (index === -1) {\n return { specifier: source, query: \"\" }\n }\n return { specifier: source.slice(0, index), query: source.slice(index) }\n}\n\nconst isRelative = (specifier: string): boolean =>\n specifier.startsWith(\"./\") || specifier.startsWith(\"../\")\n\n/**\n * Ordered platform suffixes, platform-major: every extension of the first\n * platform is tried before any extension of the next one\n * (`.linux.tsx`, `.linux.ts`, …, `.native.tsx`, …).\n */\nexport const platformSuffixes = (\n options: PlatformResolutionOptions = {},\n): string[] => {\n const platforms = options.platforms ?? DEFAULT_PLATFORMS\n const extensions = options.extensions ?? DEFAULT_EXTENSIONS\n return platforms.flatMap((platform) =>\n extensions.map((extension) => `.${platform}.${extension}`),\n )\n}\n\n/**\n * Candidate file paths for an extensionless base path, in priority order:\n * direct platform files first, then platform index files of a directory —\n * mirroring Metro, which exhausts file resolution before directory resolution.\n * The base file itself is not a candidate: when no platform file exists the\n * plugin bails out and the default resolver picks the base module.\n */\nexport const platformCandidates = (\n base: string,\n options: PlatformResolutionOptions = {},\n): string[] => {\n const suffixes = platformSuffixes(options)\n return [\n ...suffixes.map((suffix) => `${base}${suffix}`),\n ...suffixes.map((suffix) => `${base}/index${suffix}`),\n ]\n}\n\n// Source extensions Metro strips before trying platform variants: an\n// import of \"./useLinking.js\" (the TS-ESM style react-navigation and most\n// compiled RN libraries use) must still find useLinking.native.tsx /\n// useLinking.native.js — Metro resolves the BASE name platform-first, the\n// literal file is only the fallback.\nconst STRIPPABLE_EXTENSIONS = new Set([\".js\", \".jsx\", \".mjs\", \".ts\", \".tsx\"])\n\n/**\n * Metro platform-extension resolution for a single import: relative (or\n * absolute) specifiers that are extensionless — or carry a strippable\n * source extension — try platform candidates first; the first existing\n * candidate wins; the `?query` suffix is carried over. Returns null when\n * the import is out of scope or no platform file exists, handing the\n * import back to the default resolver.\n */\nexport const resolvePlatformSpecifier = (\n source: string,\n importer: string | undefined,\n exists: FileExists,\n options: PlatformResolutionOptions = {},\n): string | null => {\n if (importer === undefined || source.startsWith(\"\\0\")) {\n return null\n }\n const { specifier, query } = splitQuery(source)\n if (!isRelative(specifier) && !isAbsolute(specifier)) {\n return null\n }\n const extension = extname(specifier)\n if (extension !== \"\" && !STRIPPABLE_EXTENSIONS.has(extension)) {\n return null\n }\n const withExtension = isAbsolute(specifier)\n ? specifier\n : resolve(dirname(splitQuery(importer).specifier), specifier)\n const base =\n extension === \"\" ? withExtension : withExtension.slice(0, -extension.length)\n for (const candidate of platformCandidates(base, options)) {\n if (exists(candidate)) {\n return candidate + query\n }\n }\n return null\n}\n\n// The bridge's Adw probe (gtkx/bridge/adw.ts) needs to know, AT BUILD TIME,\n// whether this app's codegen store actually generated Adwaita bindings —\n// see the epic's own regression notes (.claude/epics/adw-optional/006). A\n// runtime-only check (a bare specifier assembled from string parts, so\n// Rollup's build-time graph walk never reaches it) is invisible to the\n// STATIC machinery every other gtkx import gets for free: `resolve.dedupe`\n// (RC4-WORKAROUND(runtime-dedupe) above) and `gtkx build`'s own asset\n// pipeline for the native addon (`@gtkx/cli`'s `gtkx:native` plugin, which\n// rewrites every STATICALLY reachable `@gtkx/native` import onto the single\n// `dist/gtkx.node` it emits) both only ever see specifiers Rollup's graph\n// walk actually visits. A second, independently-resolved copy of the native\n// addon double-initializes the gtkx runtime and aborts the process\n// (`g_log_set_writer_func() called multiple times`) — the exact failure\n// `runtime-dedupe` exists for, reached through a path dedupe cannot see.\n//\n// The fix: make the specifier a literal wherever Adw genuinely exists, so\n// it gets the SAME static treatment as every other gtkx import — and prune\n// it entirely wherever it does not, so `gtkx:undeclared-library` never\n// throws on the plain-GTK profile. `__GTKX_ADW_AVAILABLE__` is that lever:\n// a `define`d boolean CONSTANT (like `__DEV__` above), true only when this\n// app's OWN codegen store actually has an \"adw\" entry. The bridge guards\n// its probe on it; when the guard folds to a literal `false`, esbuild's\n// dead-code elimination removes the whole probe body — literal specifiers\n// and all — before Rollup's build ever walks the module graph, so the\n// undeclared-library plugin never sees them either.\n//\n// require.resolve, not a static/dynamic import: this file is a vite CONFIG\n// module (not part of the app's own bundle), so nothing here is subject to\n// the eslint bridge fence or Rollup's build-time graph at all — it is a\n// plain Node path lookup, run once while vite starts, mirroring exactly\n// what `gtkx:undeclared-library`'s own resolveId does with `this.resolve`.\nconst hasAdwStore = (root: string): boolean => {\n try {\n createRequire(resolve(root, \"package.json\")).resolve(\n [\"@gtkx\", \"gi\", \"adw\"].join(\"/\"),\n )\n return true\n } catch {\n return false\n }\n}\n\n// The two raw specifiers every Adw-dependent file in the bridge imports as a\n// LITERAL (gtkx/bridge/adw.ts's own probe, gtkx/bridge/adw-namespace.ts,\n// gtkx/bridge/widgets.generated.adw.ts) — needed as literals so `gtkx\n// build`'s real Rollup graph gives them the same static treatment as every\n// other gtkx import (see the doc above and .claude/epics/adw-optional/006.md).\n// That literal-ness has a cost `gtkx build` never pays but `gtkx dev` and\n// vitest do: both run every file through Vite's SSR module runner, which\n// resolves every import() call it can find in a file's text — including one\n// inside a dead `if (__GTKX_ADW_AVAILABLE__ === false)` branch — as part of\n// loading that FILE, before any of the file's own code (the runtime guard,\n// the try/catch around it) ever executes. `esbuild`'s dead-code elimination\n// is what makes the literal safe for `gtkx build`, and it is a real BUNDLE\n// optimization, not something the dev server or vitest's request-based\n// transform ever performs — so on a store with no \"Adw-1\" declared, every\n// one of those three files failed to even LOAD (a raw \"'./adw' is not\n// exported\" resolver error out of Vite's own builtin:vite-resolve, not any\n// message this package writes), which took the ENTIRE package down with it:\n// gtkx/bridge/adw.ts is an ordinary dependency of apis/host.gtkx.ts,\n// components/app-registry.tsx and common/navigation-stack.tsx, so nothing\n// that reaches any of those — which is most of the surface — could load\n// either. Found writing .claude/epics/adw-optional/005.md's own guard\n// tests: spike/plain-gtk's PRE-EXISTING alert/appearance GTK tests (003.md,\n// 004.md) failed the exact same way.\n//\n// Fixed the same way `gtkx:undeclared-library` itself is a resolveId hook,\n// not a source change: intercept these two specifiers HERE, before Vite's\n// own resolver (or gtkx's) ever sees them, whenever this app's store\n// genuinely lacks Adw — resolving them to a tiny virtual module that THROWS\n// ONLY WHEN ACTUALLY EVALUATED (i.e. lazily, exactly when something really\n// awaits the dynamic import, or — for the two eager importers above — when\n// something actually loads react-native-gtkx/adw). That is late enough for\n// gtkx/bridge/adw.ts's own `try { ... } catch { return null }` to catch it\n// as a genuine rejected promise, same as it always could for a specifier\n// Node's own loader could not find; for the two EAGER importers, it turns\n// what used to be a generic third-party resolver error into this package's\n// own named, actionable throw (the ONE message text below, since neither\n// eager importer has a call-site \"feature\" name available to it the way\n// requireAdwGi/requireAdwJsx's callers do — see gtkx/bridge/adw.ts). When\n// the store DOES have Adw, `hasAdwStore` is true and this hook falls\n// through to real resolution, unchanged: `gtkx build`'s dedupe and native-\n// asset rewrite still see a real, literal specifier.\nconst ADW_ONLY_SPECIFIERS = new Set([\"@gtkx/gi/adw\", \"@gtkx/jsx/adw\"])\nconst ADW_UNAVAILABLE_PREFIX = \"\\0gtkx-adw-unavailable:\"\nconst adwUnavailableMessage = (specifier: string): string =>\n `[react-native-gtkx] \"${specifier}\" requires \"Adw-1\" in this app's ` +\n \"gtkx.config.ts `libraries` — see docs/api.md (the plain-GTK profile) \" +\n \"for what needs Adw unconditionally and what falls back without it.\"\n\n// --- the vite plugin ----------------------------------------------------\n\nexport type ReactNativeGtkxOptions = PlatformResolutionOptions & {\n /**\n * Deltas over the preset's package aliases, keyed by package name — a\n * string target, a `{ pattern, replace }` rule, or `false` to drop one of\n * ours so the real package loads. Identical semantics to the Metro\n * preset's option of the same name: both compile the same table.\n * Invalid entries throw here, while the config is being read.\n */\n aliases?: AliasOverrides\n}\n\n/**\n * Vite plugin: aliases `react-native` (and subpaths) to `react-native-gtkx`\n * and resolves Metro-style platform extensions\n * (`.linux.tsx` → `.native.tsx` → base) for extensionless imports.\n */\nexport const reactNativeGtkx = (\n options: ReactNativeGtkxOptions = {},\n): Plugin => {\n // Throws on an unknown key, an overlapping pattern or an attempt to remove\n // the platform alias — at config load, naming what is valid.\n const aliases = compileAliases(options.aliases)\n return {\n name: \"react-native-gtkx:preset\",\n // Before vite's own resolver and the gtkx CLI plugins: the alias must win\n // over node resolution and platform files must win over the base file.\n enforce: \"pre\",\n\n config: (_config, env) => ({\n // `__DEV__` is part of the react-native runtime contract, not a Metro\n // detail: RN's own modules branch on it and so does every library\n // written against them (`@gorhom/bottom-sheet`'s logger and four of its\n // components read it at module scope). The Metro path gets it from the\n // app's stock RN preset; nothing supplied it on the vite path, so a\n // library that reads it crashed the bundle at startup with\n // \"ReferenceError: __DEV__ is not defined\" — found by building\n // spike/core-exports rather than by reading anything.\n //\n // vite's mode is the honest source: `gtkx dev` builds in development,\n // `gtkx build` in production, which is exactly the distinction RN draws.\n define: {\n __DEV__: JSON.stringify(env.mode !== \"production\"),\n // See hasAdwStore's doc above — process.cwd(), not _config.root:\n // `gtkx dev`/`gtkx build` always run from the app's own directory\n // (root defaults there too), and this must be resolvable before the\n // rest of the merged config is settled.\n __GTKX_ADW_AVAILABLE__: JSON.stringify(hasAdwStore(process.cwd())),\n },\n ssr: {\n // `gtkx dev` runs vite with ssr.external: true, which would hand\n // react-native-gtkx straight to node. Keep the package inside the\n // vite pipeline; noExternal wins over external: true.\n //\n // \"react-native\" must be listed too: with ssr.external: true vite\n // externalizes a bare import BEFORE the plugin pipeline whenever the\n // package resolves natively — and the real react-native (a Flow\n // codebase Node cannot parse) exists in RN monorepos. noExternal\n // forces the full plugin resolution, where the alias rewrites the\n // import to react-native-gtkx.\n // \"react-native-reanimated\" for the same reason as \"react-native\": an\n // app that also ships iOS/Android keeps the real package installed, it\n // resolves natively, and ssr.external: true would hand Node a codebase\n // built around a Babel plugin and a worklet runtime before the alias\n // ever gets a chance to rewrite the import.\n // \"react-native-worklets\" is the same case one package over — it is\n // where that worklet runtime actually lives since Reanimated 4.\n //\n // The rule those are instances of, stated once so the next alias does\n // not have to rediscover it: EVERY package name in the alias table must\n // be listed here. An alias is a resolveId hook, and a bare specifier\n // that vite externalizes never reaches one — so on the dev path the\n // alias silently loses to whatever is installed under the real name.\n // That is not hypothetical: an app that also ships iOS and Android has\n // all of them in node_modules, which is the case the alias exists to\n // serve. `react-native-gesture-handler` proved it — this list once held\n // three of the six names, its real package loaded, and it failed on an\n // extensionless internal import Node cannot resolve (found by running\n // the gallery's upstream-libraries section, which installs it).\n //\n // So the list is DERIVED rather than written out: it is exactly the\n // table's package names, and a name can no longer go missing from one\n // of the two places it has to appear.\n //\n // `aliases.names` includes the packages an app turned OFF with `false`,\n // deliberately. Dropping an alias means the REAL package loads, and the\n // real package imports `react-native` at module scope — an import that\n // only reaches the platform alias if Node never gets the package first.\n // Un-aliasing a package is therefore a reason to keep it inside the\n // pipeline, not a reason to let it out.\n noExternal: [\n REACT_NATIVE_GTKX,\n ...aliases.names,\n // @react-navigation is not aliased, but must go through the pipeline\n // too: it imports `react-native`, and an externalized copy resolves\n // that to the real Flow package (SyntaxError: Unexpected token\n // 'typeof') instead of our alias. Only bites on the dev path — a\n // production build inlines everything anyway.\n /^@react-navigation\\//,\n ],\n },\n resolve: {\n // RC4-WORKAROUND(runtime-dedupe): see docs/gtkx-rc4-notes.md\n // The gtkx runtime and react are single-instance hosts: when the app\n // and react-native-gtkx resolve them from different node_modules\n // (file:-installed package, nested installs), two bundled copies\n // double-init the runtime and GLib aborts. dedupe pins every copy to\n // the project's own resolution.\n dedupe: [\n \"@gtkx/css\",\n \"@gtkx/gi\",\n \"@gtkx/jsx\",\n \"@gtkx/native\",\n \"@gtkx/react\",\n \"@gtkx/runtime\",\n // react-navigation is context-based: two bundled copies of core\n // fail with \"couldn't find a navigation context\".\n \"@react-navigation/core\",\n \"@react-navigation/native\",\n \"react\",\n ],\n },\n build: {\n rolldownOptions: {\n output: {\n // A literal, code-split dynamic import (the Adw seam,\n // gtkx/bridge/adw.ts — see its own doc and 006 above) lands its\n // chunk under the Rolldown default `assets/[name]-[hash].js`\n // UNLESS told otherwise. `@gtkx/cli`'s own `gtkx:native` plugin\n // rewrites every reachable `@gtkx/native` import into\n // `require(\"./gtkx.node\")` — a path RELATIVE TO WHATEVER FILE\n // THE REWRITE LANDS IN, correct only when that file sits next to\n // the emitted `dist/gtkx.node` asset. The entry chunk is pinned\n // there already (`entryFileNames: \"bundle.js\"`, set by\n // `@gtkx/cli`'s own builder); a chunk placed one level down in\n // `assets/` breaks the SAME relative path\n // (\"Cannot find module './gtkx.node'\" — reproduced building\n // examples/gallery with the Adw seam's dynamic import made\n // literal). Keeping every chunk in `dist/` alongside the entry\n // and the addon sidesteps the mismatch instead of guessing at\n // gtkx's own relative-path assumption.\n chunkFileNames: \"[name]-[hash].js\",\n },\n },\n },\n }),\n\n async resolveId(source, importer) {\n // See ADW_ONLY_SPECIFIERS' own doc above: on a store with no \"Adw-1\",\n // resolving either of these for real fails the whole file that\n // imports them, before that file's own code (a runtime guard, a\n // try/catch) ever runs. Checked first, ahead of the alias table below\n // (neither specifier is in it).\n if (ADW_ONLY_SPECIFIERS.has(source) && !hasAdwStore(process.cwd())) {\n return ADW_UNAVAILABLE_PREFIX + source\n }\n // The app's table, not the default one: a package dropped with `false`\n // falls through to vite's own resolution and the real package loads.\n const aliased = applyAliases(aliases, source)\n if (aliased !== null) {\n const resolved = await this.resolve(aliased, importer, {\n skipSelf: true,\n })\n return resolved ?? aliased\n }\n return resolvePlatformSpecifier(source, importer, existsSync, options)\n },\n\n load(id) {\n if (!id.startsWith(ADW_UNAVAILABLE_PREFIX)) {\n return null\n }\n const specifier = id.slice(ADW_UNAVAILABLE_PREFIX.length)\n return `throw new Error(${JSON.stringify(adwUnavailableMessage(specifier))})`\n },\n }\n}\n\nexport default reactNativeGtkx\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"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","sourcesContent":["// react-native-gtkx/vite — makes a gtkx project React Native compatible.\n// The gtkx CLI (dev and build) starts vite with an inline config that never\n// sets `configFile: false`, so vite picks up the project's vite.config.ts\n// from the root and merges it beneath the CLI config; this preset plugs in\n// there as a single plugin.\n//\n// vite loads the config's imported packages with BARE Node, so this subpath\n// (like ./metro and ./runner) must not contain EXTENSIONLESS relative imports\n// — the pure resolution helpers live here rather than in a sibling module,\n// and the one import that does cross a file boundary spells out its `.js`.\nimport { existsSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { dirname, extname, isAbsolute, resolve } from \"node:path\"\nimport type { Plugin } from \"vite\"\nimport {\n applyAliases,\n compileAliases,\n DEFAULT_ALIAS_TABLE,\n type AliasOverrides,\n} from \"../aliases/index.js\"\n\n// Re-exported so an app configuring `aliases` never has to reach past the\n// preset subpath it already imports.\nexport {\n CONFIGURABLE_ALIASES,\n DEFAULT_ALIASES,\n PLATFORM_ALIAS,\n} from \"../aliases/index.js\"\nexport type {\n AliasOverride,\n AliasOverrides,\n AliasPattern,\n AliasTable,\n CompiledAlias,\n} from \"../aliases/index.js\"\n\n// --- pure resolution logic (unit-tested directly) -----------------------\n\n/** Options shared by the preset factory and the pure resolver helpers. */\nexport type PlatformResolutionOptions = {\n /** Platform suffix priority, most specific first. Default: [\"linux\", \"native\"]. */\n platforms?: readonly string[]\n /** Source extensions tried for every platform suffix. Default: [\"tsx\", \"ts\", \"jsx\", \"js\"]. */\n extensions?: readonly string[]\n}\n\n/** Predicate the plugin injects as fs.existsSync; tests inject fakes. */\nexport type FileExists = (filePath: string) => boolean\n\nexport const DEFAULT_PLATFORMS: readonly string[] = [\"linux\", \"native\"]\nexport const DEFAULT_EXTENSIONS: readonly string[] = [\"tsx\", \"ts\", \"jsx\", \"js\"]\n\nconst REACT_NATIVE_GTKX = \"react-native-gtkx\"\n\n/**\n * Applies the DEFAULT alias table (../aliases/index.ts) to one specifier:\n * `react-native` and its subpaths onto `react-native-gtkx`, and the five\n * package substitutions onto their compat subpaths. Returns null for every\n * other specifier, including `react-native-gtkx` itself.\n *\n * Exported for tests and for apps driving vite by hand. The plugin does NOT\n * call it — the plugin resolves through the table its own `aliases` option\n * produced, so an app's deltas apply.\n */\nexport const rewriteReactNativeImport = (source: string): string | null =>\n applyAliases(DEFAULT_ALIAS_TABLE, source)\n\n/** Splits a vite specifier into its path part and its `?query` suffix. */\nexport const splitQuery = (\n source: string,\n): { specifier: string; query: string } => {\n const index = source.indexOf(\"?\")\n if (index === -1) {\n return { specifier: source, query: \"\" }\n }\n return { specifier: source.slice(0, index), query: source.slice(index) }\n}\n\nconst isRelative = (specifier: string): boolean =>\n specifier.startsWith(\"./\") || specifier.startsWith(\"../\")\n\n/**\n * Ordered platform suffixes, platform-major: every extension of the first\n * platform is tried before any extension of the next one\n * (`.linux.tsx`, `.linux.ts`, …, `.native.tsx`, …).\n */\nexport const platformSuffixes = (\n options: PlatformResolutionOptions = {},\n): string[] => {\n const platforms = options.platforms ?? DEFAULT_PLATFORMS\n const extensions = options.extensions ?? DEFAULT_EXTENSIONS\n return platforms.flatMap((platform) =>\n extensions.map((extension) => `.${platform}.${extension}`),\n )\n}\n\n/**\n * Candidate file paths for an extensionless base path, in priority order:\n * direct platform files first, then platform index files of a directory —\n * mirroring Metro, which exhausts file resolution before directory resolution.\n * The base file itself is not a candidate: when no platform file exists the\n * plugin bails out and the default resolver picks the base module.\n */\nexport const platformCandidates = (\n base: string,\n options: PlatformResolutionOptions = {},\n): string[] => {\n const suffixes = platformSuffixes(options)\n return [\n ...suffixes.map((suffix) => `${base}${suffix}`),\n ...suffixes.map((suffix) => `${base}/index${suffix}`),\n ]\n}\n\n// Source extensions Metro strips before trying platform variants: an\n// import of \"./useLinking.js\" (the TS-ESM style react-navigation and most\n// compiled RN libraries use) must still find useLinking.native.tsx /\n// useLinking.native.js — Metro resolves the BASE name platform-first, the\n// literal file is only the fallback.\nconst STRIPPABLE_EXTENSIONS = new Set([\".js\", \".jsx\", \".mjs\", \".ts\", \".tsx\"])\n\n/**\n * Metro platform-extension resolution for a single import: relative (or\n * absolute) specifiers that are extensionless — or carry a strippable\n * source extension — try platform candidates first; the first existing\n * candidate wins; the `?query` suffix is carried over. Returns null when\n * the import is out of scope or no platform file exists, handing the\n * import back to the default resolver.\n */\nexport const resolvePlatformSpecifier = (\n source: string,\n importer: string | undefined,\n exists: FileExists,\n options: PlatformResolutionOptions = {},\n): string | null => {\n if (importer === undefined || source.startsWith(\"\\0\")) {\n return null\n }\n const { specifier, query } = splitQuery(source)\n if (!isRelative(specifier) && !isAbsolute(specifier)) {\n return null\n }\n const extension = extname(specifier)\n if (extension !== \"\" && !STRIPPABLE_EXTENSIONS.has(extension)) {\n return null\n }\n const withExtension = isAbsolute(specifier)\n ? specifier\n : resolve(dirname(splitQuery(importer).specifier), specifier)\n const base =\n extension === \"\" ? withExtension : withExtension.slice(0, -extension.length)\n for (const candidate of platformCandidates(base, options)) {\n if (exists(candidate)) {\n return candidate + query\n }\n }\n return null\n}\n\n// The bridge's Adw probe (gtkx/bridge/adw.ts) needs to know, AT BUILD TIME,\n// whether this app's codegen store actually generated Adwaita bindings —\n// see the epic's own regression notes (.claude/epics/adw-optional/006). A\n// runtime-only check (a bare specifier assembled from string parts, so\n// Rollup's build-time graph walk never reaches it) is invisible to the\n// STATIC machinery every other gtkx import gets for free: `resolve.dedupe`\n// (1.2-WORKAROUND(runtime-dedupe) above) and `gtkx build`'s own asset\n// pipeline for the native addon (`@gtkx/cli`'s `gtkx:native` plugin, which\n// rewrites every STATICALLY reachable `@gtkx/native` import onto the single\n// `dist/gtkx.node` it emits) both only ever see specifiers Rollup's graph\n// walk actually visits. A second, independently-resolved copy of the native\n// addon double-initializes the gtkx runtime and aborts the process\n// (`g_log_set_writer_func() called multiple times`) — the exact failure\n// `runtime-dedupe` exists for, reached through a path dedupe cannot see.\n//\n// The fix: make the specifier a literal wherever Adw genuinely exists, so\n// it gets the SAME static treatment as every other gtkx import — and prune\n// it entirely wherever it does not, so `gtkx:undeclared-library` never\n// throws on the plain-GTK profile. `__GTKX_ADW_AVAILABLE__` is that lever:\n// a `define`d boolean CONSTANT (like `__DEV__` above), true only when this\n// app's OWN codegen store actually has an \"adw\" entry. The bridge guards\n// its probe on it; when the guard folds to a literal `false`, esbuild's\n// dead-code elimination removes the whole probe body — literal specifiers\n// and all — before Rollup's build ever walks the module graph, so the\n// undeclared-library plugin never sees them either.\n//\n// require.resolve, not a static/dynamic import: this file is a vite CONFIG\n// module (not part of the app's own bundle), so nothing here is subject to\n// the eslint bridge fence or Rollup's build-time graph at all — it is a\n// plain Node path lookup, run once while vite starts, mirroring exactly\n// what `gtkx:undeclared-library`'s own resolveId does with `this.resolve`.\nconst hasAdwStore = (root: string): boolean => {\n try {\n createRequire(resolve(root, \"package.json\")).resolve(\n [\"@gtkx\", \"gi\", \"adw\"].join(\"/\"),\n )\n return true\n } catch {\n return false\n }\n}\n\n// The two raw specifiers every Adw-dependent file in the bridge imports as a\n// LITERAL (gtkx/bridge/adw.ts's own probe, gtkx/bridge/adw-namespace.ts,\n// gtkx/bridge/widgets.generated.adw.ts) — needed as literals so `gtkx\n// build`'s real Rollup graph gives them the same static treatment as every\n// other gtkx import (see the doc above and .claude/epics/adw-optional/006.md).\n// That literal-ness has a cost `gtkx build` never pays but `gtkx dev` and\n// vitest do: both run every file through Vite's SSR module runner, which\n// resolves every import() call it can find in a file's text — including one\n// inside a dead `if (__GTKX_ADW_AVAILABLE__ === false)` branch — as part of\n// loading that FILE, before any of the file's own code (the runtime guard,\n// the try/catch around it) ever executes. `esbuild`'s dead-code elimination\n// is what makes the literal safe for `gtkx build`, and it is a real BUNDLE\n// optimization, not something the dev server or vitest's request-based\n// transform ever performs — so on a store with no \"Adw-1\" declared, every\n// one of those three files failed to even LOAD (a raw \"'./adw' is not\n// exported\" resolver error out of Vite's own builtin:vite-resolve, not any\n// message this package writes), which took the ENTIRE package down with it:\n// gtkx/bridge/adw.ts is an ordinary dependency of apis/host.gtkx.ts,\n// components/app-registry.tsx and common/navigation-stack.tsx, so nothing\n// that reaches any of those — which is most of the surface — could load\n// either. Found writing .claude/epics/adw-optional/005.md's own guard\n// tests: spike/plain-gtk's PRE-EXISTING alert/appearance GTK tests (003.md,\n// 004.md) failed the exact same way.\n//\n// Fixed the same way `gtkx:undeclared-library` itself is a resolveId hook,\n// not a source change: intercept these two specifiers HERE, before Vite's\n// own resolver (or gtkx's) ever sees them, whenever this app's store\n// genuinely lacks Adw — resolving them to a tiny virtual module that THROWS\n// ONLY WHEN ACTUALLY EVALUATED (i.e. lazily, exactly when something really\n// awaits the dynamic import, or — for the two eager importers above — when\n// something actually loads react-native-gtkx/adw). That is late enough for\n// gtkx/bridge/adw.ts's own `try { ... } catch { return null }` to catch it\n// as a genuine rejected promise, same as it always could for a specifier\n// Node's own loader could not find; for the two EAGER importers, it turns\n// what used to be a generic third-party resolver error into this package's\n// own named, actionable throw (the ONE message text below, since neither\n// eager importer has a call-site \"feature\" name available to it the way\n// requireAdwGi/requireAdwJsx's callers do — see gtkx/bridge/adw.ts). When\n// the store DOES have Adw, `hasAdwStore` is true and this hook falls\n// through to real resolution, unchanged: `gtkx build`'s dedupe and native-\n// asset rewrite still see a real, literal specifier.\nconst ADW_ONLY_SPECIFIERS = new Set([\"@gtkx/gi/adw\", \"@gtkx/jsx/adw\"])\nconst ADW_UNAVAILABLE_PREFIX = \"\\0gtkx-adw-unavailable:\"\nconst adwUnavailableMessage = (specifier: string): string =>\n `[react-native-gtkx] \"${specifier}\" requires \"Adw-1\" in this app's ` +\n \"gtkx.config.ts `libraries` — see docs/api.md (the plain-GTK profile) \" +\n \"for what needs Adw unconditionally and what falls back without it.\"\n\n// --- the vite plugin ----------------------------------------------------\n\nexport type ReactNativeGtkxOptions = PlatformResolutionOptions & {\n /**\n * Deltas over the preset's package aliases, keyed by package name — a\n * string target, a `{ pattern, replace }` rule, or `false` to drop one of\n * ours so the real package loads. Identical semantics to the Metro\n * preset's option of the same name: both compile the same table.\n * Invalid entries throw here, while the config is being read.\n */\n aliases?: AliasOverrides\n}\n\n/**\n * Vite plugin: aliases `react-native` (and subpaths) to `react-native-gtkx`\n * and resolves Metro-style platform extensions\n * (`.linux.tsx` → `.native.tsx` → base) for extensionless imports.\n */\nexport const reactNativeGtkx = (\n options: ReactNativeGtkxOptions = {},\n): Plugin => {\n // Throws on an unknown key, an overlapping pattern or an attempt to remove\n // the platform alias — at config load, naming what is valid.\n const aliases = compileAliases(options.aliases)\n return {\n name: \"react-native-gtkx:preset\",\n // Before vite's own resolver and the gtkx CLI plugins: the alias must win\n // over node resolution and platform files must win over the base file.\n enforce: \"pre\",\n\n config: (_config, env) => ({\n // `__DEV__` is part of the react-native runtime contract, not a Metro\n // detail: RN's own modules branch on it and so does every library\n // written against them (`@gorhom/bottom-sheet`'s logger and four of its\n // components read it at module scope). The Metro path gets it from the\n // app's stock RN preset; nothing supplied it on the vite path, so a\n // library that reads it crashed the bundle at startup with\n // \"ReferenceError: __DEV__ is not defined\" — found by building\n // spike/core-exports rather than by reading anything.\n //\n // vite's mode is the honest source: `gtkx dev` builds in development,\n // `gtkx build` in production, which is exactly the distinction RN draws.\n define: {\n __DEV__: JSON.stringify(env.mode !== \"production\"),\n // See hasAdwStore's doc above — process.cwd(), not _config.root:\n // `gtkx dev`/`gtkx build` always run from the app's own directory\n // (root defaults there too), and this must be resolvable before the\n // rest of the merged config is settled.\n __GTKX_ADW_AVAILABLE__: JSON.stringify(hasAdwStore(process.cwd())),\n },\n ssr: {\n // `gtkx dev` runs vite with ssr.external: true, which would hand\n // react-native-gtkx straight to node. Keep the package inside the\n // vite pipeline; noExternal wins over external: true.\n //\n // \"react-native\" must be listed too: with ssr.external: true vite\n // externalizes a bare import BEFORE the plugin pipeline whenever the\n // package resolves natively — and the real react-native (a Flow\n // codebase Node cannot parse) exists in RN monorepos. noExternal\n // forces the full plugin resolution, where the alias rewrites the\n // import to react-native-gtkx.\n // \"react-native-reanimated\" for the same reason as \"react-native\": an\n // app that also ships iOS/Android keeps the real package installed, it\n // resolves natively, and ssr.external: true would hand Node a codebase\n // built around a Babel plugin and a worklet runtime before the alias\n // ever gets a chance to rewrite the import.\n // \"react-native-worklets\" is the same case one package over — it is\n // where that worklet runtime actually lives since Reanimated 4.\n //\n // The rule those are instances of, stated once so the next alias does\n // not have to rediscover it: EVERY package name in the alias table must\n // be listed here. An alias is a resolveId hook, and a bare specifier\n // that vite externalizes never reaches one — so on the dev path the\n // alias silently loses to whatever is installed under the real name.\n // That is not hypothetical: an app that also ships iOS and Android has\n // all of them in node_modules, which is the case the alias exists to\n // serve. `react-native-gesture-handler` proved it — this list once held\n // three of the six names, its real package loaded, and it failed on an\n // extensionless internal import Node cannot resolve (found by running\n // the gallery's upstream-libraries section, which installs it).\n //\n // So the list is DERIVED rather than written out: it is exactly the\n // table's package names, and a name can no longer go missing from one\n // of the two places it has to appear.\n //\n // `aliases.names` includes the packages an app turned OFF with `false`,\n // deliberately. Dropping an alias means the REAL package loads, and the\n // real package imports `react-native` at module scope — an import that\n // only reaches the platform alias if Node never gets the package first.\n // Un-aliasing a package is therefore a reason to keep it inside the\n // pipeline, not a reason to let it out.\n noExternal: [\n REACT_NATIVE_GTKX,\n ...aliases.names,\n // @react-navigation is not aliased, but must go through the pipeline\n // too: it imports `react-native`, and an externalized copy resolves\n // that to the real Flow package (SyntaxError: Unexpected token\n // 'typeof') instead of our alias. Only bites on the dev path — a\n // production build inlines everything anyway.\n /^@react-navigation\\//,\n ],\n },\n resolve: {\n // 1.2-WORKAROUND(runtime-dedupe): see docs/gtkx-1.2-notes.md\n // The gtkx runtime and react are single-instance hosts: when the app\n // and react-native-gtkx resolve them from different node_modules\n // (file:-installed package, nested installs), two bundled copies\n // double-init the runtime and GLib aborts. dedupe pins every copy to\n // the project's own resolution.\n dedupe: [\n \"@gtkx/css\",\n \"@gtkx/gi\",\n \"@gtkx/jsx\",\n \"@gtkx/native\",\n \"@gtkx/react\",\n \"@gtkx/runtime\",\n // react-navigation is context-based: two bundled copies of core\n // fail with \"couldn't find a navigation context\".\n \"@react-navigation/core\",\n \"@react-navigation/native\",\n \"react\",\n ],\n },\n build: {\n rolldownOptions: {\n output: {\n // A literal, code-split dynamic import (the Adw seam,\n // gtkx/bridge/adw.ts — see its own doc and 006 above) lands its\n // chunk under the Rolldown default `assets/[name]-[hash].js`\n // UNLESS told otherwise. `@gtkx/cli`'s own `gtkx:native` plugin\n // rewrites every reachable `@gtkx/native` import into\n // `require(\"./gtkx.node\")` — a path RELATIVE TO WHATEVER FILE\n // THE REWRITE LANDS IN, correct only when that file sits next to\n // the emitted `dist/gtkx.node` asset. The entry chunk is pinned\n // there already (`entryFileNames: \"bundle.mjs\"`, set by\n // `@gtkx/cli`'s own builder); a chunk placed one level down in\n // `assets/` breaks the SAME relative path\n // (\"Cannot find module './gtkx.node'\" — reproduced building\n // examples/gallery with the Adw seam's dynamic import made\n // literal). Keeping every chunk in `dist/` alongside the entry\n // and the addon sidesteps the mismatch instead of guessing at\n // gtkx's own relative-path assumption.\n chunkFileNames: \"[name]-[hash].js\",\n },\n },\n },\n }),\n\n async resolveId(source, importer) {\n // See ADW_ONLY_SPECIFIERS' own doc above: on a store with no \"Adw-1\",\n // resolving either of these for real fails the whole file that\n // imports them, before that file's own code (a runtime guard, a\n // try/catch) ever runs. Checked first, ahead of the alias table below\n // (neither specifier is in it).\n if (ADW_ONLY_SPECIFIERS.has(source) && !hasAdwStore(process.cwd())) {\n return ADW_UNAVAILABLE_PREFIX + source\n }\n // The app's table, not the default one: a package dropped with `false`\n // falls through to vite's own resolution and the real package loads.\n const aliased = applyAliases(aliases, source)\n if (aliased !== null) {\n const resolved = await this.resolve(aliased, importer, {\n skipSelf: true,\n })\n return resolved ?? aliased\n }\n return resolvePlatformSpecifier(source, importer, existsSync, options)\n },\n\n load(id) {\n if (!id.startsWith(ADW_UNAVAILABLE_PREFIX)) {\n return null\n }\n const specifier = id.slice(ADW_UNAVAILABLE_PREFIX.length)\n return `throw new Error(${JSON.stringify(adwUnavailableMessage(specifier))})`\n },\n }\n}\n\nexport default reactNativeGtkx\n"]}
|
package/dist/vitest/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type ReactNativeGtkxTestOptions = {
|
|
|
26
26
|
/**
|
|
27
27
|
* Run this project's test files serially. Default: false — window-resize
|
|
28
28
|
* signal delivery races when several per-worker headless compositors run
|
|
29
|
-
* at once (see docs/gtkx-
|
|
29
|
+
* at once (see docs/gtkx-1.2-notes.md); the suite this recipe is proven
|
|
30
30
|
* on takes seconds, so serial execution is the cheap fix. Override once a
|
|
31
31
|
* suite is large enough for parallelism to matter more than that race.
|
|
32
32
|
*/
|
package/dist/vitest/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vitest/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AACxE,uEAAuE;AACvE,mDAAmD;AACnD,EAAE;AACF,iEAAiE;AACjE,2EAA2E;AAC3E,yEAAyE;AACzE,4EAA4E;AAC5E,qEAAqE;AACrE,0EAA0E;AAC1E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAI7C,OAAO,EAAE,eAAe,EAA+B,MAAM,kBAAkB,CAAA;AAkC/E,MAAM,eAAe,GAAG,CAAC,wBAAwB,CAAC,CAAA;AAClD,MAAM,mBAAmB,GAAwB,CAAC,mBAAmB,CAAC,CAAA;AACtE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAExE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,UAAsC,EAAE,EACV,EAAE;IAChC,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAA;IACnD,MAAM,OAAO,GAAa;QACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACtB,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;KAClC,CAAA;IACD,OAAO;QACL,OAAO;QACP,IAAI,EAAE;YACJ,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,KAAK;YAC3B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe;YAC3C,UAAU,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;YACvD,MAAM,EAAE;gBACN,IAAI,EAAE;oBACJ,MAAM,EAAE,CAAC,GAAG,mBAAmB,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;iBAChE;aACF;YACD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK;SAClD;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,mBAAmB,CAAA","sourcesContent":["// react-native-gtkx/vitest — the vitest recipe this repo's own test suite\n// runs on (see vitest.config.ts's \"gtk\" project), packaged for a consumer\n// app: the @gtkx/vitest headless-compositor plugin, the react-native-gtkx\n// vite preset (react-native alias + Metro-style platform extensions), a\n// default inline-deps list for RN libraries that import \"react-native\"\n// themselves, and the React act-environment setup.\n//\n// Self-contained like ./vite, ./metro and ./runner: a consumer's\n// vitest.config.ts is loaded the same way vite.config.ts is (vite's config\n// loader bundles only the user's own relative import graph; node_modules\n// imports stay bare specifiers, resolved afterwards by plain Node ESM) — so\n// this file must not contain extensionless relative imports. The one\n// relative import below carries an explicit \".js\" extension, which a real\n// sibling file on disk resolves under plain Node without any bundler.\nimport { fileURLToPath, URL } from \"node:url\"\nimport type { PluginOptions as GtkxPluginOptions } from \"@gtkx/vitest\"\nimport type { Plugin } from \"vite\"\nimport type { UserWorkspaceConfig } from \"vitest/config\"\nimport { reactNativeGtkx, type ReactNativeGtkxOptions } from \"../vite/index.js\"\n\n/**\n * Options accepted by {@link reactNativeGtkxTest}. Every field is optional\n * and falls back to the recipe this repo's own test suite runs on.\n */\nexport type ReactNativeGtkxTestOptions = {\n /** Vitest project name. Default: \"gtk\". */\n name?: string\n /** Test file glob(s), relative to the project root. Default: `[\"**\\/*.gtk.test.{ts,tsx}\"]`. */\n include?: string[]\n /** Headless compositor size/compositor, forwarded to @gtkx/vitest's plugin. */\n headless?: GtkxPluginOptions\n /** Metro-style platform resolution options, forwarded to the vite preset. */\n platform?: ReactNativeGtkxOptions\n /**\n * Extra `server.deps.inline` patterns, merged after the default\n * (`/@react-navigation/`). Needed for any node_modules library that\n * itself imports \"react-native\" — Vitest externalizes node_modules code\n * by default, a path the react-native alias cannot reach.\n */\n inlineDeps?: (string | RegExp)[]\n /** Extra setup files, run after the built-in React act-environment setup. */\n setupFiles?: string[]\n /**\n * Run this project's test files serially. Default: false — window-resize\n * signal delivery races when several per-worker headless compositors run\n * at once (see docs/gtkx-
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vitest/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,wEAAwE;AACxE,uEAAuE;AACvE,mDAAmD;AACnD,EAAE;AACF,iEAAiE;AACjE,2EAA2E;AAC3E,yEAAyE;AACzE,4EAA4E;AAC5E,qEAAqE;AACrE,0EAA0E;AAC1E,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAI7C,OAAO,EAAE,eAAe,EAA+B,MAAM,kBAAkB,CAAA;AAkC/E,MAAM,eAAe,GAAG,CAAC,wBAAwB,CAAC,CAAA;AAClD,MAAM,mBAAmB,GAAwB,CAAC,mBAAmB,CAAC,CAAA;AACtE,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAExE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,UAAsC,EAAE,EACV,EAAE;IAChC,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAA;IACnD,MAAM,OAAO,GAAa;QACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACtB,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC;KAClC,CAAA;IACD,OAAO;QACL,OAAO;QACP,IAAI,EAAE;YACJ,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,KAAK;YAC3B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe;YAC3C,UAAU,EAAE,CAAC,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;YACvD,MAAM,EAAE;gBACN,IAAI,EAAE;oBACJ,MAAM,EAAE,CAAC,GAAG,mBAAmB,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;iBAChE;aACF;YACD,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK;SAClD;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,mBAAmB,CAAA","sourcesContent":["// react-native-gtkx/vitest — the vitest recipe this repo's own test suite\n// runs on (see vitest.config.ts's \"gtk\" project), packaged for a consumer\n// app: the @gtkx/vitest headless-compositor plugin, the react-native-gtkx\n// vite preset (react-native alias + Metro-style platform extensions), a\n// default inline-deps list for RN libraries that import \"react-native\"\n// themselves, and the React act-environment setup.\n//\n// Self-contained like ./vite, ./metro and ./runner: a consumer's\n// vitest.config.ts is loaded the same way vite.config.ts is (vite's config\n// loader bundles only the user's own relative import graph; node_modules\n// imports stay bare specifiers, resolved afterwards by plain Node ESM) — so\n// this file must not contain extensionless relative imports. The one\n// relative import below carries an explicit \".js\" extension, which a real\n// sibling file on disk resolves under plain Node without any bundler.\nimport { fileURLToPath, URL } from \"node:url\"\nimport type { PluginOptions as GtkxPluginOptions } from \"@gtkx/vitest\"\nimport type { Plugin } from \"vite\"\nimport type { UserWorkspaceConfig } from \"vitest/config\"\nimport { reactNativeGtkx, type ReactNativeGtkxOptions } from \"../vite/index.js\"\n\n/**\n * Options accepted by {@link reactNativeGtkxTest}. Every field is optional\n * and falls back to the recipe this repo's own test suite runs on.\n */\nexport type ReactNativeGtkxTestOptions = {\n /** Vitest project name. Default: \"gtk\". */\n name?: string\n /** Test file glob(s), relative to the project root. Default: `[\"**\\/*.gtk.test.{ts,tsx}\"]`. */\n include?: string[]\n /** Headless compositor size/compositor, forwarded to @gtkx/vitest's plugin. */\n headless?: GtkxPluginOptions\n /** Metro-style platform resolution options, forwarded to the vite preset. */\n platform?: ReactNativeGtkxOptions\n /**\n * Extra `server.deps.inline` patterns, merged after the default\n * (`/@react-navigation/`). Needed for any node_modules library that\n * itself imports \"react-native\" — Vitest externalizes node_modules code\n * by default, a path the react-native alias cannot reach.\n */\n inlineDeps?: (string | RegExp)[]\n /** Extra setup files, run after the built-in React act-environment setup. */\n setupFiles?: string[]\n /**\n * Run this project's test files serially. Default: false — window-resize\n * signal delivery races when several per-worker headless compositors run\n * at once (see docs/gtkx-1.2-notes.md); the suite this recipe is proven\n * on takes seconds, so serial execution is the cheap fix. Override once a\n * suite is large enough for parallelism to matter more than that race.\n */\n fileParallelism?: boolean\n}\n\nconst DEFAULT_INCLUDE = [\"**/*.gtk.test.{ts,tsx}\"]\nconst DEFAULT_INLINE_DEPS: (string | RegExp)[] = [/@react-navigation/]\nconst SETUP_FILE = fileURLToPath(new URL(\"./setup.js\", import.meta.url))\n\n/**\n * A ready Vitest project config for react-native-gtkx component tests: the\n * headless Wayland compositor, the react-native alias and platform\n * extensions, and the React act-environment setup — the same recipe this\n * repo's own suite runs on. Use the result as a whole config\n * (`defineConfig(reactNativeGtkxTest())`) or as one entry of\n * `test.projects` alongside a portable \"unit\" project.\n *\n * Requires a headless Wayland compositor on PATH (`sway` by default —\n * `apt install sway xwayland dbus`, see docs/guide/toolchains.md) and\n * `gtkx codegen` to have already generated the project's `@gtkx/gi`\n * bindings — a bare `vitest run` does not trigger codegen itself, unlike\n * `gtkx dev`/`gtkx build`. A missing compositor or codegen store fails with\n * a readable error (e.g. `Cannot find the \"sway\" executable on PATH`), not\n * a hang.\n */\nexport const reactNativeGtkxTest = async (\n options: ReactNativeGtkxTestOptions = {},\n): Promise<UserWorkspaceConfig> => {\n const gtkx = (await import(\"@gtkx/vitest\")).default\n const plugins: Plugin[] = [\n gtkx(options.headless),\n reactNativeGtkx(options.platform),\n ]\n return {\n plugins,\n test: {\n name: options.name ?? \"gtk\",\n include: options.include ?? DEFAULT_INCLUDE,\n setupFiles: [SETUP_FILE, ...(options.setupFiles ?? [])],\n server: {\n deps: {\n inline: [...DEFAULT_INLINE_DEPS, ...(options.inlineDeps ?? [])],\n },\n },\n fileParallelism: options.fileParallelism ?? false,\n },\n }\n}\n\nexport default reactNativeGtkxTest\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-gtkx",
|
|
3
|
-
"version": "0.4.0
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "React Native components and APIs for Linux desktop, rendered as native GTK4 widgets via gtkx",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Anton Petrov <anton@itsmepetrov.com>",
|
|
@@ -126,14 +126,14 @@
|
|
|
126
126
|
}
|
|
127
127
|
},
|
|
128
128
|
"dependencies": {
|
|
129
|
-
"@gtkx/cli": "1.
|
|
130
|
-
"@gtkx/codegen": "1.
|
|
131
|
-
"@gtkx/config": "1.
|
|
132
|
-
"@gtkx/css": "1.
|
|
133
|
-
"@gtkx/react": "1.
|
|
134
|
-
"@gtkx/runtime": "1.
|
|
135
|
-
"@gtkx/testing": "1.
|
|
136
|
-
"@gtkx/vitest": "1.
|
|
129
|
+
"@gtkx/cli": "1.2.2",
|
|
130
|
+
"@gtkx/codegen": "1.2.2",
|
|
131
|
+
"@gtkx/config": "1.2.2",
|
|
132
|
+
"@gtkx/css": "1.2.2",
|
|
133
|
+
"@gtkx/react": "1.2.2",
|
|
134
|
+
"@gtkx/runtime": "1.2.2",
|
|
135
|
+
"@gtkx/testing": "1.2.2",
|
|
136
|
+
"@gtkx/vitest": "1.2.2",
|
|
137
137
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
138
138
|
"rolldown": "^1.1.5",
|
|
139
139
|
"yoga-layout": "^3.2.1",
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// RC4-WORKAROUND(use-signal-stale-handler): see docs/gtkx-rc4-notes.md
|
|
2
|
-
// @gtkx/react's useSignal documents that "each emission runs the handler from
|
|
3
|
-
// the latest render", and rc.1 delivered that by pinning the handler in a ref
|
|
4
|
-
// of its own. rc.3 routes it through React's useEffectEvent instead, and
|
|
5
|
-
// react-reconciler@0.33.0 only refreshes useEffectEvent in
|
|
6
|
-
// commitBeforeMutationEffects for `case 0` (FunctionComponent) — `case 11`
|
|
7
|
-
// (ForwardRef) and `case 15` (SimpleMemoComponent) fall through unrefreshed.
|
|
8
|
-
// Any useEffectEvent inside a memo or forwardRef component is pinned to its
|
|
9
|
-
// mount closure permanently; this has nothing to do with useSignal
|
|
10
|
-
// specifically (confirmed upstream: gtkx-org/gtkx#467) — plain
|
|
11
|
-
// useEffectEvent called from an Effect fails identically. It reproduced for
|
|
12
|
-
// us because our ScrollView is a forwardRef (components/scroll-view.tsx) with
|
|
13
|
-
// the useSignal calls inside it; tree depth was a coincidence, not the
|
|
14
|
-
// trigger. The consequence is not subtle — the mount-time scroll handler
|
|
15
|
-
// windows a VirtualizedList against the data it saw at mount, so a list
|
|
16
|
-
// whose rows arrive from a fetch renders EMPTY as soon as it is scrolled.
|
|
17
|
-
//
|
|
18
|
-
// Restore the documented contract at the bridge: keep the latest handler in a
|
|
19
|
-
// ref of our own and hand gtkx a stable wrapper (stable so the connection is
|
|
20
|
-
// never re-established, which is what its useEffectEvent was for). The ref is
|
|
21
|
-
// refreshed in an INSERTION effect — the earliest commit phase, ahead of every
|
|
22
|
-
// layout effect, so a signal emitted from one (the list's own scroll
|
|
23
|
-
// corrections) already sees the handler from the render being committed.
|
|
24
|
-
// React fixed the refresh on the 19.3 line (moved into
|
|
25
|
-
// commitMutationEffectsOnFiber, covering `case 0/11/14/15` ahead of child
|
|
26
|
-
// traversal), but there is no stable 0.34.x gtkx release yet and the React
|
|
27
|
-
// canaries pin an exact prerelease peer, so this wrapper stays until a
|
|
28
|
-
// stable React 19.3 ships — not "when useSignal is fixed" (see gtkx#467).
|
|
29
|
-
import { useSignal as useGtkxSignal } from "@gtkx/react";
|
|
30
|
-
import { useCallback, useInsertionEffect, useRef } from "react";
|
|
31
|
-
export const useSignal = (object, signal, handler, options) => {
|
|
32
|
-
// The handler type is generic in the signal name; the wrapper only forwards
|
|
33
|
-
// arguments, so it crosses that boundary as an untyped callable.
|
|
34
|
-
const latest = useRef(() => undefined);
|
|
35
|
-
useInsertionEffect(() => {
|
|
36
|
-
latest.current = handler;
|
|
37
|
-
});
|
|
38
|
-
const stable = useCallback((...args) => latest.current(...args), []);
|
|
39
|
-
useGtkxSignal(object, signal, stable, options);
|
|
40
|
-
};
|
|
41
|
-
//# sourceMappingURL=use-signal.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-signal.js","sourceRoot":"","sources":["../../../src/gtkx/bridge/use-signal.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,8EAA8E;AAC9E,8EAA8E;AAC9E,yEAAyE;AACzE,2DAA2D;AAC3D,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAC5E,mEAAmE;AACnE,+DAA+D;AAC/D,4EAA4E;AAC5E,8EAA8E;AAC9E,uEAAuE;AACvE,yEAAyE;AACzE,wEAAwE;AACxE,0EAA0E;AAC1E,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,+EAA+E;AAC/E,qEAAqE;AACrE,yEAAyE;AACzE,uDAAuD;AACvD,0EAA0E;AAC1E,2EAA2E;AAC3E,uEAAuE;AACvE,0EAA0E;AAC1E,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAI/D,MAAM,CAAC,MAAM,SAAS,GAAyB,CAC7C,MAAM,EACN,MAAM,EACN,OAAO,EACP,OAAO,EACP,EAAE;IACF,4EAA4E;IAC5E,iEAAiE;IACjE,MAAM,MAAM,GAAG,MAAM,CAAa,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAClD,kBAAkB,CAAC,GAAG,EAAE;QACtB,MAAM,CAAC,OAAO,GAAG,OAAgC,CAAA;IACnD,CAAC,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,WAAW,CACxB,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,EACpC,EAAE,CACH,CAAA;IACD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAmC,EAAE,OAAO,CAAC,CAAA;AAC7E,CAAC,CAAA","sourcesContent":["// RC4-WORKAROUND(use-signal-stale-handler): see docs/gtkx-rc4-notes.md\n// @gtkx/react's useSignal documents that \"each emission runs the handler from\n// the latest render\", and rc.1 delivered that by pinning the handler in a ref\n// of its own. rc.3 routes it through React's useEffectEvent instead, and\n// react-reconciler@0.33.0 only refreshes useEffectEvent in\n// commitBeforeMutationEffects for `case 0` (FunctionComponent) — `case 11`\n// (ForwardRef) and `case 15` (SimpleMemoComponent) fall through unrefreshed.\n// Any useEffectEvent inside a memo or forwardRef component is pinned to its\n// mount closure permanently; this has nothing to do with useSignal\n// specifically (confirmed upstream: gtkx-org/gtkx#467) — plain\n// useEffectEvent called from an Effect fails identically. It reproduced for\n// us because our ScrollView is a forwardRef (components/scroll-view.tsx) with\n// the useSignal calls inside it; tree depth was a coincidence, not the\n// trigger. The consequence is not subtle — the mount-time scroll handler\n// windows a VirtualizedList against the data it saw at mount, so a list\n// whose rows arrive from a fetch renders EMPTY as soon as it is scrolled.\n//\n// Restore the documented contract at the bridge: keep the latest handler in a\n// ref of our own and hand gtkx a stable wrapper (stable so the connection is\n// never re-established, which is what its useEffectEvent was for). The ref is\n// refreshed in an INSERTION effect — the earliest commit phase, ahead of every\n// layout effect, so a signal emitted from one (the list's own scroll\n// corrections) already sees the handler from the render being committed.\n// React fixed the refresh on the 19.3 line (moved into\n// commitMutationEffectsOnFiber, covering `case 0/11/14/15` ahead of child\n// traversal), but there is no stable 0.34.x gtkx release yet and the React\n// canaries pin an exact prerelease peer, so this wrapper stays until a\n// stable React 19.3 ships — not \"when useSignal is fixed\" (see gtkx#467).\nimport { useSignal as useGtkxSignal } from \"@gtkx/react\"\nimport { useCallback, useInsertionEffect, useRef } from \"react\"\n\ntype AnyHandler = (...args: unknown[]) => unknown\n\nexport const useSignal: typeof useGtkxSignal = (\n object,\n signal,\n handler,\n options,\n) => {\n // The handler type is generic in the signal name; the wrapper only forwards\n // arguments, so it crosses that boundary as an untyped callable.\n const latest = useRef<AnyHandler>(() => undefined)\n useInsertionEffect(() => {\n latest.current = handler as unknown as AnyHandler\n })\n const stable = useCallback<AnyHandler>(\n (...args) => latest.current(...args),\n [],\n )\n useGtkxSignal(object, signal, stable as unknown as typeof handler, options)\n}\n"]}
|