diff --git a/bun.lock b/bun.lock index b8b0a5f7a9..37fdf2a39e 100644 --- a/bun.lock +++ b/bun.lock @@ -147,6 +147,9 @@ "react-native-nitro-modules": "*", "react-native-nitro-test-external": "*", }, + "optionalPeers": [ + "react-native-nitro-modules", + ], }, "packages/react-native-nitro-test-external": { "name": "react-native-nitro-test-external", @@ -170,6 +173,9 @@ "react-native": "*", "react-native-nitro-modules": "*", }, + "optionalPeers": [ + "react-native-nitro-modules", + ], }, }, "packages": { diff --git a/docs/docs/getting-started/how-to-build-a-nitro-module.md b/docs/docs/getting-started/how-to-build-a-nitro-module.md index d9375f91b8..c7ab664de5 100644 --- a/docs/docs/getting-started/how-to-build-a-nitro-module.md +++ b/docs/docs/getting-started/how-to-build-a-nitro-module.md @@ -44,6 +44,25 @@ First, you need to create a [Nitro Module](../concepts/nitro-modules) - either b npm install nitrogen --save-dev ``` + Also declare `react-native-nitro-modules` as an **optional peer dependency**, so the app using your library is the one that decides which Nitro version gets installed: + + ```json title="package.json" + { + "peerDependencies": { + "react-native-nitro-modules": "*" + }, + "peerDependenciesMeta": { + "react-native-nitro-modules": { + "optional": true + } + } + } + ``` + + :::warning + The `optional` flag matters. Without it, package managers try to satisfy the peer range themselves - and since semver ranges never match pre-releases, `"*"` does **not** match a version like `0.37.0-beta.0`. A user testing a Nitro beta would then get a second, stable copy of `react-native-nitro-modules` installed next to yours, and Nitro throws [`Nitro was installed twice`](../guides/troubleshooting#nitro-was-installed-twice) at runtime. Marking the peer optional stops the package manager from installing Nitro on your behalf. + ::: + Then, you need to decide if you want to use Nitro's C++ library directly, or use [nitrogen](../concepts/nitrogen) to generate specs: diff --git a/docs/docs/guides/troubleshooting.md b/docs/docs/guides/troubleshooting.md index d2d5bc2af7..b470f6faca 100644 --- a/docs/docs/guides/troubleshooting.md +++ b/docs/docs/guides/troubleshooting.md @@ -66,6 +66,36 @@ If your app fails to build after installing Nitro or a library powered by Nitro, +## Nitro was installed twice + +If your app crashes at startup with: + +``` +Nitro was installed twice: once with native version X and once with JS version Y. +``` + +...then two different copies of `react-native-nitro-modules` ended up in your project - the native build linked one, and Metro bundled the other. + +The most common cause is **installing a Nitro pre-release** (e.g. `0.37.0-beta.0`). Semver ranges never match pre-release versions, so a library declaring `"react-native-nitro-modules": "*"` as a required peer dependency does not consider the beta a match - and your package manager silently installs a second, stable copy inside that library's `node_modules` to satisfy the peer. + +To fix it: + +1. Check how many copies you have: + ```sh + npm ls react-native-nitro-modules + ``` +2. If a library nests its own copy, ask the library author to mark the peer dependency as [optional](../getting-started/how-to-build-a-nitro-module#11-install-nitro-and-nitrogen). +3. As a workaround until then, force a single version from your app's `package.json`: + ```json title="package.json" + { + "overrides": { + "react-native-nitro-modules": "0.37.0-beta.0" + } + } + ``` + (use `resolutions` instead of `overrides` for Yarn) +4. Delete `node_modules` and reinstall - package managers often leave the stale nested copy on disk even after the lockfile is fixed. + ## Runtime error If your app crashes at runtime, make sure to inspect the native logs. diff --git a/packages/react-native-nitro-test-external/package.json b/packages/react-native-nitro-test-external/package.json index 6d729977b3..6fb1768c2f 100644 --- a/packages/react-native-nitro-test-external/package.json +++ b/packages/react-native-nitro-test-external/package.json @@ -71,6 +71,11 @@ "react-native": "*", "react-native-nitro-modules": "*" }, + "peerDependenciesMeta": { + "react-native-nitro-modules": { + "optional": true + } + }, "eslintConfig": { "root": true, "extends": [ diff --git a/packages/react-native-nitro-test/package.json b/packages/react-native-nitro-test/package.json index 59c290b19f..a48b9db8e9 100644 --- a/packages/react-native-nitro-test/package.json +++ b/packages/react-native-nitro-test/package.json @@ -72,6 +72,11 @@ "react-native-nitro-modules": "*", "react-native-nitro-test-external": "*" }, + "peerDependenciesMeta": { + "react-native-nitro-modules": { + "optional": true + } + }, "jest": { "preset": "@react-native/jest-preset", "modulePathIgnorePatterns": [ diff --git a/packages/template/package.json b/packages/template/package.json index 5a8c98ec8c..96658f7523 100644 --- a/packages/template/package.json +++ b/packages/template/package.json @@ -71,6 +71,11 @@ "react-native": "*", "react-native-nitro-modules": "*" }, + "peerDependenciesMeta": { + "react-native-nitro-modules": { + "optional": true + } + }, "eslintConfig": { "root": true, "extends": [