1 changed files with 338 additions and 0 deletions
@ -0,0 +1,338 @@ |
|||
# Meshtastic Web Client - React & TypeScript Coding Guidelines |
|||
|
|||
_This document outlines the primary coding guidelines and contribution standards |
|||
for the Meshtastic Web Client project. It details our preferred styles, |
|||
patterns, and practices to ensure code consistency and quality. As our project |
|||
evolves, these guidelines should be treated as a living document, updated |
|||
regularly with new suggestions and improvements from the team._ |
|||
|
|||
## Table of Contents |
|||
|
|||
- [General Principles](#general-principles) |
|||
- [File Structure](#file-structure) |
|||
- [Component Design](#component-design) |
|||
- [TypeScript Usage](#typescript-usage) |
|||
- [Styling (Tailwind CSS)](#styling-tailwind-css) |
|||
- [State Management](#state-management) |
|||
- [Hooks](#hooks) |
|||
- [Testing](#testing) |
|||
- [Naming Conventions](#naming-conventions) |
|||
- [Code Formatting & Linting](#code-formatting--linting) |
|||
- [Dependency Management & Library Usage](#dependency-management--library-usage) |
|||
- [Documentation](#documentation) |
|||
- [Git & PRs](#git--prs) |
|||
|
|||
## General Principles |
|||
|
|||
- **Readability:** Write code that is easy for others (and your future self) to |
|||
understand. |
|||
- **Consistency:** Adhere to these guidelines to maintain a consistent codebase. |
|||
- **Simplicity (KISS):** Keep components and functions focused and avoid |
|||
unnecessary complexity. |
|||
- **Dry (Don't Repeat Yourself):** Abstract reusable logic and components. |
|||
- **Accessibility (a11y):** Build interfaces that are usable by everyone. Use |
|||
semantic HTML and ARIA attributes where appropriate. |
|||
|
|||
## File Structure |
|||
|
|||
Our project uses the following directory structure within `/src`: |
|||
|
|||
- **/src**: The root directory for all source code. |
|||
|
|||
- **/src/core**: Contains foundational code shared across the entire |
|||
application. |
|||
- **/src/core/dto**: Defines Data Transfer Objects, typically plain objects |
|||
mirroring API structures or used for passing structured data internally. |
|||
- **/src/core/hooks**: Houses custom hooks with **high reusability** across |
|||
multiple features or domains (e.g., `useCopyToClipboard`, |
|||
`useLocalStorage`). Hooks specific to a single component or feature should |
|||
be co-located. |
|||
- **/src/core/stores**: Contains global state management stores, implemented |
|||
using **Zustand**. |
|||
- **/src/core/utils**: Holds general-purpose utility functions with **high |
|||
reusability** across the application (e.g., date formatters, string |
|||
formatters, date/time convertion helpers). Utilities specific to a component |
|||
or feature should be co-located or placed within that feature's scope. |
|||
|
|||
- **/src/components**: Acts as a general container for components. |
|||
- **/src/components/UI**: Houses generic, highly reusable UI elements (e.g., |
|||
`Button`, `Input`, `Dialog`). These should be application-agnostic and |
|||
stylable. |
|||
- **/src/components/PageComponents**: Contains components that encapsulate |
|||
specific features or logic primarily used within one or more pages from |
|||
`/src/pages`. This is where the bulk of the logic delegated from |
|||
`/src/pages` components should reside (e.g., `NodeDetails`, `MessageItem`). |
|||
|
|||
- **/src/pages**: |
|||
- Contains view-specific components, representing distinct pages or routes |
|||
(e.g., `Channels.tsx`, `Messages.tsx`). |
|||
- These components should contain **minimal logic**, focusing on composing |
|||
layout and integrating components from `/src/components/PageComponents`. |
|||
|
|||
- **/src/validation**: |
|||
- Stores validation schemas (e.g., using Zod) primarily intended for |
|||
validating user data prior to it being saved to the Meshtatic within the |
|||
`onSubmit` function. |
|||
|
|||
- **/src/tests**: |
|||
- Contains global test configuration (e.g., Vitest config, global setup files, |
|||
potentially shared mocks or test utilities). |
|||
- **Note:** Individual test files (e.g., `Button.test.tsx`) must **still be |
|||
co-located** with the source code file they are testing. This directory is |
|||
_not_ intended to hold all test files. |
|||
|
|||
**Co-location within Directories:** |
|||
|
|||
- The co-location strategy remains essential _within_ `/pages`, |
|||
`/components/UI`, and `/components/PageComponents`. |
|||
- **Refactoring Pattern:** If a component file (e.g., `NodeList.tsx`) grows |
|||
complex or requires related files (hooks, tests, sub-components), replace it |
|||
with a directory `NodeList/`. Move the component code to `NodeList/index.tsx` |
|||
(preserving import paths). Co-locate related files (tests, |
|||
_component-specific_ hooks/utils, sub-components) within this directory. |
|||
|
|||
**Module Barrel Files (`mod.ts`):** |
|||
|
|||
- Use `mod.ts` if needed to create barrel files for exporting multiple items |
|||
from utility directories (e.g., `/src/core/utils/mod.ts`) or specific feature |
|||
groupings if they arise. |
|||
- Remember the component co-location pattern uses `index.tsx` specifically to |
|||
maintain clean import paths for the component itself. |
|||
|
|||
## Component Design |
|||
|
|||
- **Functional Components:** Use functional components with Hooks exclusively. |
|||
- **Props:** |
|||
- Define props using TypeScript interfaces |
|||
(`interface Component Name + Props {}` ex: DialogProps {}). |
|||
- Be explicit with prop types. Use `| undefined` for optional props if |
|||
necessary, but prefer explicit boolean props (e.g., `isEnabled` vs. |
|||
`disabled={true/false}`). |
|||
- Destructure props within the function signature. |
|||
- Avoid overly complex prop objects; pass data down as needed. |
|||
- **Component Size:** Keep components small and focused on a single |
|||
responsibility. If a component becomes too large or complex, break it down. |
|||
- **Composition:** Favor composition over inheritance. Build complex UIs by |
|||
combining smaller, reusable components. |
|||
- **JSX:** |
|||
- Always provide `key` props when rendering lists. The `key` prop should be |
|||
used on a unique id value and never on the array index value. |
|||
- Use fragments (`<>...</>`) when you don't need a wrapping DOM element. |
|||
- Conditional rendering: Use clear and concise methods (e.g. ternary |
|||
operators, or dedicated variables/functions for complex logic). |
|||
|
|||
## TypeScript Usage |
|||
|
|||
- **Strict Mode:** Enable `strict` mode in `deno.json` |
|||
- **Explicit Types:** Be explicit with types for function parameters, return |
|||
values, and complex variables. Let TypeScript infer simple types where |
|||
obvious. |
|||
- **Interfaces vs. Types:** |
|||
- Use `interface` for defining the shape of objects and component props. |
|||
- Use `type` for unions, intersections, primitives, tuples, or more complex |
|||
type manipulations. |
|||
- **`any`:** Avoid using `any`. Use `unknown` if the type is truly unknown and |
|||
perform type checking, or define a more specific type. Use |
|||
`// deno-lint-ignore no-explicit-any` with a justification comment in rare, |
|||
unavoidable cases. |
|||
- **Enums:** Prefer string literal unions |
|||
(`type Status = 'idle' | 'loading' | 'success' | 'error';`) over numeric or |
|||
string enums for better readability and bundle size, unless the enum provides |
|||
specific advantages for your use case. |
|||
- **Utility Types:** Leverage built-in utility types like `Partial`, `Required`, |
|||
`Readonly`, `Pick`, `Omit` to create new types from existing ones. |
|||
- **Generics:** Use generics for reusable functions, hooks, and components that |
|||
operate on different data types while maintaining type safety. |
|||
|
|||
## Styling (Tailwind CSS) |
|||
|
|||
- **Utility-First:** Embrace the utility-first nature of Tailwind. Apply styles |
|||
directly in the JSX. |
|||
- **Readability:** If a component has a large number of Tailwind classes, |
|||
consider: |
|||
- Breaking the component down into smaller ones. |
|||
- Using a utility like `clsx` or `classnames` for conditional classes. |
|||
- Extracting repetitive class combinations into variables within the component |
|||
or potentially using `@apply` in a CSS file _sparingly_ for complex, highly |
|||
reused patterns (though generally prefer composition or utility functions). |
|||
- **`@apply`:** Use `@apply` with caution. It can negate some of Tailwind's |
|||
benefits if overused. Prefer composing utilities directly in JSX or creating |
|||
reusable components. Use it mainly for complex, non-reusable local |
|||
abstractions if needed. |
|||
- **Customization:** Define custom colors, spacing, fonts, etc., in `index.css` |
|||
rather than using arbitrary values (`![...]`) frequently. |
|||
|
|||
## State Management |
|||
|
|||
- **Local State (`useState`):** Use for component-specific state that doesn't |
|||
need to be shared. |
|||
- **Derived State:** Calculate values directly from props or existing state |
|||
during rendering instead of storing derived state in `useState` unless the |
|||
calculation is expensive (then use `useMemo`). |
|||
- **Reducer (`useReducer`):** Use for complex state logic involving multiple |
|||
sub-values or when the next state depends on the previous one, especially |
|||
within a single component or closely related components (can be co-located). |
|||
- **Global State (Zustand):** Use Zustand for managing application-wide state. |
|||
Store definitions reside in `/src/core/stores`. |
|||
|
|||
## Hooks |
|||
|
|||
- **Naming:** Prefix custom hooks with `use` (e.g., `useNodeList`, |
|||
`useDebounce`). |
|||
- **Single Responsibility:** Hooks should have a clear, single purpose. |
|||
- **Reusability:** Design hooks intended for broad reuse and place them in |
|||
`/src/core/hooks`. Hooks specific to a component/feature should be co-located |
|||
next to that component. |
|||
- **Composability:** Favor creating smaller, general-purpose hooks. These can |
|||
often be composed together (used within other custom hooks) to build more |
|||
complex logic cleanly and maintainably. |
|||
- **Rules of Hooks:** Adhere strictly to the Rules of Hooks (only call Hooks at |
|||
the top level, only call Hooks from React functions). Deno Lint can help |
|||
enforce these. |
|||
- **Minimize `useEffect` Usage:** Avoid using `useEffect` for logic that can be |
|||
handled during rendering (data transformation) or in event handlers (user |
|||
interactions). Effects are primarily for synchronizing with external systems |
|||
(network requests, timers, browser APIs). Before using an Effect, consult the |
|||
React documentation to see if it's truly necessary. Refer to: |
|||
[You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect). |
|||
- **Dependency Arrays:** When `useEffect`, `useCallback`, or `useMemo` are |
|||
necessary, be diligent with their dependency arrays. Include all values from |
|||
the function scope that the hook depends on. Deno Lint includes rules (like |
|||
`react-hooks-exhaustive-deps` if enabled in config) to help enforce this, |
|||
ensuring stability and preventing stale closures. |
|||
|
|||
## Testing |
|||
|
|||
- **Test Framework (Vitest):** Use Vitest for all levels of automated testing, |
|||
including unit, integration, and component tests. Global test |
|||
configuration/setup files reside in `/src/tests`. |
|||
- **Component Testing (React Testing Library):** Utilize React Testing Library |
|||
(with Vitest) for component testing, focusing on testing behavior from the |
|||
user's perspective rather than implementation details. |
|||
- **Unit Tests:** Use Vitest for testing utility functions, hooks, and complex |
|||
logic isolated from the UI. |
|||
- **Integration Tests:** Use Vitest and React Testing Library to test |
|||
interactions between multiple components. |
|||
- **End-to-End Tests (Playwright/Cypress):** Use for testing critical user flows |
|||
through the entire application. (Choose one framework if applicable). |
|||
- **Test Requirement:** All new features or significant code changes **must** |
|||
include corresponding tests. Tests should be added, updated, or refactored |
|||
alongside the code they are testing. |
|||
- **Co-location:** Individual test files (`*.test.tsx`, `*.test.ts`) **must** be |
|||
co-located with the source code file they are testing. |
|||
- **Coverage:** Aim for reasonable test coverage, focusing on critical paths, |
|||
complex logic, and potential edge cases. Don't chase 100% coverage blindly, |
|||
prioritize meaningful tests. |
|||
- **Mocking:** Use Vitest's built-in mocking capabilities for dependencies |
|||
(modules, timers). |
|||
|
|||
## Naming Conventions |
|||
|
|||
- **Components:** `PascalCase` (e.g., `ChannelChat`, `NodeDetail`). If you are |
|||
co-locating other files together create a folder and keep the primary |
|||
component named `Component/index.tsx`. |
|||
- **Hooks:** `useCamelCase` (e.g., `useNodes`). |
|||
- **Variables/Functions:** `camelCase` (e.g., `nodeCount`). |
|||
- **Constants:** `UPPER_SNAKE_CASE` (e.g., `MAX_ROWS`, `DEFAULT_ZOOM_LEVEL`). |
|||
- **TypeScript Types/Interfaces:** `PascalCase` (e.g., `NodeData`, `MapProps`). |
|||
- **Boolean Props/Variables:** Use positive phrasing (e.g., `isEnabled`, |
|||
`isActive`) rather than negative (`isDisabled`, `isInactive`). |
|||
|
|||
## Code Formatting & Linting |
|||
|
|||
- **Formatter (Deno Formatter):** Use Deno's built-in formatter |
|||
(`deno task format`) for consistent code formatting. It is recommended to use |
|||
the default Deno formatting rules. |
|||
- **Linter (Deno Linter):** Use Deno's built-in linter (`deno task lint`) for |
|||
identifying code quality issues and potential errors. Adhere to the default |
|||
Deno linting rules. |
|||
- **Configuration:** While `deno task format` and `deno task lint` work with |
|||
defaults, specific configurations (like includes/excludes or rule adjustments) |
|||
can be managed in the `deno.json` file if necessary. Commit this configuration |
|||
file to the repository if used. |
|||
- **Integration:** Integrate `deno task format` and `deno task lint` into your |
|||
development workflow: |
|||
- Configure your editor to use Deno's tools for formatting on save and |
|||
displaying lint errors. |
|||
|
|||
## Dependency Management & Library Usage |
|||
|
|||
**Core Principle: Minimize Bundle Size** |
|||
|
|||
A primary goal of this project is to maintain a small and performant application |
|||
bundle. Every external dependency added increases the potential size and |
|||
complexity. Therefore, we prioritize minimizing the number of third-party |
|||
libraries. |
|||
|
|||
**Guidelines:** |
|||
|
|||
1. **Vanilla First:** Before reaching for an external library, strongly consider |
|||
if the required functionality can be reasonably achieved using internal or |
|||
runtime-provided resources first: |
|||
- Standard browser APIs (Web APIs). |
|||
- Vanilla TypeScript features and logic. |
|||
- **The Deno Standard Library (`@std`):** Check if a suitable, audited module |
|||
exists within the Deno Standard Library |
|||
([https://jsr.io/@std](https://jsr.io/@std)). Utilizing `@std` was a |
|||
driving factor in choosing Deno, so leverage it where appropriate before |
|||
adding external dependencies. |
|||
|
|||
2. **Primary Evaluation Criteria:** When considering adding or retaining an |
|||
_external_ library (beyond `@std`), the following are **primary decision |
|||
factors**: |
|||
- **Bundle Size Impact:** Analyze the library's size (e.g., using |
|||
`bundlephobia.com`). How significantly does it increase the overall bundle? |
|||
Does it support tree-shaking effectively? Smaller is strongly preferred. |
|||
- **Maintenance & Activity:** Is the library **actively maintained with |
|||
recent updates**? Check its repository (e.g., GitHub) for recent commits, |
|||
releases, and responsiveness to issues. Avoid libraries that appear |
|||
abandoned or unmaintained. |
|||
- **Adoption & Popularity:** Is the library widely used and established |
|||
within the community (e.g., high download counts on npm/JSR, significant |
|||
GitHub stars)? A larger user base often indicates better vetting, more |
|||
available support, and higher likelihood of long-term maintenance. |
|||
- **Necessity:** Does it solve a genuinely complex problem that would be |
|||
significantly time-consuming or error-prone to build ourselves (or isn't |
|||
covered by the Deno Standard Library `jsr:@std`)? |
|||
- **Quality & Documentation:** Is the library well-documented? Does it follow |
|||
good coding practices? |
|||
- **Ecosystem Fit:** Does it align well with our existing core technologies |
|||
(React, TypeScript, Vite)? |
|||
|
|||
3. **Strategic Adoption/Migration:** Adding or switching libraries is |
|||
justifiable when a candidate excels in the primary criteria (size, |
|||
maintenance, adoption) _and_ offers significant improvements (developer |
|||
experience, performance, capabilities) or strong ecosystem benefits over |
|||
alternatives or existing solutions. |
|||
|
|||
4. **Team Approval:** Adding significant new _external_ dependencies should |
|||
ideally be discussed briefly with the team or project maintainers to ensure |
|||
alignment with project goals. |
|||
|
|||
**In short: Prioritize Browser APIs, Vanilla TS, and the Deno Standard Library. |
|||
For external libraries, prioritize small, well-maintained, and widely adopted |
|||
ones. Be deliberate when adding external dependencies, and focus on quality and |
|||
ecosystem fit when one _is_ necessary.** |
|||
|
|||
## Documentation |
|||
|
|||
- **Complex Logic:** Add comments to explain complex algorithms, non-obvious |
|||
logic, or workarounds. Avoid commenting on obvious code. |
|||
- **README:** Maintain a comprehensive `README.md` covering project setup, |
|||
architecture overview, and contribution guidelines (or link to this file |
|||
`CONTRIBUTING.md`). |
|||
|
|||
## Git & PRs |
|||
|
|||
- **Branching:** Use a feature-branching workflow (e.g., Gitflow simplified). |
|||
Create branches from `master` for new features or fixes. |
|||
- **Commit Messages:** Follow Conventional Commits format (e.g., |
|||
`feat: add node filtering`, `fix: resolve map marker overlap`, |
|||
`refactor: simplify state logic`). |
|||
- **Pull Requests (PRs):** |
|||
- Keep PRs small and focused on a single issue or feature. |
|||
- Write clear PR descriptions explaining the _what_ and _why_ of the change. |
|||
Include steps for testing or screenshots if applicable. |
|||
- Ensure code is linted and formatted before pushing. |
|||
- Ensure tests pass. |
|||
- Require code reviews before merging. |
|||
Loading…
Reference in new issue