Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion .claude/skills/build-compilation-dependencies/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ description: >
differently in tests vs the app. Trigger phrases: "add source file", "CMakeLists", "podspec",
"build.gradle", "prebuilt binaries", "FFmpeg disabled", "pod install", "new architecture",
"compile error", "undefined symbol", "SIMD", "worklets build flag", "C++ tests", "conditional
compilation", "include path", "gradle build fails", "link error".
compilation", "include path", "gradle build fails", "link error", "yarn catalog", "catalog:", "add a dependency",
"bump a dependency", "yarn workspaces", "npmMinimalAgeGate", "shared dependency version".
---

# Skill: Build, Compilation & Dependencies
Expand Down Expand Up @@ -56,6 +57,51 @@ react-native-audio-api/

---

## Yarn Workspaces & Dependency Catalog

Dependency versions live in a single catalog in the root `.yarnrc.yml`, referenced from
manifests as `"<dep>": "catalog:"`. Bump once there and every workspace follows.
Needs Yarn >= 4.10 (repo is on 4.18).

**What belongs in the catalog** — anything that must move in lockstep, which is broader
than "used more than once":

- used by two or more workspaces, **or**
- version-locked to something already catalogued even if only one workspace uses it —
`react-test-renderer` must equal `react`; `@react-native-community/cli*` and
`@babel/runtime` move with `react-native` / `@babel/core`.

A dependency used in exactly one workspace with no such coupling stays a literal range:
it has no second copy to drift from, and catalogue membership is meant to *signal*
"keep this in lockstep". There is no CI check enforcing this — review is the enforcement,
which is why membership has to carry meaning.

**Workflow when touching dependencies:**

1. Changing the version of a catalogued dep → edit the catalog entry, not the manifest.
2. Adding a dep → check the catalog first. Already there? Use `"catalog:"`. Not there but
lockstep-coupled or now used twice? Add the entry and switch both call sites over.

Three kinds of dependency must **never** be given `catalog:`:

| Never catalogue | Why |
|---|---|
| `peerDependencies` | Public API surface of the published packages, deliberately loose (`"*"`). Pinning narrows what consumers may install. |
| Runtime `dependencies` of published packages (`semver`, `chalk`, `commander`, `fs-extra`) | The release pipeline packs with `npm pack` (`scripts/create-package.sh`, `npm-custom-node-generator-build.yml`) and npm does not understand `catalog:`. It publishes the literal string, producing an uninstallable package. Only `yarn pack` / `yarn npm publish` perform the swap. |
| `workspace:*` ranges | Not version ranges — nothing to centralise. |

Other constraints:

- **`resolutions` does not accept `catalog:`** — install fails with `typescript@catalog: isn't supported by any available resolver`. Root `package.json` pins `typescript` literally; keep it in sync with the catalog entry. It is *not* redundant with the catalog: it forces `@commitlint/load`'s optional `typescript` peer (`^4.6.4 || ^5.2.2`) onto our version, and catalogs only rewrite workspace manifests, never transitive dependencies.
- **`packages/audiodocs` is a separate Yarn 1 project** with its own lockfile and `node_modules`. It is not in root `workspaces`, so the catalog does not reach it.
- **`yarn pack` is not a drop-in for `npm pack` here.** It drops `development/react/` (a nested `package.json` listed in `files[]`) and strips the executable bit from 29 prebuilt FFmpeg binaries. Do not switch the release pipeline to it without re-verifying the tarball contents.

Supply-chain gate: `npmMinimalAgeGate: '1w'` refuses versions published in the last 7 days.
Bypass one command with `yarn add --no-time-gate` / `yarn up --no-time-gate`, or exempt
specific packages permanently via `npmPreapprovedPackages`.

---

## C++ Header File Extensions

In `common/cpp/audioapi/`: `.hpp` = header-only templates; `.h` = non-template (usually with a `.cpp`). Vendored code is excluded.
Expand Down
1,000 changes: 1,000 additions & 0 deletions .yarn/releases/yarn-4.18.0.cjs

Large diffs are not rendered by default.

925 changes: 0 additions & 925 deletions .yarn/releases/yarn-4.5.0.cjs

