Agent Skills: Modern JavaScript

Proactively apply when creating web applications, Node.js services, or any JavaScript project. Triggers on JavaScript, ES6, ES2020, ES2022, ES2024, ES2025, ES2026, modern JS, refactor legacy, array methods, async/await, optional chaining, nullish coalescing, destructuring, spread, rest, template literals, arrow functions, toSorted, toReversed, at, groupBy, iterator helpers, Set union intersection difference, Promise, Promise.try, RegExp.escape, Array.fromAsync, using await using, Temporal, functional programming. Use when writing new JavaScript code, refactoring legacy code, modernizing codebases, implementing functional patterns, checking which ECMAScript edition or runtime supports a feature, or reviewing JS for performance and readability. Modern JavaScript (ES6-ES2026) patterns and best practices.

UncategorizedID: ccheney/robust-skills/modern-javascript

Install this agent skill to your local

pnpm dlx add-skill https://github.com/ccheney/robust-skills/tree/HEAD/skills/modern-javascript

Skill Files

Browse the full folder contents for modern-javascript.

Download Skill

Loading file tree…

skills/modern-javascript/SKILL.md

Skill Metadata

Name
modern-javascript
Description
Write, modernize, and review JavaScript against real ECMAScript language features and explicit runtime targets. Use for JavaScript or Node.js code, ES edition attribution (ES2015-ES2026), compatibility and polyfill/transpile decisions, array/iterator/Set/Map/Promise/RegExp/TypedArray APIs, modules, async code, Temporal, explicit resource management, decorators, or replacing stale and withdrawn proposal syntax. Prefer standardized features, while permitting mature proposals when the required maintained transform or polyfill is stated.

Modern JavaScript

Use current JavaScript without confusing specification status, runtime support, and toolchain support. Treat these as three separate questions:

  1. Is it standardized? Check the frozen annual ECMA-262 edition or TC39's current proposal tracker.
  2. Does the target run it? Check the project's declared Node/browser targets and current compatibility data.
  3. Can tooling bridge the gap? Syntax may need a transform; built-ins need a polyfill. One does not imply the other.

Workflow

  1. Inspect package.json (engines, browserslist, dependencies), runtime/deployment config, tsconfig.json, and Babel/bundler config. Do not invent a baseline.
  2. Select the simplest standardized feature supported by that baseline.
  3. For a newer standard or mature proposal, state the required runtime, transform, or polyfill in the implementation or handoff.
  4. Preserve observable behavior while modernizing: mutation, evaluation order, iterator consumption, concurrency, cancellation, property descriptors, and error propagation matter more than shorter syntax.
  5. Run the project's formatter, type-checker, linter, and tests. Exercise both the native path and compatibility path when both ship.

Read references/COMPATIBILITY.md whenever support is uncertain, the feature is newer than ES2023, or a proposal is involved.

