create-crossbind 2.0.0-beta.66 → 2.0.0-beta.69

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.
Files changed (35) hide show
  1. package/README.md +1 -0
  2. package/package.json +2 -2
  3. package/src/extension.js +67 -0
  4. package/src/index.js +11 -1
  5. package/src/manifest.json +10 -4
  6. package/templates/app-extension/README.md +33 -0
  7. package/templates/app-extension/_gitignore +1 -0
  8. package/templates/app-extension/crossbind.extension.js +11 -0
  9. package/templates/app-extension/index.d.ts +2 -0
  10. package/templates/app-extension/index.js +8 -0
  11. package/templates/app-extension/native/starter.cpp +12 -0
  12. package/templates/app-extension/package.json +34 -0
  13. package/templates/backend-nodejs-native/README.md +2 -2
  14. package/templates/backend-nodejs-native/package.json +3 -3
  15. package/templates/backend-nodejs-standalone/package.json +1 -1
  16. package/templates/backend-nodejs-wasm/package.json +2 -2
  17. package/templates/cloud-cloudflare-worker/package.json +1 -1
  18. package/templates/desktop-app/README.md +3 -2
  19. package/templates/desktop-app/package.json +4 -4
  20. package/templates/desktop-electron/README.md +2 -2
  21. package/templates/desktop-electron/package.json +4 -4
  22. package/templates/lib-cmake/package.json +1 -1
  23. package/templates/lib-prebuilt/package.json +2 -2
  24. package/templates/lib-source/package.json +1 -1
  25. package/templates/mobile-reactnative-cli/AGENTS.md +0 -28
  26. package/templates/mobile-reactnative-cli/package.json +15 -15
  27. package/templates/mobile-reactnative-expo/package.json +7 -19
  28. package/templates/native-executable/package.json +2 -2
  29. package/templates/web-react-rspack/package.json +3 -3
  30. package/templates/web-react-vite/package.json +2 -2
  31. package/templates/web-svelte-vite/package.json +2 -2
  32. package/templates/web-vanilla/package.json +2 -2
  33. package/templates/web-vue-vite/package.json +2 -2
  34. package/templates/mobile-reactnative-cli/ci/crossbind-snapshot/cache.json +0 -12
  35. package/templates/mobile-reactnative-expo/package-lock.json +0 -8891
package/README.md CHANGED
@@ -54,6 +54,7 @@ npm create crossbind@beta -- my-app Web React Vite
54
54
  npm create crossbind@beta -- my-app Desktop Electron
55
55
  npm create crossbind@beta -- my-tool Native Executable
56
56
  npm create crossbind@beta -- my-lib Library Prebuilt