This file was deleted.

39 changes: 38 additions & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,44 @@
compressionLevel: mixed

catalog:
'@babel/core': '^7.25.2'
'@babel/preset-env': '^7.25.3'
'@babel/runtime': '^7.25.0'
'@commitlint/config-conventional': '17.0.2'
'@evilmartians/lefthook': '^1.5.0'
'@react-native-community/cli': '20.2.0'
'@react-native-community/cli-platform-android': '20.2.0'
'@react-native-community/cli-platform-ios': '20.2.0'
'@react-native/babel-preset': '0.87.0'
'@react-native/eslint-config': '0.87.0'
'@react-native/metro-config': '0.87.0'
'@react-native/typescript-config': '0.87.0'
'@types/jest': '^29.5.13'
'@types/react': '^19.2.0'
'@types/react-test-renderer': '^19.1.0'
'@types/semver': '7.7.1'
commitlint: '17.0.2'
eslint: '^8.57.0'
eslint-config-prettier: '^9.0.0'
eslint-plugin-prettier: '^5.0.1'
eslint-plugin-react: '^7.34.3'
jest: '^29.7.0'
lucide-react-native: '^0.555.0'
prettier: '^3.3.3'
react: '19.2.3'
react-native: '0.87.0'
react-native-builder-bob: '0.33.1'
react-native-svg: '15.15.4'
react-native-worklets: '0.12.1'
react-test-renderer: '19.2.3'
typescript: '~6.0.3'

enableGlobalCache: false

enableScripts: true

nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.5.0.cjs
npmMinimalAgeGate: '1w'

yarnPath: .yarn/releases/yarn-4.18.0.cjs
46 changes: 23 additions & 23 deletions apps/common-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,38 @@
"@react-navigation/native-stack": "^7.18.8",
"@react-navigation/stack": "^7.10.22",
"@shopify/react-native-skia": "2.11.0",
"lucide-react-native": "^0.555.0",
"lucide-react-native": "catalog:",
"react-native-audio-api": "workspace:*",
"react-native-audio-worklets": "workspace:*",
"react-native-background-timer": "2.4.1",
"react-native-gesture-handler": "3.1.0",
"react-native-reanimated": "4.6.0",
"react-native-safe-area-context": "5.9.0",
"react-native-screens": "4.27.0",
"react-native-svg": "15.15.4",
"react-native-worklets": "0.12.1"
"react-native-svg": "catalog:",
"react-native-worklets": "catalog:"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.2",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli": "20.2.0",
"@react-native-community/cli-platform-android": "20.2.0",
"@react-native-community/cli-platform-ios": "20.2.0",
"@react-native/babel-preset": "0.87.0",
"@react-native/eslint-config": "0.87.0",
"@react-native/metro-config": "0.87.0",
"@react-native/typescript-config": "0.87.0",
"@types/jest": "^29.5.13",
"@types/react": "^19.2.0",
"@babel/core": "catalog:",
"@babel/preset-env": "catalog:",
"@babel/runtime": "catalog:",
"@react-native-community/cli": "catalog:",
"@react-native-community/cli-platform-android": "catalog:",
"@react-native-community/cli-platform-ios": "catalog:",
"@react-native/babel-preset": "catalog:",
"@react-native/eslint-config": "catalog:",
"@react-native/metro-config": "catalog:",
"@react-native/typescript-config": "catalog:",
"@types/jest": "catalog:",
"@types/react": "catalog:",
"@types/react-native-background-timer": "^2.0.2",
"@types/react-test-renderer": "^19.1.0",
"eslint": "^8.57.0",
"jest": "^29.6.3",
"prettier": "^3.3.3",
"react": "19.2.3",
"react-native": "0.87.0",
"react-test-renderer": "19.2.3",
"typescript": "~6.0.3"
"@types/react-test-renderer": "catalog:",
"eslint": "catalog:",
"jest": "catalog:",
"prettier": "catalog:",
"react": "catalog:",
"react-native": "catalog:",
"react-test-renderer": "catalog:",
"typescript": "catalog:"
}
}
Loading
Loading