diff --git a/docs/docs/01-fundamentals/01-getting-started.md b/docs/docs/01-fundamentals/01-getting-started.md index bfc9a94e42..9a93c66385 100644 --- a/docs/docs/01-fundamentals/01-getting-started.md +++ b/docs/docs/01-fundamentals/01-getting-started.md @@ -84,13 +84,30 @@ pnpm add react-native-executorch react-native-worklets react-native-blob-util React Native ExecuTorch requires: - **New Architecture** enabled -- **React Native 0.81+** or **Expo SDK 54+** with [Development Builds](https://docs.expo.dev/develop/development-builds/introduction/) (**Expo Go is not supported** due to custom C++ native libraries) +- **React Native 0.83+** or **Expo SDK 55+** with [Development Builds](https://docs.expo.dev/develop/development-builds/introduction/) (**Expo Go is not supported** due to custom C++ native libraries) +- **`react-native-worklets` 0.10 or newer** (`>=0.10.0 <0.13.0`) - **iOS 17.0+** / **Android 13+** For supported React Native versions, see the [Compatibility table](../05-other/01-compatibility.mdx). ::: +:::caution Expo SDK 55 and 56 +Both bundle a `react-native-worklets` older than 0.10 — 0.7.4 on SDK 55, 0.8.3 +on SDK 56 — and `npx expo install` will pick that one. Install the version this +library needs instead: + +```bash +npm install react-native-worklets@^0.10.0 +``` + +Any other package in your app that uses worklets has to be moved to a release +built against the same version; two of them asking for different worklets gives +you two incompatible native runtimes. Our own example apps reconcile them this +way. **Expo SDK 54 cannot be supported**: it is React Native 0.81, below what +worklets 0.10 accepts. +::: + ### Selecting native libraries The native binaries — the ExecuTorch hardware backends (XNNPACK, Core ML, MLX, diff --git a/docs/docs/05-other/01-compatibility.mdx b/docs/docs/05-other/01-compatibility.mdx index 31c84ddd62..e99fdbb98a 100644 --- a/docs/docs/05-other/01-compatibility.mdx +++ b/docs/docs/05-other/01-compatibility.mdx @@ -17,7 +17,7 @@ React Native ExecuTorch supports only the [New Architecture](https://reactnative React Native ExecuTorch - React Native version + React Native version 0.78 @@ -28,6 +28,7 @@ React Native ExecuTorch supports only the [New Architecture](https://reactnative 0.83 0.84 0.85 + 0.86 @@ -41,6 +42,7 @@ React Native ExecuTorch supports only the [New Architecture](https://reactnative
yes
yes
yes
+
untested†
0.9.x
@@ -52,18 +54,94 @@ React Native ExecuTorch supports only the [New Architecture](https://reactnative
yes
yes
yes
+
untested†
0.10.x
no
no
no
+
no*
+
no*
yes
yes
yes
yes
+ + + + + +
+ +**\*** `react-native-executorch` 0.10 needs `react-native-worklets` +`>=0.10.0 <0.13.0`, and worklets 0.10 is the first release to serialize an +`ArrayBufferView` natively ([reanimated +#9475](https://github.com/software-mansion/react-native-reanimated/pull/9475)); +older ones rebuild the view over its whole backing buffer, losing `byteOffset` +and `length`. Worklets 0.10 in turn requires React Native 0.83+, which is what +rules out 0.81 and 0.82. + +**†** Not verified. 0.8.x and 0.9.x were released before React Native 0.86 and +are maintained on the `legacy` dist-tag only. + +
+ +## Expo SDK + +`npx expo install` picks the `react-native-worklets` an SDK bundles, which is +older than this library needs on SDK 55 and 56. Both work once you ask for the +versions below explicitly — Reanimated pins worklets exactly, so it moves with +it. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Expo SDKReact NativeBundled workletsreact-native-executorch 0.10.x
54
0.810.5.1
no
55
0.830.7.4
needs explicit versions
56
0.850.8.3
needs explicit versions
57
0.860.10.1
yes
+ +
+ +On SDK 55 and 56, install both: + +```bash +npm install react-native-worklets@^0.10.0 react-native-reanimated@^4.5.0 +``` + +SDK 54 is React Native 0.81, below what worklets 0.10 accepts, so no +combination of versions works there. + +
diff --git a/docs/src/components/CompatibilityTable/styles.module.css b/docs/src/components/CompatibilityTable/styles.module.css index a54f35849d..becc300f75 100644 --- a/docs/src/components/CompatibilityTable/styles.module.css +++ b/docs/src/components/CompatibilityTable/styles.module.css @@ -56,3 +56,22 @@ background-color: var(--swm-red-dark-140); color: var(--swm-red-dark-80); } + +.partial { + background-color: var(--swm-yellow-light-40); + color: var(--swm-yellow-dark-120); + border-radius: 4px; + padding: 4px 8px; + font-weight: 600; + font-size: 0.85rem; +} + +[data-theme='dark'] .partial { + background-color: var(--swm-yellow-dark-140); + color: var(--swm-yellow-dark-80); +} + +.footnote { + margin-top: 0.75rem; + font-size: 0.85rem; +} diff --git a/packages/react-native-executorch/__tests__/api/workletsVersionRange.test.ts b/packages/react-native-executorch/__tests__/api/workletsVersionRange.test.ts new file mode 100644 index 0000000000..56187da90d --- /dev/null +++ b/packages/react-native-executorch/__tests__/api/workletsVersionRange.test.ts @@ -0,0 +1,89 @@ +/** + * The supported `react-native-worklets` range, and the documentation of it. + * + * This range is not a formality. Worklets 0.10.0 is the first release to + * serialize an `ArrayBufferView` natively ([reanimated + * #9475](https://github.com/software-mansion/react-native-reanimated/pull/9475)); + * before it, a view was rebuilt on the target runtime over its whole backing + * buffer, so a window into a larger one — a slice of a recording, a pooled + * camera frame — reached the model silently widened. Measured on a simulator, + * `new Uint8Array(buffer, 64, 16)` arrives with `length` 1024 on worklets 0.8.3 + * and 16 on 0.10.4. + * + * Worklets 0.10 in turn requires React Native 0.83+, so the peer range is also + * what decides which React Native versions this library supports. That is + * exactly the pair that drifted before: the requirements page claimed React + * Native 0.81+ while the peer ruled it out, and nothing noticed. + * + * So the range is checked against three things at once: the version actually + * installed here, the React Native floor it implies, and the number written in + * the docs. + */ +import { readFileSync } from 'fs'; +import { join } from 'path'; + +import { satisfies, minVersion, subset } from 'semver'; + +const PACKAGE_ROOT = join(__dirname, '..', '..'); +const DOCS = join(PACKAGE_ROOT, '..', '..', 'docs', 'docs'); + +const manifest = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf8')); +const range: string = manifest.peerDependencies['react-native-worklets']; + +/** The worklets copy this repo installs, i.e. what the suites run against. */ +const installed = JSON.parse( + readFileSync( + require.resolve('react-native-worklets/package.json', { + paths: [PACKAGE_ROOT], + }), + 'utf8' + ) +); + +describe('react-native-worklets peer range', () => { + it('starts at 0.10.0, where native ArrayBufferView serialization landed', () => { + expect(satisfies('0.9.3', range)).toBe(false); + expect(satisfies('0.10.0', range)).toBe(true); + }); + + it('covers the releases published since', () => { + // A range that stops below the current release leaves anyone installing + // `react-native-worklets` with an unmet peer warning for no reason. + for (const version of ['0.10.4', '0.11.4', '0.12.2']) { + expect(satisfies(version, range)).toBe(true); + } + }); + + it('is bounded, so an unreleased major cannot be assumed to work', () => { + expect(subset(range, '>=0.10.0 <0.13.0')).toBe(true); + }); + + it('is satisfied by the version installed in this repo', () => { + expect(satisfies(installed.version, range)).toBe(true); + }); + + it('matches the React Native floor the docs promise', () => { + // Worklets declares which React Native versions it accepts; ours cannot be + // lower than what the oldest worklets we allow will run on. + const oldest = minVersion(range)!.version; + const workletsRnRange: string = installed.peerDependencies['react-native']; + const rnFloor = minVersion(workletsRnRange)!.version; + + const gettingStarted = readFileSync( + join(DOCS, '01-fundamentals', '01-getting-started.md'), + 'utf8' + ); + + expect(oldest).toBe('0.10.0'); + // e.g. "React Native 0.83+" + expect(gettingStarted).toContain(`React Native ${rnFloor.split('.').slice(0, 2).join('.')}+`); + }); + + it('is written into the requirements the same way', () => { + const gettingStarted = readFileSync( + join(DOCS, '01-fundamentals', '01-getting-started.md'), + 'utf8' + ); + expect(gettingStarted).toContain(range); + }); +}); diff --git a/packages/react-native-executorch/package.json b/packages/react-native-executorch/package.json index f7ef58035f..6d9e4852d4 100644 --- a/packages/react-native-executorch/package.json +++ b/packages/react-native-executorch/package.json @@ -142,7 +142,7 @@ "react": "*", "react-native": "*", "react-native-blob-util": "^0.24.0", - "react-native-worklets": "^0.10.0" + "react-native-worklets": ">=0.10.0 <0.13.0" }, "peerDependenciesMeta": { "@kesha-antonov/react-native-background-downloader": { diff --git a/yarn.lock b/yarn.lock index e40dc3592e..9ce1d3e437 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16506,7 +16506,7 @@ __metadata: react: "*" react-native: "*" react-native-blob-util: ^0.24.0 - react-native-worklets: ^0.10.0 + react-native-worklets: ">=0.10.0 <0.13.0" peerDependenciesMeta: "@kesha-antonov/react-native-background-downloader": optional: true