|
| 1 | +--- |
| 2 | +paths: |
| 3 | + - "**/*.js" |
| 4 | + - "**/*.jsx" |
| 5 | + - "**/*.mjs" |
| 6 | + - "**/*.cjs" |
| 7 | + - "**/*.ts" |
| 8 | + - "**/*.tsx" |
| 9 | + - "**/*.mts" |
| 10 | + - "**/*.cts" |
| 11 | +--- |
| 12 | + |
| 13 | +# JavaScript Conventions |
| 14 | + |
| 15 | +Clarity is the highest virtue — if control flow needs a comment, rewrite it. Prefer boring patterns over clever |
| 16 | +tricks. If existing code contradicts a rule below, follow the code and flag the divergence. For TypeScript files these |
| 17 | +rules are the base layer; [typescript.md](typescript.md) stacks on top. |
| 18 | + |
| 19 | +## Declarations & Naming |
| 20 | + |
| 21 | +- `const` by default, `let` only when reassigned, never `var`. One declaration per line; `const` group before `let`. |
| 22 | + `const` prevents reassignment, not mutation. |
| 23 | +- camelCase for variables/functions, PascalCase for classes, `#private` class fields (not `_` convention), |
| 24 | + `is`/`has`/`can`/`should` prefixes for booleans, kebab-case file names. |
| 25 | +- SCREAMING_SNAKE_CASE only for true compile-time constants — a variable holding a computed value is camelCase. |
| 26 | +- Descriptive names; short names (`i`, `x`) only in tiny scopes. Accepted abbreviations: `url`, `id`, `err`, `ctx`, |
| 27 | + `req`, `res` — avoid others. No redundant context (`car.make`, not `car.carMake`). Same word for the same concept |
| 28 | + across the codebase. |
| 29 | + |
| 30 | +## Equality & Safety |
| 31 | + |
| 32 | +- Always `===`/`!==`; the only valid `==` is `value == null` (catches both `null` and `undefined`). |
| 33 | +- `??` over `||` for defaults — `||` swallows `0`, `""`, `false`. |
| 34 | +- `?.` sparingly: data you expect to exist should throw, not silently yield `undefined`. |
| 35 | +- Falsy values: `false`, `0`, `-0`, `0n`, `""`, `null`, `undefined`, `NaN` — everything else is truthy, including |
| 36 | + `[]`, `{}`, `"0"`. |
| 37 | +- Ternaries: one-liner or one-branch-per-line only; anything else becomes `if`/`else` or early return. Nested |
| 38 | + ternaries are banned — no exceptions. |
| 39 | + |
| 40 | +## Syntax |
| 41 | + |
| 42 | +- Template literals only when interpolating; plain quotes otherwise. |
| 43 | +- Spread for copies (`{ ...obj }`, `[...arr]`), never `Object.assign`. Shorthand properties, grouped at the top of |
| 44 | + the literal. Computed keys `{ [key]: value }`. Logical assignment: `opts.timeout ??= 5000`. |
| 45 | + |
| 46 | +## Functions |
| 47 | + |
| 48 | +- Arrows for callbacks/anonymous functions; function declarations when hoisting or `this` binding is needed. |
| 49 | + Parentheses around single arrow params. Never arrows as object methods or on prototypes (lexical `this`). |
| 50 | +- Destructured options object for 3+ parameters. Default parameters over `||`/manual checks. Rest params over |
| 51 | + `arguments`. |
| 52 | +- Early return, guard clauses first, happy path flat. One function, one job — a name containing "and" means split. |
| 53 | + Keep under ~30 lines; extract helpers. |
| 54 | +- Prefer pure functions; isolate side effects (DOM, network, logging) — don't hide them inside data transformations. |
| 55 | +- Closures retain references, not copies — beware large objects captured unintentionally. |
| 56 | + |
| 57 | +## Async |
| 58 | + |
| 59 | +- `async`/`await` over `.then()` chains. Always `await` promises — a floating promise is a silent failure. |
| 60 | + Fire-and-forget must attach `.catch(reportError)`. |
| 61 | +- `return await` only inside `try` where you catch the error; otherwise return the promise directly. |
| 62 | +- Parallel independent work: `Promise.all`; all results regardless of failure: `Promise.allSettled`; timeout: |
| 63 | + `Promise.race`; fallback: `Promise.any`. No sequential awaits in loops — `Promise.all(items.map(...))`, or a |
| 64 | + concurrency limiter (`p-map`) for large arrays. |
| 65 | +- Throw `Error` objects, never strings. Custom error classes (extend `Error`, set `name`, add context) when callers |
| 66 | + must distinguish. |
| 67 | +- Never swallow errors — every `catch` handles, rethrows, or reports; `console.log(err)` is not handling. Let errors |
| 68 | + propagate to a top-level handler; don't wrap every `await` in `try`/`catch`. |
| 69 | +- `new Promise()` only to wrap callback APIs. `AbortController`/`{ signal }` for cancellation. `for await...of` for |
| 70 | + async iterables. |
| 71 | + |
| 72 | +## Modules |
| 73 | + |
| 74 | +- ES modules only; CommonJS is legacy. Named exports over default (exceptions: framework-required conventions). |
| 75 | + Never export mutable `let` — export accessor functions. |
| 76 | +- Imports at top, grouped with blank lines: built-in (`node:fs`), external, internal. Merge imports from the same |
| 77 | + module. Namespace import (`import * as x`) at 5+ items, named below that. |
| 78 | +- Include file extensions in relative import paths (`"./user.js"`); no directory imports resolving to `index.js`. |
| 79 | +- No barrel files in subdirectories — acceptable only as a package entry point enforced via `package.json` `exports`. |
| 80 | + No wildcard re-exports. No circular dependencies — extract a third module or inject. |
| 81 | +- Dynamic `import()` for code splitting: routes, large conditional deps, feature flags. One concern per module. |
| 82 | + Side-effect imports are rare and documented. |
| 83 | + |
| 84 | +## Objects, Arrays, Classes |
| 85 | + |
| 86 | +- Literals (`{}`, `[]`), never constructors. Method shorthand. Dot notation for static keys, brackets for dynamic. |
| 87 | + `Object.hasOwn(obj, key)` over `hasOwnProperty`. |
| 88 | +- Functional methods (`map`/`filter`/`find`/`some`/`every`/`flatMap`/`reduce`) for transforms — always return in the |
| 89 | + callback; `for...of` for side-effect loops; never `for...in` on arrays. |
| 90 | +- Don't mutate inputs — return new objects/arrays: add `[...arr, item]`, remove `arr.filter`, update `arr.map`. |
| 91 | + Return objects (not arrays) for multiple values. `Array.from(arrayLike)`; `Array.from(iterable, mapFn)` over |
| 92 | + `[...iterable].map()`. |
| 93 | +- `Map` when keys aren't strings or are user-provided (prototype pollution); `Set` for dedup; generators for lazy |
| 94 | + sequences. Never extend built-in prototypes. |
| 95 | +- ES `class` only; `#private` fields; composition over inheritance (`extends` only for true is-a); no empty |
| 96 | + constructors; `static` for state-free operations. Don't force classes — one method with state is a closure, one |
| 97 | + method without is a function. |
| 98 | + |
| 99 | +## Docs & JSDoc |
| 100 | + |
| 101 | +- Doc comments (`/** */`) are API documentation — the "no comments" default does not apply. Every exported function, |
| 102 | + class, and module-level constant gets one; `@param`/`@returns`/`@throws` for non-trivial signatures. Behavior and |
| 103 | + intent, not implementation. Update the doc in the same edit that changes behavior. |
| 104 | +- Pure-JS (non-TS) code: type via JSDoc with `// @ts-check`; annotate exported boundaries, `@typedef` for shared |
| 105 | + shapes, skip the obvious. |
0 commit comments