Skip to content

Commit f014b5e

Browse files
committed
chore: migrate from npm workspaces to pnpm
Motivation: pnpm's recursive runner (`pnpm -r run`) is fail-fast by default and runs in topological (dependency-graph) order, so the root `bootstrap` and `prerelease` no longer need the non-fail-fast `npm run <s> --workspaces` pattern that buried the real root cause under cascading failures. Workspace + package manager: - Replace the root `workspaces` array with pnpm-workspace.yaml - Add `packageManager: pnpm@10.33.0` and switch devEngines to pnpm ^10 - Allow only esbuild's build script via onlyBuiltDependencies (pnpm 10 blocks dependency lifecycle scripts by default); no shamefully-hoist needed - Replace package-lock.json with pnpm-lock.yaml; keep node_modules isolated Internal deps -> workspace:* protocol (cmake-rn, ferric, gyp-to-cmake, host, node-addon-examples, node-tests, ferric-example, test-app). Add explicit `weak-node-api` edges to node-addon-examples and node-tests so the topological bootstrap sequences weak-node-api (which builds the xcframework/.so they link) before its consumers. Phantom dependencies surfaced by pnpm's isolated node_modules (npm hoisting had masked these): - host: add `@types/babel__core` (used by src/node/babel-plugin/plugin.ts) - host: add `weak-node-api` as a devDependency (used by scripts/generate-injector.mts; previously only a peerDependency) Scripts: - bootstrap: `tsc --build && pnpm -r run bootstrap` (fail-fast, topological) - prerelease/release: make the build explicit instead of relying on npm's implicit prerelease hook (pnpm disables pre/post scripts by default) - test: `pnpm --filter ... run test` - depcheck/run-in-published: replace `npm query .workspace` with `pnpm ls -r` - Pin prettier to 3.6.2: 3.7+ is incompatible with @prettier/plugin-oxc@0.0.4 (regenerating any lockfile floated it to 3.9.5 and crashed the plugin) CI: port check.yml and release.yml to pnpm (pnpm/action-setup, cache: pnpm, `pnpm install --frozen-lockfile`, `--filter`, `pnpm exec`). The ephemeral, non-workspace macOS test app keeps its own `npm install` in scripts/init-macos-test-app.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnwodAoNbqPec77191HVXn
1 parent 1dee1a0 commit f014b5e

18 files changed

Lines changed: 10545 additions & 15552 deletions

File tree

