Add a package
Shared code under packages/ — naming, consuming it, and the Metro rules that make native packages different.
A package is code more than one app imports. One consumer is not a package; it is a directory in that app.
The workspace
packages/* is globbed, so the directory is enough. Flat naming — packages/db,
packages/ui-native — not nesting. packages/ui/native would need a second
workspace glob and is why the nested layout was abandoned.
{
"name": "statshub-odds", // matches the directory
"version": "0.0.0",
"private": true,
"main": "./src/index.ts",
"scripts": { "typecheck": "tsc --noEmit", "lint": "biome check" }
}Publish TypeScript source, not build output. Every consumer bundles it, so a build step is a cache to invalidate for no gain.
Consuming it
"dependencies": { "statshub-odds": "workspace:*" }bun install from the root, then import by name. Turbo infers the build order
from the dependency, so ^build in turbo.json already covers it.
If a React Native app consumes it
Metro does not resolve like Node, and two rules follow.
Metro package exports need an exact extension
A wildcard like "./*": "./src/*" does not resolve, and an array fallback
resolves for tsc but not for Metro. A package with a mix of .ts and .tsx
therefore cannot use an exports map — omit it and let Metro's ordinary file
lookup work. This is why @statshub/ui-native has files at its package root
and no exports field.
Metro also has to watch the package. apps/statshub-expo/metro.config.js sets
watchFolders to the repo root and lists both node_modules directories in
nodeModulesPaths, so anything under packages/ is already covered. If you add
a package whose files Metro reports as missing, that config is where to look.
Prefer deep imports over a barrel
A root index.ts re-exporting a large package pulls all of it into the bundle
for one import, and Metro does not tree-shake its way back out.
@statshub/ui-native ships no barrel on purpose — its 543 call sites each name
what they use.
Checklist
bun install
bunx turbo ls
bun run typecheck
bunx expo export --platform ios # if a native app consumes it