StatsHub Docs

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.

index.ts
package.json
tsconfig.json
packages/statshub-odds/package.json
{
  "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

apps/statshub-web/package.json
"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

On this page