57
+ npm create crossbind@beta -- my-extension Extension
57
58
  ```
58
59
 
59
60
  When you are ready to ship your app to production, run the following:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-crossbind",
3
- "version": "2.0.0-beta.66",
3
+ "version": "2.0.0-beta.69",
4
4
  "description": "Create crossbind Applications",
5
5
  "homepage": "https://github.com/crossbind/crossbind/tree/main/tooling/create-app#readme",
6
6
  "repository": {
@@ -11,7 +11,7 @@
11
11
  "main": "src/index.js",
12
12
  "type": "module",
13
13
  "engines": {
14
- "node": ">=24"
14
+ "node": ">=26"
15
15
  },
16
16
  "files": [
17
17
  "src",
@@ -0,0 +1,67 @@
1
+ import fsp from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { renameInReadme } from './readme.js';
4
+
5
+ // The extension the app-extension template carries, which a scaffold names after its project: every word starter in
6
+ // the template's text files is its name, so the template uses the word for nothing else.
7
+ const TEMPLATE_NAME = 'starter';
8
+ // An extension's name, as crossbind takes it: lowercase letters and digits, dashes between, from a letter.
9
+ const NAME = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
10
+ const TEXT_FILE = /\.(js|ts|md|cpp|h|mm|java)$/;
11
+ const TEMPLATE_WORD = new RegExp(`crossbind_extension_${TEMPLATE_NAME}\\b|\\b${TEMPLATE_NAME}\\b`, 'g');
12
+ // The words module code keeps, which can name neither the template's default export nor its import in the README.
13
+ const RESERVED = new Set(('arguments await break case catch class const continue debugger default delete do else enum eval '
14
+ + 'export extends false finally for function if implements import in instanceof interface let new null package private '
15
+ + 'protected public return static super switch this throw true try typeof var void while with yield').split(' '));
16
+
17
+ // The extension name of a project's folder, or null when it makes none: the folder's own name in lowercase, with a dash
18
+ // for each run of other characters and without a crossbind-extension- of its own.
19
+ export function extensionName(folder) {
20
+ const name = path.basename(path.resolve(folder)).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
21
+ .replace(/^crossbind-extension(-|$)/, '');
22
+ return NAME.test(name) ? name : null;
23
+ }
24
+
25
+ // The JavaScript name of an extension: camelCase, with Extension after a word module code keeps.
26
+ function identifierOf(name) {
27
+ const camel = name.replace(/-([a-z0-9])/g, (_, letter) => letter.toUpperCase());
28
+ return RESERVED.has(camel) ? `${camel}Extension` : camel;
29
+ }
30
+
31
+ // A template's text with its extension named: the C++ entry with underscores for dashes, the name itself before a dot
32
+ // or in quotes, and its JavaScript name elsewhere. One pass, so a name that holds the template's stays whole.
33
+ export function renamedText(text, name) {
34
+ const identifier = identifierOf(name);
35
+ return text.replace(TEMPLATE_WORD, (word, at) => {
36
+ if (word !== TEMPLATE_NAME) return `crossbind_extension_${name.replaceAll('-', '_')}`;
37
+ const before = text[at - 1];
38
+ const after = text[at + word.length];
39
+ return after === '.' || ((before === "'" || before === '"') && after === before) ? name : identifier;
40
+ });
41
+ }
42
+
43
+ // The text files the template gave the project, and those named after the template's extension; a file of the folder's
44
+ // own is left as it is.
45
+ async function renameFiles(template, dir, name) {
46
+ for (const entry of await fsp.readdir(template, { withFileTypes: true, recursive: true })) {
47
+ if (!entry.isFile() || !TEXT_FILE.test(entry.name)) continue;
48
+ const file = path.join(dir, path.relative(template, entry.parentPath), entry.name);
49
+ await fsp.writeFile(file, renamedText(await fsp.readFile(file, 'utf8'), name));
50
+ if (entry.name.startsWith(`${TEMPLATE_NAME}.`)) {
51
+ await fsp.rename(file, path.join(path.dirname(file), `${name}${entry.name.slice(TEMPLATE_NAME.length)}`));
52
+ }
53
+ }
54
+ }
55
+
56
+ // Names a scaffolded extension after its project: its files, then its package, whose name the README takes last so a
57
+ // name that holds the template's word stays whole.
58
+ export async function nameExtension(template, dir, name) {
59
+ await renameFiles(template, dir, name);
60
+ const pjPath = path.join(dir, 'package.json');
61
+ // private keeps the template's sample out of the repository's releases; an extension is a package to publish.
62
+ const { name: templateName, private: _, ...fields } = JSON.parse(await fsp.readFile(pjPath, 'utf8'));
63
+ const pkg = { name: `crossbind-extension-${name}`, ...fields };
64
+ await fsp.writeFile(pjPath, `${JSON.stringify(pkg, null, 2)}\n`);
65
+ const readmePath = path.join(dir, 'README.md');
66
+ await fsp.writeFile(readmePath, renameInReadme(await fsp.readFile(readmePath, 'utf8'), templateName, pkg.name));
67
+ }
package/src/index.js CHANGED
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
6
6
  import { styleText } from 'node:util';
7
7
  import * as p from '@clack/prompts';
8
8
  import { unpackDotfiles } from './dotfiles.js';
9
+ import { extensionName, nameExtension } from './extension.js';
9
10
  import { renameInReadme } from './readme.js';
10
11
  import { pkgManager } from './utils.js';
11
12
 
@@ -101,6 +102,7 @@ function usage() {
101
102
  ' npm create crossbind@beta -- my-app Web Vanilla',
102
103
  ' npm create crossbind@beta -- my-app Web React Vite',
103
104
  ' npm create crossbind@beta -- my-lib Library Prebuilt',
105
+ ' npm create crossbind@beta -- my-extension Extension',
104
106
  '',
105
107
  'Templates:',
106
108
  ...rows,
@@ -142,6 +144,12 @@ async function main() {
142
144
  await ensureEmpty(cwd);
143
145
 
144
146
  const entry = await pickTemplate(argGroup, argFirst, argSecond);
147
+ // An extension is named after its project, so a project name that makes none stops before a file is written.
148
+ const extension = entry.kind === 'extension' ? extensionName(name) : null;
149
+ if (entry.kind === 'extension' && !extension) {
150
+ p.cancel(`No extension name in ${name}: an extension's name is lowercase letters and digits, dashes between, from a letter.`);
151
+ process.exit(1);
152
+ }
145
153
  const src = path.join(TEMPLATES_DIR, entry.key);
