Skip to main content

javascript

Write and review JavaScript: declarations, equality and coercion, functions, objects, iteration, classes, async and promise semantics, ES modules, regular expressions, JSDoc typing, and the ECMAScript features a project's engine baseline permits.

Source facts

Repository
xobotyi/cc-foundry
Last source activity
October 13, 2026 at 09:31
Detected SKILL.md language
English
Stars
22
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
18 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
javascript
description
Write and review JavaScript: declarations, equality and coercion, functions, objects, iteration, classes, async and promise semantics, ES modules, regular expressions, JSDoc typing, and the ECMAScript features a project's engine baseline permits.
when_to_use
Invoke whenever JavaScript is touched at all — writing, reviewing, refactoring, debugging, or exploring a `.js`, `.mjs`, `.cjs`, or `.jsx` file, or raising a project's engine baseline. Also invoke on the symptoms: a comparison succeeds that should not, a promise rejects with nobody listening, `sort` returns the wrong order, an import reads `undefined` at module top level, a regex matches once and then fails, a class field is `undefined` inside a constructor, or a feature that runs in Node fails in a browser. Covers the language and its built-in globals; types belong to the typescript skill, runtime APIs to nodejs and bun, test authoring to vitest, and language-agnostic workflow to the coding skill.
compatibility
Uses Claude Code frontmatter beyond the Agent Skills spec (when_to_use)
**JavaScript fails by continuing.** A mismatched type is coerced, a missing property is `undefined`, a rejected promise nobody watched is a warning. None of it reports itself, so the discipline is to make a failure loud at the earliest point it can be seen. Three biases decide most calls: - **Prefer the construct that refuses over the one that converts** — `===` over `==`, `??` over `||`, `Number(s)` over `parseInt(s)`, a thrown error over a silent `undefined`. - **The declared engine baseline decides what may be written**, not the ECMAScript edition a feature belongs to. - **Reach for the built-in globals first.** A dependency earns its place by doing what `Intl`, `URL`, `structuredClone`, `AbortController`, and the iterator helpers do not. ## Language Edition Read the project's engine baseline before writing code: `engines` in `package.json`, `browserslist`, `target` and `lib` in `tsconfig.json` or `jsconfig.json`, `.nvmrc`, and the CI matrix. Where a transpiler stands between source and runtime, its target governs the output and the source may run ahead; where none does, the engine baseline's oldest engine governs the source. **An edition number is not an availability claim.** Engines ship features years before ratification and lag on others, and the gap runs both ways inside a single edition: `Array.fromAsync` and `Math.sumPrecise` are both ES2026, yet the first shipped in Chrome 121 and reached Baseline widely available on 2026-07-25, while the second reached Baseline newly available on 2026-04-10 and is absent from Node.js 26. Check the feature, never the edition. Floor edition per feature this skill's rules reference. Anything from ES2019 or earlier is available in every engine a project can realistically target. - **ES2020** — `?.`, `??`, `globalThis`, `BigInt`, `Promise.allSettled`, `String.prototype.matchAll`, dynamic `import()`, `import.meta`, specified `for...in` order - **ES2021** — `??=`, `||=`, `&&=`, `String.prototype.replaceAll`, `Promise.any`, `AggregateError`, numeric separators - **ES2022** — class fields, `#private` fields and methods, `#x in obj`, `static {}` blocks, top-level `await`, `Object.hasOwn`, `.at()`, `Error` `cause`, RegExp `d` flag - **ES2023** — `toSorted`, `toReversed`, `toSpliced`, `with`, `findLast`, `findLastIndex` - **ES2024** — `Object.groupBy`, `Map.groupBy`, `Promise.withResolvers`, RegExp `v` flag, `String.prototype.isWellFormed` - **ES2025** — iterator helpers, `Set` methods (`union`, `intersection`, `difference`, …), `Promise.try`, `RegExp.escape`, RegExp modifiers `(?i:)`, duplicate named capture groups, import attributes, JSON modules - **ES2026** — `Map.prototype.getOrInsert` and `getOrInsertComputed`, `Iterator.concat`, `Array.fromAsync`, `Error.isError`, `Math.sumPrecise`, `Uint8Array` base64 and hex, `JSON.rawJSON` - **Stage 4, unratified** — `using` and `await using` with `Symbol.dispose` and `DisposableStack`; `Temporal`; `Iterator.zip`. No Safari 26.x release implements any of them, so none is safe for an engine baseline that includes Safari 26. `using` requires Chrome 134, Firefox 141, or Node.js 24; `Temporal` requires Chrome 144, Firefox 139, or Node.js 26. `Atomics.pause` is the exception, reaching Chrome 133, Firefox 137, and Safari 18.4. - **Not shippable** — decorators are Stage 2.7 and no engine implements them. A project using them runs TypeScript or Babel, and TypeScript's `experimentalDecorators` is the older, incompatible design. Read [`${CLAUDE_SKILL_DIR}/references/versions/es20NN.md`] — one file per ratified edition, `es2020.md` through `es2026.md`, plus [`${CLAUDE_SKILL_DIR}/references/versions/stage4-queue.md`] for the unratified set — when writing against a feature near the engine baseline, and whenever raising it. Each carries what its edition added, what behavior it changed, and the traps it introduced. ## Naming - **One word per concept across the codebase.** `getUser` everywhere, never `getUser` beside `fetchUserInfo` and `loadCustomerRecord`. - **`url`, `id`, `err`, `ctx`, `req`, `res`, `db`, `fn` are the accepted abbreviations.** Spell out everything else in code. A signature written to describe an API rather than to run — `Object.groupBy(items, cb)` — takes the placeholder name the documentation uses. - **No redundant context**: `car.make`, never `car.carMake`. - **Name a value or a type for its role, never its structure.** `UserShape`, `UserObject`, `UserData`, and `userArray` restate what the declaration already shows; `User`, `users`, `UserPatch`, and `UserRow` say what the value is for. - **File names are kebab-case**: `user-service.js`. Match the surrounding directory when it already differs. - **A single-letter name is legal in a scope of one or two lines** — a loop index, a comparator parameter — and nowhere else. ## Declarations and Scope - **Never `var`.** It is function-scoped and hoists to `undefined`, which is why it survives only in code that predates block scoping. - **`const` freezes the binding, not the value.** A `const` object is still mutable. - **One declaration per statement.** `const a = 1, b = 2` breaks under a debugger and under `git blame`. - **Declare at first use, not at the top of the function.** The distance between declaration and use is the variable's real scope. - **A `let` that is assigned exactly once in every branch wants a ternary or a helper function returning `const`.** ## Equality and Coercion - **`===` and `!==` always, with one exception:** `value == null` tests `null` and `undefined` together, and the strict alternative is longer without being clearer. - **`??` for defaults, `||` only when `0`, `""`, and `false` genuinely mean "absent".** Every configuration default is `??`. - **`?.` where the absence is expected, never to silence a bug.** Data that must be present throws at the access rather than surfacing as `undefined` three frames later. `obj?.method()` still throws when `method` is missing; write `obj?.method?.()` when both are optional. - **`??` cannot be mixed with `||` or `&&` without parentheses** — it is a `SyntaxError`. - **The falsy set is `false`, `0`, `-0`, `0n`, `""`, `null`, `undefined`, `NaN`.** `[]`, `{}`, and `"0"` are truthy. - **`Number.isNaN`, never the global `isNaN`**, which coerces first: `isNaN("abc")` is `true`. - **`Number(s)` over `parseInt(s)` for a whole-string conversion.** `parseInt` stops at the first invalid character, so `parseInt("1e3")` is `1`. Never pass `parseInt` as a bare callback — `map` supplies the index as the radix, and `["1","7","11"].map(parseInt)` is `[1, NaN, 3]`. - **Never compute money in a double.** `(1.005).toFixed(2)` is `"1.00"`. Hold integer minor units, and format with `Intl.NumberFormat`. - **`Object.is` only for `NaN` and `-0`.** Everywhere else it is `===` with extra ceremony. Read [`${CLAUDE_SKILL_DIR}/references/conventions/coercion.md`] when a comparison behaves inconsistently between two built-ins, when a number loses precision, or when a copy shares state it should not — it carries the four equality algorithms and which built-in uses each, the `-0` sources, BigInt mixing rules, property-order rules, and the prototype-pollution boundary. ## Functions - **Arrow functions for callbacks, `function` declarations for named module-level functions.** A `function` declaration hoists, which lets helpers sit below the code that reads them. - **Never an arrow function as an object method or on a prototype.** It has no `this` of its own. - **Guard clauses first, happy path unindented.** A function whose body is one `if` wrapping everything wants an early return. - **Destructure the parameter object at three or more parameters.** Positional arguments past two are unreadable at the call site and impossible to extend. - **Default parameters over `||` inside the body.** They are evaluated left to right and may reference earlier parameters. - **Rest parameters, never `arguments`** — `arguments` is array-like, absent in arrow functions, and defeats optimization. - **Return an object when returning several values.** A tuple binds callers to positional order. - **A closure keeps the whole scope alive, not the values it reads.** A callback that captures one field of a large object retains the object. ## Objects and Copying - **Spread over `Object.assign` for a copy**, because it does not fire setters on the target. Both are shallow and both drop the prototype. - **`structuredClone` for a deep copy of data.** It throws `DataCloneError` on functions and symbols, and discards the prototype, so a class instance clones to a plain object. - **`Object.hasOwn(obj, key)`, never `obj.hasOwnProperty(key)`** — the latter breaks on a null-prototype object. - **Dot notation for a known key, brackets only for a computed one.** - **`Object.freeze` is shallow.** Freezing a tree means freezing every level, or not claiming immutability. - **Never mutate a parameter**, except a `reduce` accumulator whose initial value the same call created. Return a new value; the caller decides whether to keep the old one. - **Never extend a built-in prototype.** It is visible to every dependency in the process. ## Arrays and Iteration - **`for...of` for side effects, array methods for transformation, `for` only when the index is used.** `forEach` cannot `break` and does not await; reach for it only when the callback is already a named function. - **Never `for...in` on an array**, and rarely on an object — it walks inherited enumerable keys. - **Always pass a comparator to `sort` for numbers.** The default is lexicographic: `[1, 5, 10, 2].sort()` is `[1, 10, 2, 5]`. The comparator must return a number; `(a, b) => a > b` returns booleans, which makes the order implementation-defined and the bug engine-dependent. - **`sort` and `reverse` mutate.** Prefer `toSorted` and `toReversed` where the engine baseline allows. - **Never `delete` an array element.** It leaves a hole and does not change `length`. Use `splice` or `filter`. - **Build a dense array with `Array.from({ length: n })` or `[...Array(n)]`.** `Array(n).map(f)` calls `f` zero times, because array methods skip holes while spread and `for...of` treat them as `undefined`. - **`Map` when keys are not strings, come from outside the program, or must keep insertion order.** Object keys are stringified, and integer-like keys reorder themselves ahead of the rest. - **`Set` for membership and deduplication** — `[...new Set(items)]`. - **Grow an accumulator in place, never by copying it.** `{ ...acc, [k]: v }`, `[...acc, x]`, `acc.concat(x)`, and `Object.assign({}, acc, x)` copy everything gathered so far on every step, so the loop is quadratic. Measured on Node 26.9.0, a spread `reduce` over 8 000 items takes 3.7 s, and assigning into the accumulator takes under 1 ms. Assign or push into an accumulator the loop created, then return it. `Object.fromEntries`, `Object.groupBy`, and `new Map(entries)` replace most of these reductions outright. - **Iterator helpers for a stream that is infinite, expensive, or larger than memory.** They are lazy and single-pass: draining a helper drains its source. - **Sort user-visible text with `Intl.Collator`.** `<` compares UTF-16 code units, so `"ä"` sorts after `"z"`. - **Count characters with `Intl.Segmenter`, not `.length`.** `.length` counts code units and `[...str]` counts code points; neither is a user-perceived character. Read [`${CLAUDE_SKILL_DIR}/references/conventions/iteration.md`] when a loop produces the wrong count, when an array method skips elements, or when a generator's cleanup does not run — it carries the full hole-behavior split, the iterator-closing rules, and the `Map`/`Set` mutation-during-iteration semantics. ## Classes - **Reach for a class when a type has invariants, several methods over shared state, or a lifecycle.** A class with one method and no state is a function. - **`#private` fields, never a leading underscore.** `#x in obj` is the only reliable same-class test; `instanceof` is forgeable and fails across realms. - **Never call an overridable method from a constructor.** The base constructor completes before any derived field initializer runs, so the method sees `undefined`. - **A method belongs on the prototype; an arrow-function field is an own property per instance.** Use the field form only where the function is detached from its receiver. - **Composition over `extends`.** Inherit only for a real "is-a" with a shared contract. - **Omit a constructor that only calls `super(...args)`.** - **Never rely on a built-in method to return your subclass.** Array methods honor `Symbol.species` and the `Set` methods do not; `structuredClone` discards the prototype either way. Read [`${CLAUDE_SKILL_DIR}/references/conventions/classes.md`] when a field reads `undefined`, when `instanceof` gives the wrong answer, or when designing a class hierarchy — it carries the full initialization order, the private-field semantics, and the prototype-pollution rules. ## Async - **Every promise is awaited or has a handler.** `send().catch(reportError)` for fire-and-forget, which is the one place a `.then` belongs. A floating promise is a failure nobody will see. - **`.map(async …)` needs a `Promise.all` around it, and `forEach(async …)` is always wrong** — `forEach` discards the returned promise and finishes before any callback body does. - **Start independent work together, await together**: `const [a, b] = await Promise.all([f(), g()])`. Two sequential awaits with no dependency between them double the latency. - **Bound a large fan-out.** `Promise.all` over ten thousand items opens ten thousand operations. - **Pick the combinator by failure mode** — `all` when every result is required, `allSettled` when every outcome must be observed, `any` for the first success, `race` only with an `AbortController`, because the losers keep running. - **`return await` inside a `try`, plain `return` outside it.** `return promise` inside a `try` returns before the promise settles, so the `catch` never fires. - **Never `return`, `break`, or `continue` out of a `finally`** — it discards a pending throw. - **`AbortController` is the cancellation protocol.** Thread `{ signal }` through; use `AbortSignal.timeout(ms)` for a deadline and `AbortSignal.any([...])` to combine. Distinguish an abort from a real failure before retrying. - **`new Promise(...)` only to wrap a callback API.** Where a resolver must escape, `Promise.withResolvers()` returns `{ promise, resolve, reject }`. - **Sequential `await` in a loop is correct when each step depends on the last** and a defect otherwise. - **`using` and `await using` release a resource on every exit path** where the engine baseline allows them; `try`/`finally` with one nested block per resource is the equivalent elsewhere.
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub