Skip to content
Merged
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
124 changes: 124 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Project Coding Standards (reusable)

> Drop this file in **unchanged** across projects. Save as `CLAUDE.md` (Claude Code),
> `AGENTS.md` (Codex), `GEMINI.md` (Gemini CLI), or `.cursorrules` (Cursor).
>
> This file never contains project-specific details. Each repository may add a small
> companion file (e.g. `PROJECT.md`) with its architecture and conventions — see §10.

## 0. Governing principle

You do **not** impose a coding style. You **discover** how this project already works and
match it. When a rule here conflicts with what the codebase clearly does, the codebase
wins (except on correctness and safety). When two rules here conflict, apply §1.
When unsure, **stop and ask** (§9) instead of guessing.

## 1. Priority order (use this to resolve conflicts)

1. **Correctness & safety** — code that works and doesn't break existing behavior.
2. **Consistency** with the existing codebase.
3. **Readability & maintainability.**
4. **Performance.**
5. **Brevity.**

Never sacrifice a higher item for a lower one. Optimize performance only when it does not
wreck readability — unless a *measured* bottleneck justifies it.

## 2. Before you change anything

- Read the files you're about to touch **and their immediate neighbors** (same module/
folder). Do not attempt to read the whole repo.
- Find the **nearest existing example** of what you're building (a similar component,
service, or endpoint) and mirror its structure, naming, and error handling.
- Identify utilities, hooks, services, and abstractions that **already exist** and reuse
them. Do not add a dependency or write a helper if an equivalent is already present.
- If the existing pattern is unclear or the codebase is inconsistent, **ask which
convention to follow** — do not invent a third one.

## 3. Consistency & scope

- Match the surrounding code's style, naming, and file layout. Do **not** introduce a new
pattern, library, or abstraction unless explicitly requested.
- Keep changes **minimal and scoped to the task**. Do not refactor, reformat, or "clean
up" unrelated code in the same change.
- Preserve public APIs, types, and behavior unless the task is specifically to change them.

## 4. Code quality (actionable)

- One responsibility per function / class / module.
- Prefer composition over inheritance.
- No duplicated logic — reuse or extract.
- Type everything the language allows. No `any` / untyped escapes without a written reason.
- Handle errors the way the surrounding module already does (same error types, same logging).
- Remove dead code and unused imports **that you introduce**; don't touch unrelated ones.
- Meaningful names; don't introduce abbreviations the project doesn't already use.
- Apply SOLID and immutable patterns where they fit — as tools, not dogma.

## 5. Comments

- **English only.**
- Explain the **why**, never the **what**. No comment that merely restates the code.
- Include a one-line file header only when it clarifies a non-obvious responsibility.

```ts
/**
* Manages authentication state and token refresh.
* Prevents duplicated refresh requests across the application.
*/
```

Avoid:

```ts
// Create variable
const user = ...
```

## 6. UI (only when the project has a UI)

- **Adopt the project's existing design system** — Material, Fluent, Human Interface,
Tailwind/shadcn, or custom tokens. Follow whatever is already in use; do not switch.
- Use existing components and **theme tokens**. Never hardcode spacing, color, typography,
or elevation values — reference the tokens the project defines.
- If the project has no design system, follow the platform's native conventions.
- Always apply, regardless of system: **accessibility** (labels, roles, focus, contrast)
and **responsive** layouts (no fixed-pixel designs that break on small screens).

## 7. Performance (measure before optimizing)

- Avoid unnecessary re-renders / recomputation. Memoize only where it measurably matters.
- Lazy-load heavy or rarely-used modules.
- Clean up subscriptions, listeners, and timers to prevent leaks.
- Do not micro-optimize non-hot-path code at the cost of clarity.

## 8. Verify before finishing (discover the commands)

Find this project's quality commands — check `package.json` scripts, `Makefile`,
`pubspec.yaml`, `build.gradle`, or the CI config — then run them:

- Lint / format check
- Type check
- Tests
- Build

The task is **not done** until they pass. If no such commands exist, say so and describe
what you verified manually instead. Never claim a change works without checking.

## 9. When to STOP and ask

Do not guess — ask first — when:

- The task is ambiguous or under-specified.
- Two existing patterns conflict and there is no clear winner.
- The change would touch many files, alter a public API, add a dependency, or change
architecture.
- You would have to invent business logic that isn't specified anywhere.

## 10. Project-specific context

Before starting, look for a repo-specific companion file (e.g. `PROJECT.md`,
`docs/ARCHITECTURE.md`, or a `## Project` section below). If it exists, treat it as
authoritative for architecture, folder structure, and naming.

If it does **not** exist, infer those conventions from the codebase (§2) and, for anything
you cannot infer with confidence, ask rather than assume.
189 changes: 189 additions & 0 deletions PROJECT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# PROJECT.md — react-native-components

