160 lines
8.6 KiB
Markdown
160 lines
8.6 KiB
Markdown
# Coding Conventions
|
||
|
||
**Analysis Date:** 2026-07-29
|
||
|
||
## Naming Patterns
|
||
|
||
**Files:**
|
||
- TypeScript/Vue: PascalCase for components (e.g., `ToggleSwitch.vue`, `SendBitcoinModal.vue`), camelCase for composables and stores (e.g., `useFileType.ts`, `controller.ts`)
|
||
- Rust: snake_case for modules and files (e.g., `bitcoin_rpc.rs`, `storage_crypto.rs`)
|
||
- Test files: co-located with source in `__tests__/` subdirectories with `.test.ts` or `.spec.ts` suffix for Vitest, `.bats` for shell tests
|
||
- Constants in TypeScript use UPPER_SNAKE_CASE within modules (e.g., `IMAGE_EXTS`, `CATEGORY_COLORS` in `useFileType.ts`)
|
||
|
||
**Functions:**
|
||
- TypeScript/Vue: camelCase for all functions (e.g., `getFileCategory`, `formatSize`, `useFileType`)
|
||
- Composables: `use` prefix for Vue composables (e.g., `useFileType`, `useToast`, `useMessageToast`) — exported as named exports or default exports
|
||
- Store functions (Pinia): defined with snake_case action names, exported from `defineStore` factory
|
||
- Rust: snake_case for all functions and methods (e.g., `doesnt_reallocate`, following Rust conventions)
|
||
|
||
**Variables:**
|
||
- TypeScript: camelCase for local variables and reactive refs (e.g., `modelValue`, `isActive`, `gamepadCount`)
|
||
- Refs (Vue 3): prefix not required, but convention is lowercase start (e.g., `const ext = ref('jpg')`)
|
||
- Computed properties: camelCase, explicit `.value` suffix in templates when needed
|
||
- Parameters: camelCase, typed explicitly in TypeScript (e.g., `password: string`, `isDir: Ref<boolean>`)
|
||
|
||
**Types:**
|
||
- TypeScript: PascalCase for type aliases and interfaces (e.g., `RPCOptions`, `FileCategory`, `CatalogVersionInfo`)
|
||
- Union types: PascalCase (e.g., `PendingState = 'pending' | 'sent' | 'approved'`)
|
||
- Component props: typed with `defineProps<{ ... }>()` syntax in `<script setup>`
|
||
- Rust: PascalCase for structs and enums, snake_case for fields within them
|
||
|
||
## Code Style
|
||
|
||
**Formatting:**
|
||
- No global Prettier config; code style follows project patterns incrementally
|
||
- Vue components: single-file components (`.vue`) with `<template>`, `<script setup>`, optional `<style scoped>`
|
||
- TypeScript: indentation is 2 spaces (visible in `vitest.config.ts`, Vue components, test files)
|
||
- Line width: no strict enforcement observed; pragmatic wrapping around 80–100 characters
|
||
- Arrow functions preferred for short callbacks: `(x) => x * 2`
|
||
- Template strings for multi-line formatting
|
||
|
||
**Linting:**
|
||
- No `.eslintrc` detected at repo root or `neode-ui/` level
|
||
- Rust: Clippy allowances declared at crate level in `main.rs` (`#![allow(...)]`) to suppress stylistic lints and focus CI on correctness issues
|
||
- Examples of suppressed Clippy lints: `too_many_arguments`, `type_complexity`, `enum_variant_names`, `unused_io_amount`
|
||
|
||
## Import Organization
|
||
|
||
**Order:**
|
||
1. Vue framework imports (`import { computed, ref, type Ref } from 'vue'`)
|
||
2. Library imports (`import { defineStore } from 'pinia'`, `import { format } from 'date-fns'`)
|
||
3. Local module imports (`import { useFileType } from '../useFileType'`, `import { rpcClient } from '../api/rpc-client'`)
|
||
4. Types/interfaces (inline in import statements via `type` keyword when needed)
|
||
5. No blank lines required between groups in practice
|
||
|
||
**Path Aliases:**
|
||
- TypeScript: `@` alias maps to `src/` (configured in `vitest.config.ts` and `tsconfig.json`)
|
||
- Usage: `import { displayVersion } from '@/utils/version'`
|
||
- Rust: crate-relative paths (`use crate::module::submodule`) and external crate paths
|
||
|
||
## Error Handling
|
||
|
||
**Patterns:**
|
||
- TypeScript: explicit try-catch with error type narrowing (e.g., `if (error instanceof Error) { ... }`)
|
||
- RPC client (`rpc-client.ts`): catches fetch errors, AbortError, and HTTP errors; distinguishes retryable (502, 503) from permanent (401, 403)
|
||
- Rust: `anyhow::Result<T>` for fallible operations; `?` operator for error propagation; `.context("message")` for adding context
|
||
- Backend error responses: JSON-RPC format with `error: { code, message, data? }` structure; UI catches and displays via toast system
|
||
- Network errors: automatic retry with exponential backoff (600ms × (attempt + 1) with jitter); configurable `maxRetries` per call
|
||
|
||
## Logging
|
||
|
||
**Framework:** console object for frontend, `tracing` crate for Rust backend
|
||
|
||
**Patterns:**
|
||
- Frontend: `console.warn`, `console.error` used selectively (e.g., `[RPC]` prefixed logs in `rpc-client.ts` for session/CSRF events)
|
||
- Rust: `tracing::info!`, `tracing::warn!` for structured logging; `println!` avoided in production code
|
||
- No log levels enforced or documented; pragmatic use based on severity
|
||
|
||
## Comments
|
||
|
||
**When to Comment:**
|
||
- Explain non-obvious retry logic, timeout decisions, CSRF handling (see `rpc-client.ts` lines 138–178 for example)
|
||
- Clarify why a workaround exists (e.g., "Already on the login page: redirecting = a full reload")
|
||
- Document integration points with backend RPC methods and their expected response shapes
|
||
- Avoid redundant comments restating what the code obviously does
|
||
|
||
**JSDoc/TSDoc:**
|
||
- Function parameter types documented inline via TypeScript type annotations (e.g., `ext: Ref<string>`)
|
||
- Minimal use of explicit JSDoc blocks; type signature is the primary documentation
|
||
- Comments above exports explain purpose in one sentence (e.g., "// RPC Client for connecting to Archipelago backend")
|
||
- Optional fields in interfaces documented via property-level comments (e.g., `/** Abort the call from the outside … */`)
|
||
|
||
## Function Design
|
||
|
||
**Size:** Functions are typically 5–50 lines; error-handling paths in `callInner<T>` (`rpc-client.ts`) stretch to 120 lines but remain single-responsibility (retry logic + error classification)
|
||
|
||
**Parameters:**
|
||
- Prefer object parameters for 3+ arguments (e.g., `RPCOptions` object over separate `method, params, timeout`)
|
||
- Vue composables accept `Ref<T>` types to maintain reactivity (e.g., `useFileType(ext: Ref<string>, isDir: Ref<boolean>)`)
|
||
- Store action functions accept only necessary parameters; broader state via closure
|
||
|
||
**Return Values:**
|
||
- Composables return object with properties (computed values + reactive refs): `{ category, isImage, isAudio, ... }`
|
||
- Store actions return `void` or the modified state
|
||
- Utilities return simple values or objects (e.g., `formatSize` returns string, `formatDate` returns string)
|
||
- Async functions return `Promise<T>` with explicit type parameters (e.g., `async call<T>(options): Promise<T>`)
|
||
|
||
## Module Design
|
||
|
||
**Exports:**
|
||
- Composables export a single named function and helper functions: `export function useFileType(...)`, `export function getFileCategory(...)`
|
||
- Stores export the Pinia store factory: `export const useControllerStore = defineStore(...)`
|
||
- RPC client exports as singleton instance: `export const rpcClient = new RPCClient()`
|
||
- Utilities export multiple helpers from the same file (e.g., `formatSize`, `formatDate` from same module)
|
||
|
||
**Barrel Files:**
|
||
- Not observed as a primary pattern; each file self-documents its exports
|
||
- Imports use direct paths (e.g., `from '../composables/useFileType'`) rather than barrel `index.ts`
|
||
- Test files import specific utilities directly to minimize test setup complexity
|
||
|
||
## Type Safety
|
||
|
||
**Vue 3 with TypeScript:**
|
||
- Components use `<script setup lang="ts">` with `defineProps<{ ... }>()` and `defineEmits<{ ... }>()`
|
||
- Props explicitly typed as interfaces/objects with required/optional fields marked
|
||
- Events typed as call signatures (e.g., `'update:modelValue': [value: boolean]`)
|
||
- Reactive variables typed at declaration: `const isActive = ref<boolean>(false)`, or via inference when obvious
|
||
|
||
**Rust:**
|
||
- Explicit type annotations on public APIs; inference acceptable inside functions
|
||
- Generic parameters used to encode interface contracts (e.g., `struct DataUrl<'a>`)
|
||
- Pattern matching to handle enums and `Option<T>` / `Result<T, E>` types safely
|
||
|
||
## Component Architecture (Vue)
|
||
|
||
**File structure:**
|
||
- Single-file components with template → script → (optional) style
|
||
- Props come first, emits second, internal state/computed/methods follow
|
||
- One component per file (naming matches the file name)
|
||
- Slots used minimally; prefer explicit prop configuration over slot forwarding
|
||
|
||
**Reactivity:**
|
||
- `ref()` for primitive/object state; `computed()` for derived values
|
||
- `watch()` used for side effects on ref changes (not extensively shown in samples but implied)
|
||
- Pinia stores used for global state (authentication, app list, mesh status, etc.)
|
||
|
||
## Constants and Enums
|
||
|
||
**Pattern:** Constants are module-level `const` with UPPER_SNAKE_CASE names and immutable type annotations:
|
||
|
||
```typescript
|
||
const IMAGE_EXTS = new Set(['jpg', 'jpeg', ...])
|
||
const CATEGORY_COLORS: Record<FileCategory, string> = { ... }
|
||
```
|
||
|
||
**Enums:** Type aliases preferred over TypeScript `enum` keyword (e.g., `type FileCategory = 'folder' | 'image' | ...`)
|
||
|
||
---
|
||
|
||
*Convention analysis: 2026-07-29*
|