From dae1a662a3277e1485949d22ba127a0bb5b5b55d Mon Sep 17 00:00:00 2001 From: Angel Manuel Carrasco Date: Sun, 2 Aug 2026 19:04:40 -0400 Subject: [PATCH] docs: write down for the agents how this library works CLAUDE.md holds the generic rules and no project detail at all, so the same file can be dropped into every repo and copied under whatever name each tool reads. This repo is where it is kept: the copies in CustomerApp, Firebase and fleet-management are byte-identical to this one. PROJECT.md is what an agent could not see from the file it happened to open. Chiefly that this is a published dependency of a production app, so every line of packages/components/src/index.js is public API and a renamed prop is a breaking change rather than a cleanup. And that the repo is two things at once: the library under packages/, and a private Storybook harness at the root that is not shipped and is usually not what a request to "change a component" means. The rest is the things that are silently wrong rather than loudly wrong: - npm run build copies src to lib with no transpilation, so lib/ ships raw JSX, is stale until rebuilt, and editing it instead of src/ survives until the next publish. - .storybook/main.ts is a hand-written list of globs, not a glob over src/**. A new story renders on device and is missing from the web docs. - localized() returns the key when a translation is missing, so a string added to en.json alone looks fine in English and shows the key in Spanish. - The two styles in the tree are deliberate -- 2-space TypeScript at the root, 4-space JavaScript in the library -- and are not to be unified as a side effect of another task. --- CLAUDE.md | 124 +++++++++++++++++++++++++++++++++++ PROJECT.md | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 CLAUDE.md create mode 100644 PROJECT.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..fb0753f --- /dev/null +++ b/CLAUDE.md @@ -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. diff --git a/PROJECT.md b/PROJECT.md new file mode 100644 index 0000000..591f155 --- /dev/null +++ b/PROJECT.md @@ -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 + / one folder per component + .js the component + .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//.js` +2. `packages/components/src//.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`.