Skip to Content
Examples12 · Expo

12 · Expo

The TypeScript helpers an Expo app shares between screens (theme tokens, text and date formatting) plus a tooling script that validates app.json. All of it runs under 3va without Metro or an emulator.

Source: examples/12-expo

app.json Expo config (validated by the script below) src/ theme.ts light/dark palettes, spacing, radius, font sizes text.ts truncate, pluralize, capitalize, slug, formatCount date.ts formatRelative ("hace 5min", "en 3d"), formatShortDate index.ts runnable demo validate-app.ts app.json checker expo.test.ts 18 tests incl. a theme snapshot

Run the demo

cd examples/12-expo 3va run src/index.ts --allow-read=./src
=== Expo helpers, ejecutados con 3va === Tema claro (primary): #007AFF Tema oscuro (primary): #0A84FF Espaciado SM: 8 px Radio LG: 20 px H1 font-size: 32 px Texto truncado: Esta cadena es bastante larga… Plural: ha enviado 1 mensaje Capitalizar: Exp-router Conteo 15200: 15.2K Conteo 2300000: 2.3M Formato relativo: ahora: ahora hace 5min: hace 5min hace 2h: hace 2h en 3d: en 3d Fecha corta: 11/09/2026

Validate app.json

3va run src/validate-app.ts --allow-read=./app.json
✓ app.json válido — expo-3va-demo v1.0.0 slug: expo-3va-demo scheme: expodemo

With a broken config ({"expo":{"name":"x","version":"1.0"}}) it exits with code 1:

[WARN] expo.scheme no está definido — deep links no funcionarán [ERROR] ✗ expo.slug es requerido (usado por Expo Router) [ERROR] ✗ expo.version '1.0' no sigue semver

That makes it usable as a pre-build check in CI.

Run the tests

3va test # 18 passed

The last test snapshots the whole dark palette into src/__snapshots__/expo.test.ts.snap.json. If anyone changes a color by accident, the test fails.

Scaffold a real Expo app

3va create expo-app my-app # runs create-expo-app, like `npm create` cd my-app

3va create <pkg> works like npm create / bun create. Extra arguments go to the scaffolder, e.g. 3va create expo-app my-app --template default@sdk-57.

Things to keep in mind

  • Each script needs only the reads it does. index.ts needs ./src for its imports. validate-app.ts imports nothing, so ./app.json alone is enough. Without it: Permission denied: --allow-read=…/app.json is required.
  • validate-app.ts mixes require() with TypeScript. 3va accepts const fs = require("fs") inside a .ts file. tsc would want @types/node for that.
  • console.warn and console.error get [WARN] / [ERROR] prefixes in non-interactive runs like the one above. Keep that in mind if you parse the output in CI.
  • Tests that depend on the clock (formatRelative(Date.now() - 3600_000) → "hace 1h") pass because they compute relative to “now”. Absolute dates (formatShortDate) use the machine’s local time zone, so a test fixed to a UTC timestamp could fail in a different time zone.
  • What 3va doesn’t do for Expo: Metro bundling, Expo Router, native modules and EAS builds stay with the Expo toolchain. 3va covers the pure-TS helpers, scripts and tests.