javascript
Installation
SKILL.md
JavaScript
House conventions for modern JavaScript and Node. Apply them to code you are writing or changing — don't refactor untouched files to match unless asked.
Conventions
- ESM everywhere. New packages get
"type": "module". Mixing CJS and ESM inside one package produces the dual-package hazard — two copies of a module with separate state, failing in ways that read like race conditions. - Match the combinator to the intent. Sequential
awaitin a loop serialises independent work;Promise.alldrops the whole batch on one rejection;allSettledis for when partial success is acceptable. The wrong choice is a latency bug or a swallowed-error bug, and both look fine in review. - Every
catchhandles or rethrows. Acatchthat logs and continues converts a failure into corrupt state further downstream, where the stack trace no longer points at the cause. If there is nothing useful to do, don't catch. AbortControllerfor anything cancellable — fetches, timers, streams, listeners ({ signal }). Without it an abandoned request keeps its handler and its closure alive.- Never block the event loop. No
readFileSync, no long synchronous parse or crypto on a request path: Node serves every other request from that thread. CPU-bound work goes to a worker thread. - Don't mutate arguments. A function that edits its input changes the caller's object with no assignment in sight. Return new values — and reach for the non-mutating array methods that exist for exactly this:
toSorted,toReversed,toSpliced,with.arr.sort()sorting the caller's array in place is one of the most common accidental mutations in JS. node:prefix on builtins. Unambiguous, and immune to shadowing by a userland package of the same name.- Reach for the platform before a dependency.
structuredClone,Intl,URL/URLSearchParams,crypto.randomUUID,Array.prototype.at/findLast,Object.groupBycover most of what utility libraries were installed for.
Publishing a package
The exports map is the public surface — anything not listed is unreachable, which is the point: it lets you refactor internals without breaking consumers. Declare sideEffects: false (or list the files that have them) so bundlers can drop unused exports; without it, tree shaking has to assume importing your module does something and keeps all of it.