Status Rules

  • Use the frozen ECMAScript 2026 specification for ES2026 attribution and the living draft for already-integrated future features.
  • Use TC39's finished-proposals table for Stage 4 publication-year mapping and its active tracker for current stages.
  • Do not infer an edition from the year a browser shipped a feature. Array.fromAsync, for example, shipped before its ES2026 publication.
  • Do not describe Stage 3 or 2.7 syntax as standard JavaScript. It can still be a sound project choice when a maintained toolchain implements the selected proposal revision.
  • Do not emit withdrawn Records & Tuples syntax (#{} / #[]).

Edition Map

This is a routing index, not a substitute for the detailed references.

| Edition | Representative additions | |---|---| | ES2015 | let/const, classes, modules, arrow functions, destructuring, promises, generators, Map/Set, Proxy | | ES2016 | exponentiation, Array.prototype.includes | | ES2017 | async functions, Object.values/entries, string padding, shared memory and Atomics | | ES2018 | object rest/spread, async iteration, Promise.prototype.finally, major RegExp additions | | ES2019 | flat/flatMap, Object.fromEntries, optional catch binding, stable sort | | ES2020 | optional chaining, nullish coalescing, BigInt, Promise.allSettled, dynamic import(), globalThis | | ES2021 | logical assignment, Promise.any, replaceAll, numeric separators, WeakRef | | ES2022 | .at, Object.hasOwn, top-level await, class fields/private elements/static blocks, error cause, RegExp indices | | ES2023 | array change-by-copy methods, findLast/findLastIndex, hashbang grammar, symbols as weak keys | | ES2024 | grouping, Promise.withResolvers, well-formed strings, RegExp v, resizable/transferable buffers, Atomics.waitAsync | | ES2025 | Set methods, sync iterator helpers, RegExp.escape, Promise.try, import attributes/JSON modules, RegExp modifiers, Float16 | | ES2026 | Array.fromAsync, Error.isError, Math.sumPrecise, Uint8Array base64/hex, Iterator.concat, Map/WeakMap upsert, JSON source access |

Already Stage 4 with publication expected in ES2027: Temporal, explicit resource management (using / await using), Atomics.pause, and Iterator.zip / zipKeyed. See references/UPCOMING.md.

Choose by Semantics

Collections

| Need | Prefer | Important behavior | |---|---|---| | Transform/filter an array | map, filter, flatMap | Eager; creates arrays | | Process a large/infinite sync source | iterator helpers | Lazy; consumes the iterator | | Find from the end | findLast, findLastIndex | Does not reverse or copy | | Change without mutating the source | toSorted, toReversed, toSpliced, with | Shallow copy | | Group under string/symbol keys | Object.groupBy | Returns a null-prototype object | | Group under identity keys | Map.groupBy | Preserves key identity | | Collect an async iterable | Array.fromAsync | Pulls and awaits sequentially | | Run already-created work concurrently | Promise.all | Rejects early; does not cancel inputs |

Do not replace mutation mechanically. A private, newly allocated array can be mutated efficiently without exposing mutable shared state. Use change-by-copy methods when preserving the source is part of the contract.

const ranked = scores.toSorted((a, b) => b.points - a.points);

const byTeam = Object.groupBy(players, player => player.team);
if (Object.hasOwn(byTeam, requestedTeam)) {
  render(byTeam[requestedTeam]);
}

Nullish and Property Checks

const city = user?.address?.city ?? 'Unknown';

// `??` preserves 0, false, and ''. Use `||` only when all falsy values mean absent.
settings.timeout ??= 5_000;

// Safe for overwritten or null-prototype objects.
if (Object.hasOwn(record, 'id')) use(record.id);

Async Work

// Independent work: concurrent.
const [user, permissions] = await Promise.all([
  fetchUser(id),
  fetchPermissions(id),
]);

// Ordered source: sequential consumption.
const pages = await Array.fromAsync(fetchPages());

// A loop is correct when each step depends on the previous one.
for (const migration of migrations) {
  await migration.run();
}

Never use forEach(async () => ...) when completion must be awaited. Read references/PROMISES.md for combinators and references/CONCURRENCY.md for bounded concurrency and cancellation.

Modules

Use with for import attributes; the older assert spelling is not current ECMAScript.

import config from './config.json' with { type: 'json' };

const locale = await import(`./locales/${language}.js`);

Import attributes and JSON modules are ES2025, but module loading remains host-sensitive. Verify Node, browser, bundler, and test-runner support together.

Exact API Boundaries

  • structuredClone, fetch, streams, AbortController, and DOM APIs are host/web APIs, not ECMA-262 language features.
  • Intl.* is standardized in ECMA-402, not ECMA-262.
  • TypeScript syntax and types are not JavaScript runtime features.
  • A TypeScript lib entry supplies type declarations, not a runtime implementation.
  • Babel syntax transforms do not automatically install missing globals or prototype methods.

Compatibility Paths

Prefer native support when the target has it. Otherwise choose deliberately:

| Feature kind | Compatibility mechanism | |---|---| | Syntax (using, decorators) | Babel/TypeScript transform that matches the selected proposal revision | | Built-in (Iterator.concat, Map#getOrInsert) | Runtime upgrade or maintained polyfill such as current core-js | | Namespace API (Temporal) | Runtime upgrade or a maintained Temporal polyfill | | Shared-memory primitive (Atomics.pause) | Native support only; feature-detect and keep a fallback algorithm |

TypeScript 7.0 is released, but it is not a polyfill and 7.0 has no stable programmatic compiler API. Projects whose framework/linter embeds TypeScript may need TypeScript 6 alongside it. See references/COMPATIBILITY.md.

Review Traps

  • .sort(), .reverse(), .splice(), Map#set, Set#add, and most TypedArray methods mutate. That is not automatically wrong; make it intentional.
  • Object/array spread is shallow and invokes ordinary property access; it does not preserve prototypes or descriptors.
  • structuredClone is a host API and cannot clone functions, weak collections, or every platform object.
  • Object.groupBy has no inherited hasOwnProperty or toString.
  • Iterator helpers are lazy and single-pass; arrays are reusable and eager.
  • Promise.all does not cancel remaining operations after rejection. Pass AbortSignal when cancellation is required.
  • Math.sumPrecise accurately sums the binary Number inputs; it does not make decimal money exact.
  • Error.isError is cross-realm-safe for ECMAScript Error objects; host error types can have runtime-specific behavior.
  • Temporal is not part of ES2026. Its Stage 4 publication target is ES2027.
  • Standard decorators are not legacy TypeScript experimentalDecorators.

References

Load only what the task needs.

| Reference | Use for | |---|---| | ES2016-ES2017.md | early annual additions, async functions, shared memory | | ES2018-ES2019.md | async iteration, object spread, RegExp, flattening | | ES2022-ES2023.md | classes, .at, change-by-copy, weak symbol keys | | ES2024.md | grouping, resolvers, Unicode sets, buffers | | ES2025.md | Set/iterator helpers, modules, RegExp, Float16 | | ES2026.md | all seven finalized ES2026 feature proposals | | UPCOMING.md | Stage 4 ES2027 set, selected mature proposals, withdrawn syntax | | COMPATIBILITY.md | runtime matrices, Babel/TypeScript/core-js/polyfill decisions | | PROMISES.md | promise semantics and anti-patterns | | CONCURRENCY.md | pools, retries, cancellation, async iteration | | CHEATSHEET.md | compact syntax lookup |

Authority Order

  1. Frozen annual ECMA-262 editions for edition membership
  2. Living ECMA-262 plus TC39 proposal tracker/spec repositories for future status and semantics
  3. Test262 for conformance cases
  4. MDN Browser Compatibility Data and official engine/runtime releases for availability
  5. Official TypeScript, Babel, and polyfill documentation for compatibility paths