Companion to `CLAUDE.md` (§10). Authoritative for this repo's architecture, conventions and
commands. `CLAUDE.md` is the generic, project-agnostic rules file and **lives here as its source of
truth** — it is copied unchanged into the other repos. Do not add project-specific content to it;
it goes here.

## What this is — two things in one repo

1. **The published library**: `packages/components/` → **`@jmstechnologiesinc/react-native-components`**
(currently `0.1.79`, ISC, published to npm from GitHub). This is the real product.
2. **A Storybook harness** at the repo root (`package.json` name `react_native_storybook_starter`,
`private: true`). A throwaway React Native app whose only job is to render the library's stories,
on-device and on web. It is **not** shipped and **not** the thing you are editing when asked to
change a component.

Lerna (`lerna.json`, `packages/*`, `version: "independent"`) manages the package, but there is
effectively one package.

**Almost every change belongs in `packages/components/src/`.** Touch the root only for Storybook,
lint/babel/metro config, or the hosted docs.

## Who consumes this

`CustomerApp` (all four apps: Shopping / Vendor / Driver / RideAndSharing) depends on this package.
`Firebase/functions` and `fleet-management` share the sibling `@jmstechnologiesinc/*` packages.

That makes this a **published, versioned dependency of a production app**: every export in
`packages/components/src/index.js` is public API. Renaming, reshaping props, or removing an export
is a **breaking change** — `CLAUDE.md` §3 ("preserve public APIs") applies literally here. If a
change would break a consumer, stop and ask (§9).

## Layout of the library

```
packages/components/src/
index.js THE public API — the single barrel. Nothing is public unless exported here.
styles.js shared style objects built from MD3LightTheme tokens
consts.js shared constants (LAYOUT_MODE, …)
utils.js shared helpers (ImageKit URL builders, action sheets, deep linking, …)
Config.js
<ComponentName>/ one folder per component
<ComponentName>.js the component
<ComponentName>.stories.js its Storybook story
(sub-components, utils.js, index.js for the bigger ones)
Localization/ localized() / setI18nConfig() + Translations/{en,es}.json
truly-native/ TN* legacy primitives (TNActivityIndicator, TNEmptyStateView)
```

Bigger features are folders with an `index.js` and internal sub-components + hooks — see `Chat/`
(`Bubble`, `Composer`, `MessageList`, `useChat.js`, `useStreamingMessages.js`, `models.js`) and
`Order/`. Mirror the nearest existing folder of similar size rather than inventing a shape.

### Adding a component (the checklist)

1. `packages/components/src/<Name>/<Name>.js`
2. `packages/components/src/<Name>/<Name>.stories.js`
3. Export it from `packages/components/src/index.js` — otherwise it does not exist for consumers.
4. Add the folder's glob to `.storybook/main.ts` (see "Storybook" below — the web config is a
hand-maintained list, not a glob over `src/**`).
5. Any user-facing string goes into **both** `Translations/en.json` and `es.json` via `localized()`.

## Style — note the split, and match the file you are in

The repo has **two different styles**, which is exactly the situation `CLAUDE.md` §0/§2 covers:
match the surrounding code, do not unify them as a side effect of another task.

| | Root harness (`App.tsx`, `.storybook/`, `.ondevice/`) | Library (`packages/components/src/`) |
|---|---|---|
| Config | `.eslintrc.js` → `@react-native-community`; `.prettierrc.js` → `singleQuote`, `bracketSpacing: false`, `arrowParens: 'avoid'`, `bracketSameLine` | Follows the JMS house style used across `CustomerApp` / `Firebase` |
| Indent | 2 spaces | **4 spaces** |
| Braces | `{foo}` | `{ foo }` |
| Language | TypeScript (`.tsx`/`.ts`) | **JavaScript (`.js`) — do not add TS to the library** |

Library conventions: **arrow-function components**, named export folder + default export component,
`PascalCase` files, import order React → react-native → `@jmstechnologiesinc/*` → local (blank-line
separated). Comments in **English** (§5).

## Design system — this repo *is* the design system layer

- UI primitives come from **`@jmstechnologiesinc/react-native-paper`** (a Material Design 3 fork),
never from upstream `react-native-paper` and never raw `react-native` components where a Paper
one exists.
- **Spacing/color/typography come from `MD3LightTheme` tokens** (`MD3LightTheme.spacing.x2`,
`MD3LightTheme.colors.surfaceDisabled`, `variant="headlineSmall"`). Hardcoding a pixel or hex
value here propagates the mistake into every consuming app — `CLAUDE.md` §6 is not negotiable in
this repo.
- Icons via `MATERIAL_ICONS` from `@jmstechnologiesinc/commons`, not string literals.
- `styles.js` holds the shared style objects; extend it rather than re-declaring the same margins.

## Sibling `@jmstechnologiesinc/*` packages