.github/copilot-instructions.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ See the [README.md](../README.md#packages) for detailed descriptions of each pac
3333
### Development Setup
3434

3535
```bash
36-
npm ci && npm run build # Install deps and build all packages
37-
npm run bootstrap # Build native components (weak-node-api, examples)
36+
pnpm install && pnpm run build # Install deps and build all packages
37+
pnpm run bootstrap # Build native components (weak-node-api, examples)
3838
```
3939

4040
### Package Development
4141

4242
- **TypeScript project references**: Use `tsc --build` for incremental compilation
43-
- **Workspace scripts**: Most build/test commands use npm workspaces (`--workspace` flag)
43+
- **Workspace scripts**: Most build/test commands use pnpm workspaces (`--filter` flag), run in topological (dependency) order and fail fast
4444
- **Focus on Node.js packages**: AI development primarily targets the Node.js tooling packages rather than native mobile code
4545
- **No TypeScript type asserts**: You have to ask explicitly and justify if you want to add `as` type assertions.
4646

@@ -70,8 +70,8 @@ Library names use double-dash separation: `package-name--path-component--addon-n
7070

7171
### Testing
7272

73-
- **Individual packages**: Some packages have VS Code test tasks and others have their own `npm test` scripts for focused iteration (e.g., `npm test --workspace cmake-rn`). Use the latter only if the former is missing.
74-
- **Cross-package**: Use root-level `npm test` for cross-package testing once individual package tests pass
73+
- **Individual packages**: Some packages have VS Code test tasks and others have their own test scripts for focused iteration (e.g., `pnpm --filter cmake-rn run test`). Use the latter only if the former is missing.
74+
- **Cross-package**: Use root-level `pnpm test` for cross-package testing once individual package tests pass
7575
- **Mobile integration**: Available but not the primary AI development focus - ask the developer to run those tests as needed
7676

7777
**Documentation**: Integration details, platform setup, and toolchain configuration are covered in existing repo documentation files.

.github/workflows/check.yml

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ jobs:
2424
runs-on: ubuntu-latest
2525
steps:
2626
- uses: actions/checkout@v4
27+
- uses: pnpm/action-setup@v4
2728
- uses: actions/setup-node@v6
2829
with:
2930
node-version: lts/krypton
31+
cache: pnpm
3032
- name: Setup cpp tools
3133
uses: aminya/setup-cpp@v1
3234
with:
@@ -47,18 +49,18 @@ jobs:
4749
with:
4850
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
4951
- run: rustup target add x86_64-linux-android
50-
- run: npm ci
51-
- run: npm run build
52+
- run: pnpm install --frozen-lockfile
53+
- run: pnpm run build
5254
# Bootstrap weak-node-api and ferric-example to get types
5355
# TODO: Solve this by adding an option to ferric to build only types or by committing the types into the repo as a fixture for an "init" command
54-
- run: npm run bootstrap --workspace weak-node-api
55-
- run: npm run bootstrap --workspace @react-native-node-api/ferric-example
56-
- run: npm run lint
56+
- run: pnpm --filter weak-node-api run bootstrap
57+
- run: pnpm --filter @react-native-node-api/ferric-example run bootstrap
58+
- run: pnpm run lint
5759
env:
5860
DEBUG: eslint:eslint
59-
- run: npm run prettier:check
60-
- run: npm run depcheck
61-
- run: npm run publint
61+
- run: pnpm run prettier:check
62+
- run: pnpm run depcheck
63+
- run: pnpm run publint
6264
unit-tests:
6365
strategy:
6466
fail-fast: false
@@ -71,9 +73,11 @@ jobs:
7173
name: Unit tests (${{ matrix.runner }})
7274
steps:
7375
- uses: actions/checkout@v4
76+
- uses: pnpm/action-setup@v4
7477
- uses: actions/setup-node@v6
7578
with:
7679
node-version: lts/krypton
80+
cache: pnpm
7781
- name: Setup cpp tools
7882
uses: aminya/setup-cpp@v1
7983
with:
@@ -92,9 +96,15 @@ jobs:
9296
with:
9397
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
9498
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
95-
- run: npm ci
96-
- run: npm run bootstrap
97-
- run: npm test
99+
# macOS runners ship a newer CMake than the project supports; pin it to match the version used elsewhere in CI
100+
- name: Install compatible CMake version
101+
if: runner.os == 'macOS'
102+
uses: jwlawson/actions-setup-cmake@v2
103+
with:
104+
cmake-version: ${{ env.CMAKE_VERSION }}
105+
- run: pnpm install --frozen-lockfile
106+
- run: pnpm run bootstrap
107+
- run: pnpm test
98108
weak-node-api-tests:
99109
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'weak-node-api')
100110
strategy:
@@ -108,9 +118,11 @@ jobs:
108118
name: Weak Node-API tests (${{ matrix.runner }})
109119
steps:
110120
- uses: actions/checkout@v4
121+
- uses: pnpm/action-setup@v4
111122
- uses: actions/setup-node@v6
112123
with:
113124
node-version: lts/krypton
125+
cache: pnpm
114126
- name: Setup cpp tools
115127
uses: aminya/setup-cpp@v1
116128
with:
@@ -119,10 +131,16 @@ jobs:
119131
uses: hendrikmuhs/ccache-action@v1.2
120132
with:
121133
key: ${{ github.job }}-${{ runner.os }}
122-
- run: npm ci
123-
- run: npm run build
134+
# macOS runners ship a newer CMake than the project supports; pin it to match the version used elsewhere in CI
135+
- name: Install compatible CMake version
136+
if: runner.os == 'macOS'
137+
uses: jwlawson/actions-setup-cmake@v2
138+
with:
139+
cmake-version: ${{ env.CMAKE_VERSION }}
140+
- run: pnpm install --frozen-lockfile
141+
- run: pnpm run build
124142
- name: Prepare weak-node-api
125-
run: npm run prebuild:prepare --workspace weak-node-api
143+
run: pnpm --filter weak-node-api run prebuild:prepare
126144
- name: Build and run weak-node-api C++ tests
127145
run: |
128146
cmake -S . -B build -DBUILD_TESTS=ON
@@ -135,9 +153,11 @@ jobs:
135153
runs-on: macos-latest
136154
steps:
137155
- uses: actions/checkout@v4
156+
- uses: pnpm/action-setup@v4
138157
- uses: actions/setup-node@v6
139158
with:
140159
node-version: lts/krypton
160+
cache: pnpm
141161
- name: Setup cpp tools
142162
uses: aminya/setup-cpp@v1
143163
with:
@@ -156,17 +176,22 @@ jobs:
156176
with:
157177
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
158178
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
159-
- run: npm ci
160-
- run: npm run bootstrap
179+
# macOS runners ship a newer CMake than the project supports; pin it to match the version used elsewhere in CI
180+
- name: Install compatible CMake version
181+
uses: jwlawson/actions-setup-cmake@v2
182+
with:
183+
cmake-version: ${{ env.CMAKE_VERSION }}
184+
- run: pnpm install --frozen-lockfile
185+
- run: pnpm run bootstrap
161186
env:
162187
CMAKE_RN_TRIPLETS: arm64;x86_64-apple-ios-sim
163188
FERRIC_TARGETS: aarch64-apple-ios-sim
164-
- run: npm run pod-install
189+
- run: pnpm run pod-install
165190
working-directory: apps/test-app
166191
- name: Run tests (iOS)
167-
run: npm run test:ios:allTests
192+
run: pnpm run test:ios:allTests
168193
# TODO: Enable release mode when it works
169-
# run: npm run test:ios:allTests -- --mode Release
194+
# run: pnpm run test:ios:allTests -- --mode Release
170195
working-directory: apps/test-app
171196
test-macos:
172197
# Disabling this on main for now, as initializing the template takes a long time and
@@ -176,9 +201,11 @@ jobs:
176201
runs-on: macos-latest
177202
steps:
178203
- uses: actions/checkout@v4
204+
- uses: pnpm/action-setup@v4
179205
- uses: actions/setup-node@v6
180206
with:
181207
node-version: lts/krypton
208+
cache: pnpm
182209
- name: Setup cpp tools
183210
uses: aminya/setup-cpp@v1
184211
with:
@@ -192,13 +219,17 @@ jobs:
192219
with:
193220
java-version: "17"
194221
distribution: "temurin"
222+
- name: Install compatible CMake version
223+
uses: jwlawson/actions-setup-cmake@v2
224+
with:
225+
cmake-version: ${{ env.CMAKE_VERSION }}
195226
- run: rustup target add x86_64-apple-darwin
196-
- run: npm ci
197-
- run: npm run bootstrap
227+
- run: pnpm install --frozen-lockfile
228+
- run: pnpm run bootstrap
198229
env:
199230
CMAKE_RN_TRIPLETS: arm64;x86_64-apple-darwin
200231
FERRIC_TARGETS: aarch64-apple-darwin,x86_64-apple-darwin
201-
- run: npm run init-macos-test-app
232+
- run: pnpm run init-macos-test-app
202233
- run: pod install --project-directory=macos
203234
working-directory: apps/macos-test-app
204235
- name: Bundle test app
@@ -213,18 +244,16 @@ jobs:
213244
env:
214245
MOCHA_REMOTE_CONTEXT: allTests
215246
test-android:
216-
# Temporarily gated to labeled PRs only: the ubuntu-self-hosted runner is
217-
# offline, so running this on main pushes leaves the job queued forever and
218-
# blocks the whole Check run from completing. Re-enable on main once the
219-
# self-hosted runner is back. Tracked in #379.
220-
if: contains(github.event.pull_request.labels.*.name, 'Android 🤖')
247+
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'Android 🤖')
221248
name: Test app (Android)
222249
runs-on: ubuntu-self-hosted
223250
steps:
224251
- uses: actions/checkout@v4
252+
- uses: pnpm/action-setup@v4
225253
- uses: actions/setup-node@v6
226254
with:
227255
node-version: lts/krypton
256+
cache: pnpm
228257
- name: Setup cpp tools
229258
uses: aminya/setup-cpp@v1
230259
with:
@@ -243,15 +272,15 @@ jobs:
243272
with:
244273
packages: tools platform-tools ndk;${{ env.NDK_VERSION }} cmake;${{ env.CMAKE_VERSION }}
245274
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
246-
- run: npm ci
247-
- run: npm run bootstrap
275+
- run: pnpm install --frozen-lockfile
276+
- run: pnpm run bootstrap
248277
env:
249278
CMAKE_RN_TRIPLETS: x86_64-linux-android
250279
FERRIC_TARGETS: x86_64-linux-android
251280
- name: Clone patched Hermes version
252281
shell: bash
253282
run: |
254-
REACT_NATIVE_OVERRIDE_HERMES_DIR=$(npx react-native-node-api vendor-hermes --silent)
283+
REACT_NATIVE_OVERRIDE_HERMES_DIR=$(pnpm exec react-native-node-api vendor-hermes --silent)
255284
echo "REACT_NATIVE_OVERRIDE_HERMES_DIR=$REACT_NATIVE_OVERRIDE_HERMES_DIR" >> $GITHUB_ENV
256285
working-directory: apps/test-app
257286
# - name: Setup Android Emulator cache
@@ -269,9 +298,9 @@ jobs:
269298
sudo udevadm control --reload-rules
270299
sudo udevadm trigger --name-match=kvm
271300
- name: Build weak-node-api for all Android architectures
272-
run: npm run prebuild:build:android --workspace weak-node-api
301+
run: pnpm --filter weak-node-api run prebuild:build:android
273302
- name: Build ferric-example for all architectures
274-
run: npm run build -- --android
303+
run: pnpm run build -- --android
275304
working-directory: packages/ferric-example
276305
- name: Run tests (Android)
277306
timeout-minutes: 75
@@ -293,7 +322,7 @@ jobs:
293322
adb logcat > emulator-logcat.txt 2>&1 &
294323
LOGCAT_PID=$!
295324
# Build, install and run the app
296-
npm run test:android:allTests -- --mode Release
325+
pnpm run test:android:allTests -- --mode Release
297326
# Wait a bit for the sub-process to terminate, before terminating the emulator
298327
sleep 5
299328
# Stop logcat
@@ -310,9 +339,11 @@ jobs:
310339
runs-on: macos-latest
311340
steps:
312341
- uses: actions/checkout@v4
342+
- uses: pnpm/action-setup@v4
313343
- uses: actions/setup-node@v6
314344
with:
315345
node-version: lts/krypton
346+
cache: pnpm
316347
- name: Setup cpp tools
317348
uses: aminya/setup-cpp@v1
318349
with:
@@ -328,14 +359,19 @@ jobs:
328359
distribution: "temurin"
329360
- run: rustup target add x86_64-apple-darwin x86_64-apple-ios aarch64-apple-ios aarch64-apple-ios-sim
330361
- run: rustup toolchain install nightly --component rust-src
331-
- run: npm ci
332-
- run: npm run build
362+
# macOS runners ship a newer CMake than the project supports; pin it to match the version used elsewhere in CI
363+
- name: Install compatible CMake version
364+
uses: jwlawson/actions-setup-cmake@v2
365+
with:
366+
cmake-version: ${{ env.CMAKE_VERSION }}
367+
- run: pnpm install --frozen-lockfile
368+
- run: pnpm run build
333369
- name: Build weak-node-api for all Apple architectures
334370
run: |
335-
npm run prebuild:prepare --workspace weak-node-api
336-
npm run prebuild:build:apple --workspace weak-node-api
371+
pnpm --filter weak-node-api run prebuild:prepare
372+
pnpm --filter weak-node-api run prebuild:build:apple
337373
# Build Ferric example for all Apple architectures
338-
- run: npx ferric --apple
374+
- run: pnpm exec ferric --apple
339375
working-directory: packages/ferric-example
340376
- name: Inspect the structure of the prebuilt binary
341377
run: lipo -info ferric_example.apple.node/*/libferric_example.framework/libferric_example > lipo-info.txt

.github/workflows/release.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ jobs:
1818
environment: main
1919
steps:
2020
- uses: actions/checkout@v4
21+
- uses: pnpm/action-setup@v4
2122
- uses: actions/setup-node@v6
2223
with:
2324
node-version: lts/krypton
25+
cache: pnpm
2426
- name: Setup cpp tools
2527
uses: aminya/setup-cpp@v1
2628
with:
@@ -39,12 +41,12 @@ jobs:
3941
with:
4042
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
4143
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
42-
- run: npm install
44+
- run: pnpm install
4345

4446
- name: Create Release Pull Request or Publish to NPM
4547
id: changesets
4648
uses: changesets/action@v1
4749
with:
48-
publish: npm run release
50+
publish: pnpm run release
4951
env:
5052
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Pods
55
target
66
.cxx
77

8+
# Ignore the pnpm lockfile (managed by pnpm, not formatted by Prettier)
9+
pnpm-lock.yaml
10+
811
# Ignore hermes
912
packages/host/hermes
1013
packages/node-addon-examples/examples

apps/test-app/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
"@react-native-community/cli": "^20.0.2",
2828
"@react-native-community/cli-platform-android": "^20.0.2",
2929
"@react-native-community/cli-platform-ios": "^20.0.2",
30-
"@react-native-node-api/ferric-example": "*",
31-
"@react-native-node-api/node-addon-examples": "*",
32-
"@react-native-node-api/node-tests": "*",
30+
"@react-native-node-api/ferric-example": "workspace:*",
31+
"@react-native-node-api/node-addon-examples": "workspace:*",
32+
"@react-native-node-api/node-tests": "workspace:*",
3333
"@react-native/babel-preset": "0.81.4",
3434
"@react-native/metro-config": "0.81.4",
3535
"@react-native/typescript-config": "0.81.4",
@@ -42,8 +42,8 @@
4242
"mocha-remote-react-native": "^1.13.2",
4343
"react": "19.1.0",
4444
"react-native": "0.81.4",
45-
"react-native-node-api": "*",
45+
"react-native-node-api": "workspace:*",
4646
"react-native-test-app": "^4.4.7",
47-
"weak-node-api": "*"
47+
"weak-node-api": "workspace:*"
4848
}
4949
}

0 commit comments

Comments
 (0)