146
154
  if (!fs.existsSync(src)) {
147
155
  p.cancel(`Template missing: ${entry.key} (expected ${src}). Did you publish with prepublishOnly?`);
@@ -156,7 +164,9 @@ async function main() {
156
164
 
157
165
  const pjPath = path.join(cwd, 'package.json');
158
166
  let scripts = {};
159
- if (fs.existsSync(pjPath)) {
167
+ if (extension) {
168
+ await nameExtension(src, cwd, extension);
169
+ } else if (fs.existsSync(pjPath)) {
160
170
  const pkg = JSON.parse(await fsp.readFile(pjPath, 'utf8'));
161
171
  const templateName = pkg.name;
162
172
  pkg.name = entry.kind === 'lib' ? `crossbind-lib-${name}` : name;
package/src/manifest.json CHANGED
@@ -56,8 +56,7 @@
56
56
  "label": "Expo",
57
57
  "kind": "app",
58
58
  "source": "examples/mobile-reactnative-expo",
59
- "nativeFolders": false,
60
- "keepLockfile": true
59
+ "nativeFolders": false
61
60
  },
62
61
  {
63
62
  "key": "desktop-app",
@@ -92,8 +91,7 @@
92
91
  "framework": "Node.js",
93
92
  "label": "Node-API",
94
93
  "kind": "app",
95
- "source": "examples/backend-nodejs-native",
96
- "workspaceOnly": ["ci"]
94
+ "source": "examples/backend-nodejs-native"
97
95
  },
98
96
  {
99
97
  "key": "backend-nodejs-standalone",
@@ -147,5 +145,13 @@
147
145
  "label": "CMake",
148
146
  "kind": "lib",
149
147
  "source": "examples/lib-cmake"
148
+ },
149
+ {
150
+ "key": "app-extension",
151
+ "group": "Extension",
152
+ "framework": null,
153
+ "label": "App",
154
+ "kind": "extension",
155
+ "source": "examples/app-extension"
150
156
  }
151
157
  ]
@@ -0,0 +1,33 @@
1
+ # @crossbind/example-app-extension
2
+
3
+ An app extension: a function of the app's native code, which its page calls on macOS, iOS, Android, Windows and Linux.
4
+ [The contract](https://github.com/crossbind/crossbind/blob/main/docs/api/extensions.md) says what an extension is, and
5
+ [the playbook](https://github.com/crossbind/crossbind/blob/main/docs/playbooks/new-extension.md) the order of the work
6
+ of making this one your own.
7
+
8
+ ## Use
9
+
10
+ Once it is on npm, `npx crossbind add @crossbind/example-app-extension` installs it in an app and lists it in the app's
11
+ `crossbind.config.js`, whose host builds its native code in; until then, `crossbind add` takes the tarball `npm pack`
12
+ makes. The config it writes:
13
+
14
+ ```js
15
+ import starter from '@crossbind/example-app-extension/crossbind.extension.js';
16
+
17
+ export default {
18
+ target: { runtimeEnv: 'app' },
19
+ extensions: [starter()],
20
+ };
21
+ ```
22
+
23
+ ```js
24
+ import { greet } from '@crossbind/example-app-extension';
25
+
26
+ console.log(await greet('crossbind')); // Hello, crossbind, from <the app's name>
27
+ ```
28
+
29
+ ## Files
30
+
31
+ - `crossbind.extension.js`: the extension, named 'starter', and the sources each platform compiles.
32
+ - `native/starter.cpp`: its entry, which adds the functions the page calls as `starter.<function>`.
33
+ - `index.js` and `index.d.ts`: the page's functions, which call the extension's with `invoke` from `crossbind/app`.
@@ -0,0 +1 @@
1
+ node_modules
@@ -0,0 +1,11 @@
1
+ // The extension as an app's crossbind.config.js lists it: extensions: [starter()].
2
+ export default function starter() {
3
+ return {
4
+ name: 'starter',
5
+ contract: 1,
6
+ root: import.meta.dirname,
7
+ app: {
8
+ sources: ['native/starter.cpp'],
9
+ },
10
+ };
11
+ }
@@ -0,0 +1,2 @@
1
+ /** What the app's native code says to the name given. */
2
+ export declare function greet(name: string): Promise<string>;
@@ -0,0 +1,8 @@
1
+ // The extension's functions, from an app's page.
2
+ import { invoke } from 'crossbind/app';
3
+
4
+ // What the app's native code says to the name given.
5
+ export const greet = async (name) => {
6
+ if (typeof name !== 'string') throw new TypeError('crossbind: starter.greet takes a string.');
7
+ return invoke('starter.greet', { name });
8
+ };
@@ -0,0 +1,12 @@
1
+ // The extension's functions, which the page calls through its package, the same on every platform.
2
+ #include "crossbind/extension.h"
3
+
4
+ #include <stdexcept>
5
+
6
+ void crossbind_extension_starter(crossbind::Registry &registry) {
7
+ registry.function("starter.greet", [](crossbind::Call call) {
8
+ const crossbind::Value &name = call.options()["name"];
9
+ if (!name.isString()) throw std::invalid_argument("crossbind: starter.greet takes a name");
10
+ call.resolve("Hello, " + name.asString() + ", from " + crossbind::appName());
11
+ });
12
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@crossbind/example-app-extension",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "description": "crossbind app extension: a function of the app's native code, which its page calls",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "default": "./index.js"
12
+ },
13
+ "./crossbind.extension.js": "./crossbind.extension.js"
14
+ },
15
+ "files": [
16
+ "crossbind.extension.js",
17
+ "index.d.ts",
18
+ "index.js",
19
+ "native"
20
+ ],
21
+ "crossbind": {
22
+ "extension": "./crossbind.extension.js"
23
+ },
24
+ "peerDependencies": {
25
+ "crossbind": "^2.0.0-beta.69"
26
+ },
27
+ "devDependencies": {
28
+ "crossbind": "^2.0.0-beta.69"
29
+ },
30
+ "keywords": [
31
+ "crossbind-extension",
32
+ "app"
33
+ ]
34
+ }
@@ -15,8 +15,8 @@ which loads the addons the same library ships prebuilt.
15
15
  # Getting Started
16
16
 
17
17
  >**Note**: Make sure you have completed the [crossbind - Prerequisites](https://crossbind.dev/docs/guide/getting-started/prerequisites) instructions.
18
- The bindings and the Linux and Windows addons build in Docker; the macOS addons build on a macOS
19
- host with Xcode's command line tools.
18
+ The Linux and Windows addons build in Docker, and so do the bindings but on a Mac, where
19
+ `@crossbind/bridge-tools` generates them; the macOS addons build on a macOS host with Xcode's command line tools.
20
20
 
21
21
  ## Setup
22
22
 
@@ -12,11 +12,11 @@
12
12
  "e2e:prod": "node e2e/run.mjs"
13
13
  },
14
14
  "dependencies": {
15
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66"
15
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69"
16
16
  },
17
17
  "devDependencies": {
18
- "@crossbind/core-embind-napi": "^2.0.0-beta.66",
19
- "crossbind": "^2.0.0-beta.66"
18
+ "@crossbind/core-embind-napi": "^2.0.0-beta.69",
19
+ "crossbind": "^2.0.0-beta.69"
20
20
  },
21
21
  "keywords": [
22
22
  "crossbind-sample",
@@ -11,7 +11,7 @@
11
11
  "e2e:prod": "node e2e/run.mjs"
12
12
  },
13
13
  "dependencies": {
14
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66"
14
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69"
15
15
  },
16
16
  "keywords": [
17
17
  "crossbind-sample",
@@ -10,10 +10,10 @@
10
10
  "build": "crossbind build -p wasm -a wasm32 -r st -e node -b release"
11
11
  },
12
12
  "dependencies": {
13
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66"
13
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69"
14
14
  },
15
15
  "devDependencies": {
16
- "crossbind": "^2.0.0-beta.66"
16
+ "crossbind": "^2.0.0-beta.69"
17
17
  },
18
18
  "keywords": [
19
19
  "crossbind-sample",
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "dependencies": {},
25
25
  "devDependencies": {
26
- "crossbind": "^2.0.0-beta.66",
26
+ "crossbind": "^2.0.0-beta.69",
27
27
  "wrangler": "^4.136.3",
28
28
  "@playwright/test": "^1.63.0"
29
29
  },
@@ -11,8 +11,9 @@ Windows and Android System WebView on Android.
11
11
 
12
12
  >**Note**: Make sure you have completed the [crossbind - Prerequisites](https://crossbind.dev/docs/guide/getting-started/prerequisites) instructions.
13
13
  Apps build on macOS with Xcode's command line tools, iOS apps with Xcode and its iOS platform, and Linux, Windows and
14
- Android apps in crossbind's linux, windows and android-app images, which carry everything they build with; the
15
- bindings are generated in Docker. A macOS or iOS build fetches React Native's Hermes once into `~/.crossbind/hermes`.
14
+ Android apps in crossbind's linux, windows and android-app images, which carry everything they build with. The
15
+ bindings are generated in Docker, but on a Mac `@crossbind/bridge-tools` generates every app's but Android's. A macOS or
16
+ iOS build fetches React Native's Hermes once into `~/.crossbind/hermes`.
16
17
 
17
18
  ## Setup
18
19
 
@@ -19,12 +19,12 @@
19
19
  "e2e:prod": "node e2e/run.mjs"
20
20
  },
21
21
  "dependencies": {
22
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66"
22
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69"
23
23
  },
24
24
  "devDependencies": {
25
- "@crossbind/core-app-host": "^2.0.0-beta.66",
26
- "@crossbind/plugin-vite": "^2.0.0-beta.66",
27
- "crossbind": "^2.0.0-beta.66",
25
+ "@crossbind/core-app-host": "^2.0.0-beta.69",
26
+ "@crossbind/plugin-vite": "^2.0.0-beta.69",
27
+ "crossbind": "^2.0.0-beta.69",
28
28
  "vite": "^8.2.1"
29
29
  },
30
30
  "keywords": [
@@ -11,8 +11,8 @@ version and nothing for `electron-rebuild` to do.
11
11
  # Getting Started
12
12
 
13
13
  >**Note**: Make sure you have completed the [crossbind - Prerequisites](https://crossbind.dev/docs/guide/getting-started/prerequisites) instructions.
14
- The bindings and the Linux and Windows addons build in Docker; the macOS addons build on a macOS
15
- host with Xcode's command line tools.
14
+ The Linux and Windows addons build in Docker, and so do the bindings but on a Mac, where
15
+ `@crossbind/bridge-tools` generates them; the macOS addons build on a macOS host with Xcode's command line tools.
16
16
 
17
17
  ## Setup
18
18
 
@@ -16,11 +16,11 @@
16
16
  "e2e:prod": "electron-builder --dir && playwright test --project=packaged"
17
17
  },
18
18
  "devDependencies": {
19
- "@crossbind/core-embind-napi": "^2.0.0-beta.66",
20
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66",
19
+ "@crossbind/core-embind-napi": "^2.0.0-beta.69",
20
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69",
21
21
  "@playwright/test": "^1.63.0",
22
- "crossbind": "^2.0.0-beta.66",
23
- "electron": "^44.5.1",
22
+ "crossbind": "^2.0.0-beta.69",
23
+ "electron": "^45.0.0",
24
24
  "electron-builder": "^26.15.3"
25
25
  },
26
26
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossbind/example-lib-cmake",
3
- "version": "2.0.0-beta.66",
3
+ "version": "2.0.0-beta.69",
4
4
  "description": "crossbind sample library: simple cmake library",
5
5
  "homepage": "https://github.com/crossbind/crossbind/tree/main/examples/lib-cmake#readme",
6
6
  "repository": "https://github.com/crossbind/crossbind.git",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossbind/example-lib-prebuilt-matrix",
3
- "version": "2.0.0-beta.66",
3
+ "version": "2.0.0-beta.69",
4
4
  "description": "crossbind sample prebuilt library: matrix multiplier",
5
5
  "homepage": "https://github.com/crossbind/crossbind/tree/main/examples/lib-prebuilt-matrix#readme",
6
6
  "repository": "https://github.com/crossbind/crossbind.git",
@@ -18,7 +18,7 @@
18
18
  "prepublishOnly": "crossbind build"
19
19
  },
20
20
  "devDependencies": {
21
- "crossbind": "^2.0.0-beta.66"
21
+ "crossbind": "^2.0.0-beta.69"
22
22
  },
23
23
  "keywords": [
24
24
  "crossbind-sample",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossbind/example-lib-source",
3
- "version": "2.0.0-beta.66",
3
+ "version": "2.0.0-beta.69",
4
4
  "description": "crossbind sample library: simple source library",
5
5
  "homepage": "https://github.com/crossbind/crossbind/tree/main/examples/lib-source#readme",
6
6
  "repository": "https://github.com/crossbind/crossbind.git",
@@ -6,7 +6,6 @@
6
6
 
7
7
  - A minimal RN-cli app that loads `@crossbind/example-lib-prebuilt-matrix` and calls into it from JS.
8
8
  - Reference for `metro.config.js` + `crossbind.config.mjs` shape in a bare RN project.
9
- - Source of the **CI bridge cache** used by `.github/workflows/test-ios-sample.yml` to skip SWIG bridge generation in CI.
10
9
 
11
10
  ## Layout
12
11
 
@@ -14,12 +13,6 @@
14
13
  examples/mobile-reactnative-cli/
15
14
  ├── android/ ← bare RN Android project
16
15
  ├── ios/ ← bare RN iOS project (Podfile + Podfile.lock)
17
- ├── ci/
18
- │ └── crossbind-snapshot/ ← bridge fixtures restored by iOS workflow
19
- │ ├── build/bridge/native.i.cpp
20
- │ ├── build/bridge/native.i.cpp.exports.json
21
- │ ├── build/interface/native.i
22
- │ └── cache.json
23
16
  ├── src/ ← RN app and native bridge input
24
17
  ├── __tests__/ ← Jest component smoke
25
18
  ├── app.json
@@ -33,24 +26,6 @@ examples/mobile-reactnative-cli/
33
26
 
34
27
  - **`metro.config.js`** — exact wiring for `@crossbind/plugin-metro`. Used as the canonical example in `docs/playbooks/integration/react-native-cli.md`.
35
28
  - **`crossbind.config.mjs`** — shows how to consume a workspace dep (`@crossbind/example-lib-prebuilt-matrix`).
36
- - **`ci/crossbind-snapshot/`** — git-tracked fixtures. **Do not delete.** The iOS workflow's `cp -r ci/crossbind-snapshot/. .crossbind/` step depends on them.
37
-
38
- ## CI bridge cache pattern
39
-
40
- The directory is named `crossbind-snapshot/` (not `.crossbind/`) on purpose: `pnpm clear:cache:examples` and similar globs match `*/.crossbind`, which would otherwise wipe these fixtures. Renaming the snapshot dir survives clear globs.
41
-
42
- The iOS workflow restores the snapshot **before** `pod install` so SWIG / bridge generation can be skipped in CI:
43
-
44
- ```yaml
45
- - name: Restore cached bridge files
46
- run: |
47
- mkdir -p ./examples/.../my-app/.crossbind
48
- cp -r ./examples/.../my-app/ci/crossbind-snapshot/. ./examples/.../my-app/.crossbind/
49
- ```
50
-
51
- The trailing `/.` + `mkdir -p` keeps the merge nesting-safe regardless of pre-existing state in the runner.
52
-
53
- If you regenerate the snapshot, re-commit the four files under `ci/crossbind-snapshot/build/...` and `ci/crossbind-snapshot/cache.json`.
54
29
 
55
30
  ## Validation
56
31
 
@@ -76,8 +51,6 @@ CI: `.github/workflows/test-ios-sample.yml` and `test-android-sample.yml`.
76
51
 
77
52
  ## Common pitfalls
78
53
 
79
- - **Deleting `ci/crossbind-snapshot/`** with a careless `rm -rf` or assuming it's regenerable — it isn't (well, it is, but only after a full local build that we skip in CI).
80
- - **Renaming back to `ci/.crossbind`.** `pnpm run clear`-style globs will eat it.
81
54
  - **Skipping `pod install`** after touching the RN plugin's `react-native-crossbind.podspec`. Cached pods will be wrong.
82
55
  - **Editing the `monorepo` `watchFolders` config** in `metro.config.js` to point elsewhere. The default `require('path').resolve('../../')` is intentional — Metro must see the workspace root to resolve the crossbind plugin packages.
83
56
  - **Adding `ios/Pods/` or `android/.gradle/` to git.** They're ignored; build artifacts.
@@ -87,4 +60,3 @@ CI: `.github/workflows/test-ios-sample.yml` and `test-android-sample.yml`.
87
60
  - RN-cli playbook: `docs/playbooks/integration/react-native-cli.md`
88
61
  - Plugin source: `plugins/react-native/AGENTS.md`
89
62
  - iOS CI workflow: `.github/workflows/test-ios-sample.yml`
90
- - Snapshot restoration logic in CI: see "Restore cached bridge files" step
@@ -20,14 +20,14 @@
20
20
  "e2e:android": "pnpm run run:android && pnpm run maestro:android"
21
21
  },
22
22
  "dependencies": {
23
- "@crossbind/core-embind-jsi": "^2.0.0-beta.66",
24
- "@crossbind/plugin-react-native": "^2.0.0-beta.66",
25
- "@crossbind/plugin-react-native-ios-helper": "^2.0.0-beta.66",
26
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66",
23
+ "@crossbind/core-embind-jsi": "^2.0.0-beta.69",
24
+ "@crossbind/plugin-react-native": "^2.0.0-beta.69",
25
+ "@crossbind/plugin-react-native-ios-helper": "^2.0.0-beta.69",
26
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69",
27
27
  "react": "19.3.1",
28
- "react-native": "0.88.0",
28
+ "react-native": "0.88.1",
29
29
  "react-native-safe-area-context": "^5.8.0",
30
- "hermes-compiler": "250829098.0.17"
30
+ "hermes-compiler": "260318099.0.4"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@babel/core": "^7.29.7",
@@ -36,13 +36,13 @@
36
36
  "@react-native-community/cli": "20.2.0",
37
37
  "@react-native-community/cli-platform-android": "20.2.0",
38
38
  "@react-native-community/cli-platform-ios": "20.2.0",
39
- "@react-native/babel-preset": "0.88.0",
40
- "@react-native/codegen": "0.88.0",
41
- "@react-native/eslint-config": "0.88.0",
42
- "@react-native/jest-preset": "0.88.0",
43
- "@react-native/gradle-plugin": "0.88.0",
44
- "@react-native/metro-config": "0.88.0",
45
- "@react-native/typescript-config": "0.88.0",
39
+ "@react-native/babel-preset": "0.88.1",
40
+ "@react-native/codegen": "0.88.1",
41
+ "@react-native/eslint-config": "0.88.1",
42
+ "@react-native/jest-preset": "0.88.1",
43
+ "@react-native/gradle-plugin": "0.88.1",
44
+ "@react-native/metro-config": "0.88.1",
45
+ "@react-native/typescript-config": "0.88.1",
46
46
  "@types/jest": "^29.5.13",
47
47
  "@types/react": "^19.3.0",
48
48
  "@types/react-test-renderer": "^19.3.0",
@@ -51,10 +51,10 @@
51
51
  "prettier": "3.9.10",
52
52
  "react-test-renderer": "19.3.1",
53
53
  "typescript": "^6.0.3",
54
- "@crossbind/plugin-metro": "^2.0.0-beta.66"
54
+ "@crossbind/plugin-metro": "^2.0.0-beta.69"
55
55
  },
56
56
  "engines": {
57
- "node": ">=24"
57
+ "node": ">=26"
58
58
  },
59
59
  "keywords": [
60
60
  "crossbind-sample",
@@ -23,10 +23,10 @@
23
23
  "e2e:android": "npm run run:android && npm run maestro:android"
24
24
  },
25
25
  "dependencies": {
26
- "@crossbind/core-embind-jsi": "2.0.0-beta.66",
27
- "@crossbind/plugin-react-native": "2.0.0-beta.66",
28
- "@crossbind/plugin-react-native-ios-helper": "2.0.0-beta.66",
29
- "@crossbind/example-lib-prebuilt-matrix": "2.0.0-beta.66",
26
+ "@crossbind/core-embind-jsi": "^2.0.0-beta.69",
27
+ "@crossbind/plugin-react-native": "^2.0.0-beta.69",
28
+ "@crossbind/plugin-react-native-ios-helper": "^2.0.0-beta.69",
29
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69",
30
30
  "hermes-compiler": "260318099.0.4",
31
31
  "expo": "~58.0.14",
32
32
  "expo-constants": "~58.0.12",
@@ -54,21 +54,9 @@
54
54
  "devDependencies": {
55
55
  "@types/react": "~19.3.0",
56
56
  "typescript": "~6.0.3",
57
- "@crossbind/typescript-config": "2.0.0-beta.66",
58
- "@crossbind/plugin-metro": "2.0.0-beta.66"
59
- },
60
- "overrides": {
61
- "uuid": "11.1.1",
62
- "replace": {
63
- "minimatch": "3.1.4"
64
- },
65
- "brace-expansion@5": "^5.0.12",
66
- "shell-quote": "^1.12.0",
67
- "js-yaml@4": "^4.3.1",
68
- "nanoid@3": "^3.3.18",
69
- "postcss": "^8.5.26",
70
- "ws@7": "^7.5.13",
71
- "@babel/core@7": "^7.29.7"
57
+ "metro-config": "^0.87.1",
58
+ "@crossbind/typescript-config": "^2.0.0-beta.69",
59
+ "@crossbind/plugin-metro": "^2.0.0-beta.69"
72
60
  },
73
61
  "keywords": [
74
62
  "crossbind-sample",
@@ -12,10 +12,10 @@
12
12
  "e2e:prod": "node e2e/run.mjs"
13
13
  },
14
14
  "dependencies": {
15
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66"
15
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69"
16
16
  },
17
17
  "devDependencies": {
18
- "crossbind": "^2.0.0-beta.66"
18
+ "crossbind": "^2.0.0-beta.69"
19
19
  },
20
20
  "keywords": [
21
21
  "crossbind-sample",
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "react": "^19.3.0",
18
18
  "react-dom": "^19.3.0",
19
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.66"
19
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.69"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@rspack/plugin-react-refresh": "^2.0.2",
@@ -29,8 +29,8 @@
29
29
  "react-refresh": "^0.19.0",
30
30
  "@playwright/test": "^1.63.0",
31
31
  "serve": "^14.2.6",
32
- "@crossbind/plugin-webpack": "^2.0.0-beta.66",
33
- "@crossbind/plugin-webpack-loader": "^2.0.0-beta.66"
32
+ "@crossbind/plugin-webpack": "^2.0.0-beta.69",
33
+ "@crossbind/plugin-webpack-loader": "^2.0.0-beta.69"
34
34
  },
35
35
  "keywords": [
36
36
  "crossbind-sample",