`commons`, `user`, `vendor`, `driver`, `order`, `cart`, `react-native-paper`,
`material-bottom-tabs`, `bottom-sheet`, `react-native-size-matters`, `react-native-phone-input`,
`react-native-google-places-autocomplete`, `react-native-image-blur-loading`.

Constants and generic helpers (`isNumeric`, `MATERIAL_ICONS`, order statuses, money formatters) live
in `commons` and friends — **check there before writing a helper here**, and never duplicate one.
Money is `dinero.js` (cents/Dinero objects, never floats).

## Localization

`Localization/Localization.js`: `setI18nConfig()` picks the best tag via `react-native-localize`,
`localized(key, config)` is a `lodash.memoize`d `i18n-js` lookup that **falls back to returning the
key when the translation is missing** — so a missing string fails silently and looks like a key on
screen. Always add to `en.json` **and** `es.json`. `localized.cache.clear()` inside `setI18nConfig`
is why a language change actually takes effect; don't remove it.

`Localization.web.js` is the web twin. Several modules have `.web.js` counterparts — if you change a
module that has one, check whether the twin needs the same change.

## Storybook

Two separate configs, both reading stories from `packages/components/src`:

- **`.ondevice/`** (`@storybook/react-native`) — glob `**/*.stories.?(ts|tsx|js|jsx)`, picks up new
stories automatically. Run `npm run storybook-generate` after adding one.
- **`.storybook/`** (`@storybook/react-webpack5` + `react-native-web`) — an **explicit, hand-written
list of per-folder globs**. A new story does **not** appear on the web/hosted Storybook until you
add its line here. This is the step most easily forgotten.

Entry point: `App.tsx` swaps in `./.ondevice` when `STORYBOOK_ENABLED` is set (`react-native-dotenv`).

The web build is the **public documentation site** — `firebase.json` deploys `storybook-static/` to
`react-native-components-e19ee.web.app`, and the README's demo links point at it. A story is the
component's documentation, not an optional extra.

## Build & publish — read this before releasing

`packages/components/package.json`:

```json
"main": "lib/index.js", "files": ["lib"],
"build": "npm run clean && mkdir lib && cp -r src/* lib"
```

**The build is a plain copy — there is no transpilation.** `lib/` ships raw JSX + ESM, which is why
consumers must have this package inside their Metro/Babel transform path. Consequences:

- `lib/` is generated. **Never edit `lib/` — edit `src/` and rebuild.**
- `lib/` is stale until you run `npm run build` in `packages/components/`; publishing without it
ships the previous version's code.
- Adding syntax that Metro/Babel in the consumer can't handle breaks consumers at bundle time, not
here. Test a real change against `CustomerApp` when in doubt.
- `peerDependencies` (`react`, `react-native`, `@jmstechnologiesinc/react-native-paper`,
`@react-navigation/elements`, `centrifuge`, `react-native-gesture-handler`,
`react-native-keyboard-controller`, `react-native-reanimated`, `react-native-vector-icons`) are the
contract with the host app. **A new runtime dependency is an architectural change — ask first (§9)**;
it must either be a peer the app already has, or be justified as a real dependency.

## Commands (§8)

```bash
# root (harness)
npm run lint # eslint . <- must pass
npm test # jest (preset react-native)
npm run prettier # prettier --write "**/*.{js,jsx,ts,tsx,json,css,md}"

npm run storybook # metro with STORYBOOK_ENABLED
npm run storybook:ios / storybook:android
npm run storybook-generate # regenerate .ondevice/storybook.requires.ts after adding stories
npm run storybook:web # storybook dev -p 6006
npm run build-storybook # -> storybook-static/
firebase deploy --only hosting # publishes the docs site — confirm before running

# library
cd packages/components && npm run build # clean + copy src -> lib
```

There is **no type check** for the library (it is JS). "Verified" means: lint passes, `jest` passes,
and the affected story renders in Storybook. For a change consumers depend on, also build and run it
in `CustomerApp`.

## Tests

Jest with the `react-native` preset. Coverage is thin — `packages/components/__tests__/components.test.js`
and `Localization/__tests__/Localization.test.js`, plus the root `__tests__/App.test.tsx`. New tests
go next to the code in a `__tests__/` folder. In practice **the story is the primary verification**
for visual components; write one that exercises the states you changed.

## Gotchas

- `main.js` / entry: root `index.js` registers the app; `App.tsx` decides Storybook vs demo screen.
- Web support is real (`react-native-web`, `mapbox-gl`, `react-map-gl`, `.web.js` twins) and the
`.storybook` webpack config polyfills `os` via `os-browserify`. Don't assume native-only.
- `truly-native/` is legacy (`TN*` prefix). Don't extend it; new work goes in a normal component folder.
- The root package is `private: true` on purpose — never publish from the root.
- Current branch at time of writing: `feat/menu-schedule-strings`; remote
`github.com/jmstechnologiesinc/react-native-components`.