8.6 KiB
8.6 KiB
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.tsor.spec.tssuffix for Vitest,.batsfor shell tests - Constants in TypeScript use UPPER_SNAKE_CASE within modules (e.g.,
IMAGE_EXTS,CATEGORY_COLORSinuseFileType.ts)
Functions:
- TypeScript/Vue: camelCase for all functions (e.g.,
getFileCategory,formatSize,useFileType) - Composables:
useprefix 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
defineStorefactory - 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
.valuesuffix 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
.eslintrcdetected at repo root orneode-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:
- Vue framework imports (
import { computed, ref, type Ref } from 'vue') - Library imports (
import { defineStore } from 'pinia',import { format } from 'date-fns') - Local module imports (
import { useFileType } from '../useFileType',import { rpcClient } from '../api/rpc-client') - Types/interfaces (inline in import statements via
typekeyword when needed) - No blank lines required between groups in practice
Path Aliases:
- TypeScript:
@alias maps tosrc/(configured invitest.config.tsandtsconfig.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
maxRetriesper call
Logging
Framework: console object for frontend, tracing crate for Rust backend
Patterns:
- Frontend:
console.warn,console.errorused selectively (e.g.,[RPC]prefixed logs inrpc-client.tsfor 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.tslines 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.,
RPCOptionsobject over separatemethod, 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
voidor the modified state - Utilities return simple values or objects (e.g.,
formatSizereturns string,formatDatereturns 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,formatDatefrom 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 barrelindex.ts - Test files import specific utilities directly to minimize test setup complexity
Type Safety
Vue 3 with TypeScript:
- Components use
<script setup lang="ts">withdefineProps<{ ... }>()anddefineEmits<{ ... }>() - 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 valueswatch()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:
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