
Project Discipline Guides
- 2 installs
- 921 repo stars
- Updated August 4, 2026
- googlechrome/modern-web-guidance-src
Refactors discipline-level guides like JavaScript or CSS to remove common knowledge by generating and comparing against model-specific knowledge mirrors.
About
Refactors discipline-level web guides such as JavaScript and CSS to strip out common knowledge using generated model-specific knowledge mirrors. A developer uses it to keep guidance focused on non-obvious content.
- Generates model-specific knowledge mirrors
- Removes common knowledge from discipline guides
Project Discipline Guides by the numbers
- 2 all-time installs (skills.sh)
- Ranked #1,294 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/googlechrome/modern-web-guidance-src --skill project-discipline-guidesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 921 |
| Last updated | August 4, 2026 |
| Repository | googlechrome/modern-web-guidance-src ↗ |
What it does
Refactors discipline-level guides like JavaScript or CSS to remove common knowledge by generating and comparing against model-specific knowledge mirrors.
Files
Project Discipline Guides
This skill facilitates the "Differential Knowledge Refactor" of technical guides. It ensures that SKILL.md files for specific disciplines are lean, context-efficient, and strictly additive to what modern AI models already know natively.
Workflow: The Inverse Knowledge Filter
The goal is to aggressively "whittle down" a target guide (the Target) by removing any content that is natively understood by modern coding models.
1. Generate Knowledge Mirrors
Use the provided script to generate "Redundancy Mirrors"—comprehensive guides of what Gemini, Claude, and Codex consider "Common Knowledge" for a given discipline, and intersect them into a single LCD mirror.
# Ensure that model setup is configured (instructions are in the top level readme)
node .agents/skills/project-discipline-guides/scripts/generate_mirrors.ts <discipline_name>This will create files inside the .agents/skills/project-discipline-guides/mirrors/ directory:
mirrors/<discipline>/gemini_mirror.md(via Gemini - temporary)mirrors/<discipline>/claude_mirror.md(via Claude - temporary)mirrors/<discipline>/codex_mirror.md(via Codex - temporary)mirrors/<discipline>/mirror.md(Unified Redundancy Mirror intersection)
[!IMPORTANT]
Ensure that the unified Redundancy Mirror (.agents/skills/project-discipline-guides/mirrors/<discipline>/mirror.md) has been successfully generated. This mirror represents the exact mathematical intersection of all three source models, preventing pruning errors and reducing context window overhead.2. Perform the Inverse Filter (Agent Task)
As the agent, read the Target guide and the unified Redundancy Mirror (.agents/skills/project-discipline-guides/mirrors/<discipline>/mirror.md).
- Strict A - B Comparison: Compare rules in the Target guide strictly against the unified Redundancy Mirror. You should ONLY remove a rule or pattern from the Target guide if it is fully and clearly covered in the Redundancy Mirror.
- No Subjective Self-Attestation: Rely exclusively on the literal content present in the Redundancy Mirror. Do not prune rules based on your own subjective self-attestation of what you "know" or would do by default. If a concept is not in the Redundancy Mirror, it remains in the Target guide.
- Avoid Blind Bullet-Point Deletions: If a single bullet point or rule in the Target contains multiple guidelines or APIs, do not delete the entire bullet point just because some parts of it are common knowledge. Carefully split or dissect the rule, pruning only the redundant parts and explicitly retaining any differential knowledge.
- Preserve Differential Knowledge: Keep only what is unique to this project or necessary to guide the AI effectively. This generally includes:
- Rules that counter common AI biases.
- Conventions chosen among valid alternatives.
- Advanced performance patterns, security constraints, or heuristics that models know but often omit in default outputs.
[!WARNING]
Keep the Refactor Scoped: The primary and sole goal of this skill is reduction of redundant common knowledge. Do not add new rules, modify logic, or introduce unrelated guidelines in this refactoring pass. Keep the changes highly focused and easy to review.
3. Apply Surgical Edits
Perform the cleanup on the Target file. Aim for surgical edits that preserve the file's structure while removing unnecessary content.
In your response, provide an explanation of the changes made and the rationale for what was removed or kept.
4. Verification Step
After applying edits, perform a self-verification:
- Contrast Check: Verify that none of the rules in the new guide are present in the Redundancy Mirror.
- Preservation Check: Ensure that critical project-specific rules, behavioral steering, and any specific advanced APIs (such as
Object.groupBy()) were not accidentally removed.
Core Principles
- Delete Redundancies: If a model natively "knows" a rule, it does not belong in the skill.
- Preserve Signal: Keep instructions that force the model into "Senior Engineer" mode or align with specific project architecture.
- Context Efficiency: A lean skill is a fast and cheap skill.
You are an expert technical editor. You are given three "Knowledge Mirrors" of standard development practices for {{ discipline }}. These mirrors were generated by different models (Gemini, Claude, Codex) to represent what they natively consider "Common Knowledge".
Your task is to generate a single, unified "Lowest Common Denominator (LCD) Mirror" (the intersection of the three).
Rules for Intersection: 1. Strict Inclusion: ONLY include best practices, syntax, APIs, and guidelines that are clearly and consistently represented in ALL THREE source mirrors. 2. Strict Exclusion: If a concept, API, or rule is missing, omitted, or not natively understood in ANY ONE of the three source mirrors, you must NOT include it in the LCD mirror. For example, if a modern feature (such as Object.groupBy()) is present in the Claude mirror but not covered in the Gemini mirror, exclude it completely. 3. Rigorous Evidence: Be extremely literal. Do not synthesize, extrapolate, or assume any knowledge that is not explicitly evidenced in all three documents. 4. Formatting: Organize the LCD mirror cleanly with clear headers and bullet points matching standard technical conventions.
Below are the three source mirrors:
---
Source Mirror 1: Gemini
{{ gemini_mirror }}
---
Source Mirror 2: Claude
{{ claude_mirror }}
---
Source Mirror 3: Codex
{{ codex_mirror }}
Based on your training data and inherent knowledge of {{ discipline }} development, generate a comprehensive guide of standard best practices, syntax, and APIs that you natively understand and would apply by default.
Focus on: 1. The guide should be based on the most modern language features you're aware of, as long as it has shipped in every major browser, even if only recently. 2. You can also recommend cutting edge features that have only shipped in one browser, as long as they are progressive enhancement or have good fallbacks/polyfills. 3. Standard library/API usage. 4. Common clean code principles.
This guide will be used as a "Redundancy Mirror" to prune a project-specific skill file. We want to see exactly what you consider "Common Knowledge" so we can delete it from our local guides, so be as exhaustive as you can. Do not omit guidance for conciseness, do not prioritize.
[!IMPORTANT]
Answer strictly from inherent knowledge: Do not use any tools (such as file reading, directory listings, shell command execution, or web search). Do not read, inspect, or reference any local files in the workspace or repository. You must generate this guide based solely on your pre-trained knowledge.
JavaScript Best Practices, Syntax, and APIs — Common Knowledge Guide
1. Variable Declarations
Use const by default, let when reassigning, never var
const MAX_RETRIES = 3; // Immutable binding
let counter = 0; // Mutable binding
counter += 1;
// Avoid:
var x = 5; // Function-scoped, hoisted, no TDZconstandletare block-scoped.constprevents reassignment, not mutation (const arr = []; arr.push(1)is fine).varshould be avoided due to hoisting quirks and function scoping.- Temporal Dead Zone (TDZ):
let/constcannot be accessed before declaration.
Naming conventions
camelCasefor variables and functions.PascalCasefor classes and constructors.SCREAMING_SNAKE_CASEfor true constants (module-level primitives)._prefixhistorically signaled "private"; prefer#privateFieldin classes now.
2. Strict Equality and Type Coercion
Always use === and !==
if (value === null) { ... }
if (count !== 0) { ... }
// Avoid:
if (value == null) // Loose equality (though == null is one common exception)==performs type coercion with surprising results ([] == false,'' == 0).- The single legitimate use of
==isvalue == nullto check bothnullandundefined, butvalue === null || value === undefinedis clearer.
Object.is() for special cases
Object.is(NaN, NaN); // true (=== returns false)
Object.is(0, -0); // false (=== returns true)3. Nullish Handling
Nullish coalescing ??
const port = config.port ?? 3000; // Falls back only on null/undefined
const name = input || 'default'; // Falls back on '', 0, false tooOptional chaining ?.
const city = user?.address?.city;
const result = obj?.method?.();
const item = arr?.[0];Logical assignment operators
a ??= b; // a = a ?? b
a ||= b; // a = a || b
a &&= b; // a = a && b4. Functions
Arrow functions for callbacks and lexical this
const double = (x) => x * 2;
const add = (a, b) => a + b;
const log = () => console.log('hi');
[1, 2, 3].map((n) => n * 2);- Arrow functions don't have their own
this,arguments,super, ornew.target. - Cannot be used as constructors.
- Use traditional
functiondeclarations for methods that needthis, or for hoisted top-level functions.
Default parameters
function greet(name = 'World', greeting = 'Hello') {
return `${greeting}, ${name}!`;
}Rest and spread
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
const merged = [...arr1, ...arr2];
const cloned = { ...original, override: true };
fn(...args);Avoid arguments object
Use rest parameters (...args) instead — they're real arrays.
5. Destructuring
// Object destructuring
const { name, age, email = 'n/a' } = user;
const { name: userName } = user; // Rename
const { a, ...rest } = obj; // Rest
// Array destructuring
const [first, second, ...others] = list;
const [, , third] = list; // Skip
[a, b] = [b, a]; // Swap
// Nested
const { address: { city } } = user;
// In parameters
function render({ title, body, author = 'Anon' }) { ... }6. Template Literals
const greeting = `Hello, ${name}!`;
const multiline = `
Line 1
Line 2
`;
// Tagged templates
const html = tag`<div>${value}</div>`;- Always prefer template literals over string concatenation.
7. Objects
Shorthand syntax
const x = 1, y = 2;
const point = { x, y }; // Property shorthand
const obj = {
greet() { return 'hi'; }, // Method shorthand
[`dynamic_${key}`]: value, // Computed property names
};Object methods
Object.keys(obj);
Object.values(obj);
Object.entries(obj);
Object.fromEntries(entries);
Object.assign(target, ...sources); // Prefer { ...spread } usually
Object.freeze(obj); // Shallow immutability
Object.hasOwn(obj, 'key'); // Modern replacement for hasOwnPropertyIterate with Object.entries
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}8. Arrays
Prefer immutable, functional methods
arr.map(fn);
arr.filter(fn);
arr.reduce(fn, initial);
arr.find(fn);
arr.findIndex(fn);
arr.findLast(fn);
arr.findLastIndex(fn);
arr.some(fn);
arr.every(fn);
arr.flat(depth);
arr.flatMap(fn);
arr.includes(value);
arr.at(-1); // Negative indexingImmutable counterparts to mutating methods (modern)
arr.toSorted(); // Non-mutating sort
arr.toReversed(); // Non-mutating reverse
arr.toSpliced(start, deleteCount, ...items);
arr.with(index, value); // Replace at indexArray creation
Array.from(iterable);
Array.from({ length: 5 }, (_, i) => i);
Array.of(1, 2, 3);
[...iterable]; // Often the cleanestAvoid for...in for arrays
Use for...of, forEach, or index loops. for...in iterates enumerable properties (including inherited ones).
9. Iteration
// for...of: values from any iterable
for (const item of iterable) { ... }
// for...in: keys from object (rarely the right choice)
for (const key in obj) {
if (Object.hasOwn(obj, key)) { ... }
}
// Classic for: when you need index control
for (let i = 0; i < arr.length; i++) { ... }
// Entries for index + value
for (const [i, value] of arr.entries()) { ... }10. Maps and Sets
const map = new Map();
map.set(key, value);
map.get(key);
map.has(key);
map.delete(key);
map.size;
for (const [k, v] of map) { ... }
const set = new Set([1, 2, 3]);
set.add(4);
set.has(2);
set.delete(1);
[...new Set(arr)]; // Deduplicate
// WeakMap / WeakSet for keys held weakly (no enumeration, GC-friendly)
const cache = new WeakMap();Use Map over plain objects when:
- Keys aren't strings/symbols.
- You need ordered iteration.
- You need frequent add/delete operations.
- You need a known size.
11. Async / Await and Promises
Prefer async/await over .then() chains
async function loadUser(id) {
try {
const res = await fetch(`/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error('Failed to load user', err);
throw err;
}
}Parallel awaits
// Sequential (slow)
const a = await fetchA();
const b = await fetchB();
// Parallel (fast)
const [a, b] = await Promise.all([fetchA(), fetchB()]);Promise combinators
Promise.all(promises); // All succeed or first rejection
Promise.allSettled(promises); // Wait for all, regardless
Promise.race(promises); // First to settle
Promise.any(promises); // First to fulfillTop-level await
Available in ES modules. No need to wrap in an IIFE.
Async iteration
for await (const chunk of stream) { ... }
async function* generate() {
yield await fetch(...);
}Never forget to await
Floating promises swallow errors. Use void promise to explicitly ignore, or await it.
12. Error Handling
Throw Error instances
throw new Error('Descriptive message');
throw new TypeError('Expected number');
// Custom errors
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}Error.cause for chaining
try {
doThing();
} catch (err) {
throw new Error('Higher-level failure', { cause: err });
}try/catch without binding
try { ... } catch { ... } // Optional bindingAggregateError for multiple errors
Returned by Promise.any when all fail.
13. Modules (ESM)
// Named exports
export function foo() {}
export const BAR = 1;
export { existing as renamed };
// Default export
export default class App {}
// Imports
import App, { foo, BAR as CONST } from './module.js';
import * as utils from './utils.js';
// Dynamic imports (returns a Promise)
const mod = await import('./lazy.js');
// Re-exports
export { foo } from './other.js';
export * from './other.js';- Prefer named exports; they aid refactoring and tree-shaking.
- Use
.jsextensions in import paths for native ESM. - One responsibility per module.
14. Classes
class Counter {
#count = 0; // Private field
static instances = 0; // Static field
constructor(start = 0) {
this.#count = start;
Counter.instances++;
}
get value() { return this.#count; } // Getter
set value(v) { this.#count = v; } // Setter
increment() { this.#count++; return this; }
static create(start) { // Static method
return new Counter(start);
}
#privateMethod() { ... } // Private method
}
class Timer extends Counter {
constructor() {
super(0);
}
}- Use
#fieldfor true privacy (enforced by the engine). - Prefer composition over inheritance.
- Don't add methods to prototypes manually — use
classsyntax.
15. Symbols and Well-Known Symbols
const id = Symbol('id');
obj[id] = 123;
// Well-known symbols enable customization
class Range {
*[Symbol.iterator]() { yield 1; yield 2; }
}16. Generators and Iterators
function* range(start, end) {
for (let i = start; i < end; i++) yield i;
}
for (const n of range(0, 5)) { ... }
[...range(0, 5)];17. Numbers, BigInt, Math
Number.isInteger(x);
Number.isFinite(x);
Number.isNaN(x); // Reliable, unlike global isNaN
Number.parseInt(str, 10); // Always pass radix
Number.parseFloat(str);
Number.EPSILON;
Number.MAX_SAFE_INTEGER;
// Numeric separators for readability
const million = 1_000_000;
// BigInt for arbitrary precision integers
const big = 9007199254740993n;
big + 1n;
Math.trunc(x);
Math.sign(x);
Math.hypot(a, b);
Math.clz32(x);Floating-point comparison
Math.abs(a - b) < Number.EPSILON;18. Strings
str.startsWith(prefix);
str.endsWith(suffix);
str.includes(sub);
str.padStart(targetLength, padChar);
str.padEnd(targetLength, padChar);
str.repeat(n);
str.trim();
str.trimStart();
str.trimEnd();
str.replaceAll(search, replacement);
str.at(-1);
str.normalize('NFC'); // Unicode normalization
str.matchAll(regex); // Returns iteratorString iteration (Unicode-aware)
[...'😀'].length; // Correctly handles surrogates
for (const char of str) { ... }19. Regular Expressions
const re = /pattern/giu; // u flag for Unicode
// Named capture groups
const { groups: { year, month } } = '2026-05'.match(/(?<year>\d{4})-(?<month>\d{2})/);
// Lookbehind
/(?<=\$)\d+/
// Sticky flag
/foo/y- Use
uflag for Unicode-correct matching. - Use
sflag (dotAll) when.should match newlines. - Prefer
String.matchAlloverRegExp.execloops.
20. JSON
JSON.stringify(value, null, 2); // Pretty-print with 2-space indent
JSON.stringify(value, replacer);
JSON.parse(text, reviver);- Wrap
JSON.parsein try/catch when parsing untrusted input. undefined, functions, and Symbols are dropped during stringify.
21. Dates
const now = new Date();
now.toISOString(); // '2026-05-12T...'
Date.now(); // Epoch ms- For non-trivial date logic, prefer libraries (date-fns, dayjs, Luxon) or the upcoming
TemporalAPI where available. Intl.DateTimeFormatfor locale-aware formatting:
new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' }).format(date);22. Internationalization (Intl)
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(1234.5);
new Intl.RelativeTimeFormat('en').format(-3, 'day'); // '3 days ago'
new Intl.ListFormat('en').format(['A', 'B', 'C']); // 'A, B, and C'
new Intl.PluralRules('en').select(2); // 'other'
new Intl.Collator('en').compare('a', 'b');
new Intl.Segmenter('en', { granularity: 'word' });23. Fetch API
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
signal: controller.signal,
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();fetchonly rejects on network errors — always checkres.ok.- Use
AbortControllerfor cancellation and timeouts:
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
fetch(url, { signal: controller.signal });
// Or built-in:
fetch(url, { signal: AbortSignal.timeout(5000) });24. URL and URLSearchParams
const url = new URL('/path', 'https://example.com');
url.searchParams.set('q', 'hello');
url.searchParams.append('tag', 'js');
url.toString();
const params = new URLSearchParams(window.location.search);
params.get('id');Don't manually concatenate query strings.
25. DOM (Browser)
Selection
document.querySelector('.btn');
document.querySelectorAll('li');
element.closest('.container');
element.matches('.active');Manipulation
element.classList.add('active');
element.classList.toggle('open', isOpen);
element.dataset.userId; // Reads data-user-id
element.replaceChildren(...nodes);
element.append(child); // Accepts strings & nodes
element.prepend(child);
element.before(node);
element.after(node);
element.remove();Events
element.addEventListener('click', handler, { once: true, passive: true, signal });
// Use AbortController to remove multiple listeners at once
const controller = new AbortController();
el.addEventListener('click', h, { signal: controller.signal });
controller.abort(); // Removes them all
// Event delegation
container.addEventListener('click', (e) => {
const btn = e.target.closest('button');
if (!btn) return;
...
});Avoid innerHTML with untrusted input (XSS risk)
Prefer textContent, append, or sanitize with a library.
26. Web APIs (Common)
// Storage
localStorage.setItem('key', JSON.stringify(value));
JSON.parse(localStorage.getItem('key'));
sessionStorage;
// Timers
setTimeout(fn, ms);
setInterval(fn, ms);
queueMicrotask(fn); // Microtask queue
requestAnimationFrame(fn); // Before next paint
requestIdleCallback(fn); // When idle
// Crypto
crypto.randomUUID();
crypto.getRandomValues(new Uint8Array(16));
await crypto.subtle.digest('SHA-256', data);
// Observers
new IntersectionObserver(cb).observe(el);
new ResizeObserver(cb).observe(el);
new MutationObserver(cb).observe(el, { childList: true });
// Structured cloning
const clone = structuredClone(obj); // Deep clone, handles cycles, Maps, etc.27. Modern Cloning and Equality
structuredClone(value)for deep clones (no JSON round-trip needed).{ ...obj }and[...arr]for shallow clones.- Reference equality only — no built-in deep equality.
28. Typing and Documentation
JSDoc for type hints in plain JS
/**
* @param {string} name
* @param {number} [age]
* @returns {Promise<User>}
*/
async function findUser(name, age) { ... }- Editors (VS Code) understand JSDoc and provide IntelliSense.
- For larger projects, consider TypeScript.
29. Clean Code Principles
Naming
- Use intention-revealing names:
daysSinceLogin, notd. - Booleans read as questions:
isReady,hasPermission,canEdit. - Functions are verbs:
getUser,renderList,parseJSON. - Avoid abbreviations; favor clarity over brevity.
- Plural for collections (
users), singular for items (user).
Functions
- Single Responsibility: one function, one reason to change.
- Keep functions small; extract when they do >1 thing.
- Prefer pure functions: same input → same output, no side effects.
- Limit parameters (≤3); use options objects when more are needed.
- Avoid boolean flag parameters that change behavior — split into two functions.
- Return early; avoid deep nesting.
// Guard clauses
function process(user) {
if (!user) return null;
if (!user.active) return null;
...
}Avoid magic numbers and strings
const MAX_LOGIN_ATTEMPTS = 5;
const STATUS_PENDING = 'pending';Immutability
- Don't mutate function arguments.
- Prefer non-mutating array methods (
map,filter,toSorted). - Treat data as immutable; return new objects.
Composition over inheritance
- Small reusable functions composed together generally beat deep class hierarchies.
- Mixins via plain objects/functions when sharing behavior.
Don't repeat yourself — but don't over-abstract
- Three similar usages is better than a premature abstraction.
- Wait until the right shape is obvious.
Comments
- Code should be self-documenting.
- Comments explain why, not what.
- Delete commented-out code — version control remembers.
- Use
// TODO:and// FIXME:sparingly; track in an issue tracker.
Error handling
- Fail fast at boundaries (validate input, throw early).
- Don't swallow errors; either handle or rethrow.
- Use specific error types so callers can discriminate.
try/catchshould wrap the smallest possible block.
Avoid global state
- Use modules; export only what's needed.
- Side effects belong at edges, not in pure logic.
Avoid eval, Function constructor, and with
Security hazards and prevent optimization.
30. Performance Defaults
- Cache repeated DOM lookups in variables.
- Batch DOM writes (use
DocumentFragmentorreplaceChildren). - Debounce/throttle high-frequency events (input, scroll, resize).
- Use
passive: trueon touch/wheel listeners that don'tpreventDefault. - Lazy-load with dynamic
import(). - Use
Map/Setover arrays for membership lookups. - Memoize pure expensive computations.
- Avoid premature optimization — measure first (
performance.now(), DevTools).
31. Security Defaults
- Never trust user input — validate and sanitize.
- Use parameterized queries; never string-concat SQL.
- Avoid
innerHTMLwith untrusted content; prefertextContent. - Use
crypto.randomUUID()andcrypto.getRandomValues— neverMath.randomfor tokens/IDs requiring uniqueness or unpredictability. - Set
Content-Security-Policy,X-Content-Type-Options, etc. on the server. - Use
rel="noopener noreferrer"ontarget="_blank"links. - Hash passwords with bcrypt/argon2 server-side; never roll your own crypto.
32. Testing Hygiene
- Test behavior, not implementation.
- Arrange / Act / Assert structure.
- One logical assertion per test.
- Descriptive test names:
it('returns null when user is inactive'). - Avoid shared mutable state between tests.
- Mock at module boundaries; prefer real objects when possible.
33. Tooling Defaults
- Use a formatter (Prettier) — don't bikeshed style.
- Use a linter (ESLint) with sensible defaults.
- Pin dependency versions in
package-lock.json/pnpm-lock.yaml. - Use
"type": "module"inpackage.jsonfor native ESM. - Use
node:prefix for Node built-ins:import fs from 'node:fs/promises';.
34. Async Patterns Worth Knowing
Sequential async with reduce
await items.reduce(async (prev, item) => {
await prev;
await process(item);
}, Promise.resolve());Concurrent with limit
Often via libraries (p-limit); avoid hand-rolling.
Avoid async in forEach
forEach ignores returned promises. Use for...of + await, or Promise.all(arr.map(async ...)).
35. Common Pitfalls to Avoid
- Mutating a parameter (
function(arr) { arr.push(...) }). - Forgetting
awaitbefore an async call. - Using
==instead of===. - Using
for...inon arrays. - Calling
.lengthrepeatedly on huge arrays inside loops. - Confusing
nullandundefined(preferundefinedfor "not set"; reservenullfor "intentional empty"). - Using
parseIntwithout radix. - Capturing loop variables with
varin closures (uselet). - Returning from inside
forEachexpecting it to break (it doesn't). - Comparing objects/arrays with
===(reference equality only). - Mixing tabs and spaces.
- Leaking secrets into client-side code.
36. Modern Syntax Quick Reference
// Numeric separators
1_000_000
// Logical assignment
a ??= b; a ||= b; a &&= b;
// Optional chaining
obj?.prop?.method?.()
// Nullish coalescing
value ?? fallback
// Object spread
{ ...a, ...b }
// Top-level await (in modules)
const data = await fetch(url).then(r => r.json());
// Private class fields
class C { #x = 0; }
// Static blocks
class C { static { /* init */ } }
// Error cause
new Error('msg', { cause: err })
// Array.prototype.at
arr.at(-1)
// Object.hasOwn
Object.hasOwn(obj, key)
// String.prototype.replaceAll
str.replaceAll('a', 'b')
// Array immutable methods
arr.toSorted(); arr.toReversed(); arr.with(i, v);
// structuredClone
structuredClone(obj)---
This represents the baseline of "common JavaScript knowledge" I apply by default — modern (ES2020+) features that have shipped across all evergreen browsers, plus standard clean-code conventions widely recognized in the JS ecosystem.
Below is a redundancy mirror of JavaScript knowledge I would treat as broadly standard, default, and generally “common knowledge” when writing modern JavaScript.
JavaScript Redundancy Mirror
Baseline Assumptions
Modern JavaScript should generally mean:
- ECMAScript modules by default.
letandconst, notvar.- Strict-mode semantics via modules.
- Promises and
async/await. - Modern collection types:
Map,Set,WeakMap,WeakSet. - Modern object and array APIs.
- Browser-native DOM APIs rather than legacy libraries for basic DOM work.
- Feature detection for APIs that may not exist everywhere.
- Progressive enhancement for newer platform features.
- Avoiding transpilation unless project constraints require older browser support.
- Avoiding polyfills unless they are necessary and intentionally scoped.
JavaScript code should be clear, predictable, side-effect-conscious, and written for maintainability rather than cleverness.
Language Basics
Use const by default.
const value = computeValue();Use let when reassignment is required.
let count = 0;
count += 1;Avoid var.
Use semicolons consistently if the project uses them. If not, understand ASI hazards and avoid ambiguous leading tokens like (, [, /, +, and - at statement boundaries.
Use strict equality by default.
if (id === selectedId) {}
if (value !== null) {}Avoid == and != except for the deliberate value == null pattern when checking both null and undefined.
if (value == null) {
// null or undefined
}Prefer explicit boolean logic over truthiness when empty string, 0, or false are valid values.
if (name !== "") {}
if (count > 0) {}
if (enabled === true) {}Use template literals for interpolation and multiline strings.
const message = `Hello, ${name}`;Use numeric separators for readability.
const timeout = 10_000;
const maxBytes = 5_242_880;Use BigInt only when integer precision beyond Number.MAX_SAFE_INTEGER is required.
const id = 9007199254740993n;Do not mix BigInt and Number without explicit conversion.
Variables And Scope
Prefer the narrowest possible scope.
if (shouldRun) {
const result = run();
}Avoid mutable shared state.
Avoid reusing variables for different meanings.
const user = getUser();
const profile = getProfile(user.id);Avoid assigning to undeclared variables.
Avoid global variables. If global state is necessary, isolate it behind a module or explicit API.
Prefer named constants for meaningful magic values.
const MAX_RETRIES = 3;
const RETRY_DELAY_MS = 250;Functions
Prefer small, single-purpose functions.
function formatUserName(user) {
return `${user.firstName} ${user.lastName}`;
}Use function declarations for top-level named functions when hoisting is useful.
function parseConfig(input) {
return JSON.parse(input);
}Use arrow functions for callbacks and small expressions.
items.map((item) => item.id);Do not use arrow functions when dynamic this, arguments, or constructor behavior is needed.
Use default parameters instead of manual fallback logic.
function createUser({ role = "user" } = {}) {}Be careful with default values: they only apply to undefined, not null.
function greet(name = "Guest") {}
greet(null); // name is nullPrefer object parameters for functions with multiple optional values.
function createRequest({ method = "GET", headers = {}, body } = {}) {}Avoid boolean parameter flags when they make call sites unclear.
// Less clear
renderUser(user, true);
// Clearer
renderUser(user, { compact: true });Return early to reduce nesting.
function getLabel(value) {
if (value == null) return "";
if (value === "") return "Empty";
return String(value);
}Prefer pure functions where practical.
Avoid functions that both compute a value and mutate external state unless that is clearly their purpose.
Use rest parameters instead of arguments.
function sum(...values) {
return values.reduce((total, value) => total + value, 0);
}Use spread syntax for argument expansion.
Math.max(...values);Objects
Use object literals for plain objects.
const user = {
id,
name,
active: true,
};Use property shorthand.
const id = "123";
const name = "Ada";
const user = { id, name };Use method shorthand.
const service = {
start() {},
stop() {},
};Use computed property names when needed.
const field = "email";
const data = {
[field]: value,
};Use destructuring for clear extraction.
const { id, name } = user;Use defaults with destructuring.
const { role = "user" } = user;Use renaming when names conflict.
const { id: userId } = user;Avoid destructuring so deeply that readability suffers.
Prefer optional chaining for safe property access.
const city = user.address?.city;Prefer nullish coalescing for defaults where 0, false, or "" are valid.
const limit = options.limit ?? 20;Avoid using || for defaults unless all falsy values should trigger the fallback.
const label = input || "Untitled";Use object spread for shallow copies and updates.
const nextUser = {
...user,
name: "Grace",
};Remember object spread is shallow.
Avoid mutating input objects unless mutation is explicit and documented.
Use Object.freeze() for shallow immutability when appropriate.
Use Object.assign() when that is clearer or needed for target mutation.
Object.assign(target, source);Use Object.keys(), Object.values(), and Object.entries() for object iteration.
for (const [key, value] of Object.entries(record)) {}Use Object.fromEntries() to build objects from key-value pairs.
const byId = Object.fromEntries(users.map((user) => [user.id, user]));Use Object.hasOwn() instead of obj.hasOwnProperty().
if (Object.hasOwn(config, "timeout")) {}Avoid relying on property enumeration order unless the behavior is specifically defined and appropriate.
Use structuredClone() for deep cloning supported data types.
const copy = structuredClone(value);Do not use JSON.parse(JSON.stringify(value)) as a general deep clone because it loses types and fails on unsupported values.
Arrays
Use array literals.
const items = [];Use Array.from() to create arrays from iterables or array-like values.
const nodes = Array.from(document.querySelectorAll(".item"));Use spread for shallow copies.
const copy = [...items];Use map() for one-to-one transformations.
const ids = users.map((user) => user.id);Use filter() for selection.
const activeUsers = users.filter((user) => user.active);Use find() for the first matching item.
const selected = users.find((user) => user.id === id);Use some() and every() for predicates.
const hasErrors = fields.some((field) => field.error);
const allValid = fields.every((field) => field.valid);Use reduce() when it genuinely expresses accumulation, but avoid overly clever reducers.
const total = items.reduce((sum, item) => sum + item.price, 0);Prefer simple loops when they are clearer.
const results = [];
for (const item of items) {
if (!item.active) continue;
results.push(transform(item));
}Use flat() and flatMap() for flattening.
const tags = posts.flatMap((post) => post.tags);Use includes() instead of indexOf(...) !== -1.
if (allowedRoles.includes(role)) {}Use at() for relative indexing.
const last = items.at(-1);Use modern non-mutating array methods where available:
const sorted = items.toSorted((a, b) => a.name.localeCompare(b.name));
const reversed = items.toReversed();
const next = items.with(index, updatedItem);
const trimmed = items.toSpliced(index, 1);Use mutating methods intentionally:
items.push(item);
items.sort(compare);
items.splice(index, 1);Do not mutate arrays passed into functions unless mutation is the explicit contract.
Always provide a comparator for numeric sorting.
numbers.toSorted((a, b) => a - b);Do not rely on default sort for numbers.
[10, 2, 1].sort(); // lexical, not numericUse stable identifiers as keys when rendering lists in UI frameworks; do not use indexes when order can change.
Use Array.isArray() instead of instanceof Array.
if (Array.isArray(value)) {}Strings
Use trim(), trimStart(), and trimEnd() for whitespace cleanup.
const normalized = input.trim();Use startsWith(), endsWith(), and includes().
if (path.startsWith("/api/")) {}Use replaceAll() for simple global replacement.
const slug = title.toLowerCase().replaceAll(" ", "-");Use regular expressions for pattern-based replacement.
const slug = title.toLowerCase().replace(/\s+/g, "-");Use padStart() and padEnd() for formatting.
const minutes = String(date.getMinutes()).padStart(2, "0");Use Intl.Collator or localeCompare() for human-facing sorting.
const collator = new Intl.Collator(undefined, { sensitivity: "base" });
names.toSorted((a, b) => collator.compare(a, b));Use Unicode-aware approaches when user-facing text matters. Avoid assuming .length equals visual character count.
Numbers And Math
Use Number.isNaN() instead of global isNaN().
if (Number.isNaN(value)) {}Use Number.isFinite() instead of global isFinite().
if (Number.isFinite(value)) {}Use Number.isInteger() and Number.isSafeInteger() when appropriate.
if (!Number.isSafeInteger(id)) {}Use Number.parseInt() and Number.parseFloat().
const count = Number.parseInt(input, 10);Always pass radix to parseInt.
Use Math.trunc(), Math.round(), Math.floor(), and Math.ceil() intentionally.
Use Math.min() and Math.max() with spread for reasonable array sizes.
const max = Math.max(...values);Avoid floating-point equality for decimal calculations.
Math.abs(a - b) < Number.EPSILON;Do not use JavaScript floating-point arithmetic for exact money math without a deliberate integer, decimal, or library strategy.
Dates And Time
Use Date for basic timestamps and interoperability.
const now = new Date();Use ISO 8601 strings for serialization.
const value = new Date().toISOString();Use epoch milliseconds for simple comparisons.
if (end.getTime() > start.getTime()) {}Avoid parsing ambiguous date strings.
new Date("2026-05-12T12:00:00Z");Prefer explicit time zones for user-facing date/time behavior.
Use Intl.DateTimeFormat for localized formatting.
const formatter = new Intl.DateTimeFormat(undefined, {
dateStyle: "medium",
timeStyle: "short",
});Be careful with Date month indexes: months are zero-based in constructor overloads.
Avoid manual date math where time zones and daylight saving time matter.
Use Temporal when available or via polyfill for robust date/time modeling, especially as a progressive enhancement or in environments where it is supported.
Regular Expressions
Use regex literals for static patterns.
const emailLike = /\S+@\S+\.\S+/;Use RegExp constructor for dynamic patterns and escape user input before interpolation.
Use named capture groups for clarity.
const match = input.match(/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/);Use the u flag for Unicode-aware matching when appropriate.
const pattern = /\p{Letter}+/gu;Use matchAll() for repeated captures.
for (const match of text.matchAll(/#(\w+)/g)) {}Avoid complex regexes when parsing would be clearer and safer.
Modules
Use ECMAScript modules.
export function parse() {}
export const VERSION = "1.0.0";import { parse } from "./parse.js";Prefer named exports for most shared utilities.
Use default exports when a module has one primary concept.
Avoid circular dependencies.
Keep module boundaries clear.
Avoid modules with large hidden side effects.
Use dynamic import() for lazy loading.
const { renderChart } = await import("./chart.js");Use import assertions/attributes where required by the runtime for JSON or other module types, subject to support.
Use top-level await in modules only when it is appropriate and does not unnecessarily block loading.
Classes And Prototypes
Use classes when modeling stateful entities with behavior.
class Store {
#items = [];
add(item) {
this.#items.push(item);
}
get items() {
return [...this.#items];
}
}Use private fields for internal state.
class Counter {
#count = 0;
increment() {
this.#count += 1;
}
}Use static methods for class-level helpers.
class User {
static fromJSON(value) {
return new User(value);
}
}Avoid deep inheritance hierarchies.
Prefer composition over inheritance.
Use extends for real subtype relationships.
Avoid modifying built-in prototypes.
Avoid relying on this in callbacks unless intentionally bound.
button.addEventListener("click", this.handleClick.bind(this));or:
handleClick = () => {};where class field syntax is supported by the project toolchain/runtime.
Error Handling
Throw Error objects, not strings.
throw new Error("Invalid user ID");Use custom error classes when callers need to distinguish error types.
class ValidationError extends Error {
constructor(message, details) {
super(message);
this.name = "ValidationError";
this.details = details;
}
}Preserve causes with cause.
throw new Error("Failed to load config", { cause: error });Use try / catch around operations that can fail and that you can meaningfully handle.
try {
return await loadUser(id);
} catch (error) {
logger.error(error);
return null;
}Do not swallow errors silently.
Avoid catching errors only to rethrow them unchanged.
Use finally for cleanup.
try {
await lock.acquire();
} finally {
lock.release();
}Validate external inputs at boundaries.
Fail early when required invariants are missing.
Make error messages actionable.
Do not expose sensitive details in user-facing errors.
Promises And Async
Use async / await for asynchronous control flow.
async function loadData() {
const response = await fetch("/api/data");
return response.json();
}Always handle promise rejections.
loadData().catch(reportError);Use Promise.all() for independent concurrent work where all must succeed.
const [user, posts] = await Promise.all([
fetchUser(id),
fetchPosts(id),
]);Use Promise.allSettled() when all outcomes matter.
const results = await Promise.allSettled(tasks);Use Promise.race() for first-settled behavior.
Use Promise.any() for first-fulfilled behavior.
Avoid await in a loop when operations can run concurrently.
const results = await Promise.all(items.map(processItem));Use sequential await in loops when order, rate limits, or dependencies matter.
for (const item of items) {
await processItem(item);
}Do not use Array.prototype.forEach() with async callbacks when awaiting completion is needed.
// Avoid
items.forEach(async (item) => {
await processItem(item);
});Use for...of or Promise.all.
Use AbortController for cancellable async operations.
const controller = new AbortController();
fetch(url, { signal: controller.signal });
controller.abort();Use timeout helpers carefully.
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error("Timed out")), ms);
});
}Prefer APIs with built-in cancellation where available.
Iteration And Iterables
Use for...of for iterable values.
for (const item of items) {}Use for...in only for object property names, and usually prefer Object.keys() or Object.entries().
Use generators for lazy sequences.
function* range(start, end) {
for (let value = start; value < end; value += 1) {
yield value;
}
}Use async iterators for streaming async data.
for await (const chunk of stream) {}Understand that arrays, strings, maps, sets, NodeLists in modern browsers, and many platform objects are iterable.
Maps, Sets, WeakMaps, WeakSets
Use Map when keys are not naturally strings or when insertion order and frequent additions/removals matter.
const usersById = new Map();
usersById.set(user.id, user);Use Set for uniqueness.
const uniqueIds = new Set(ids);Use WeakMap for metadata keyed by objects without preventing garbage collection.
const metadata = new WeakMap();
metadata.set(element, { initialized: true });Use WeakSet for object membership tracking without retaining objects.
Prefer map.has(key) over checking map.get(key) !== undefined when undefined can be a stored value.
if (cache.has(key)) {}Convert maps and sets when needed.
const entries = [...map.entries()];
const values = [...set];JSON And Structured Data
Use JSON.stringify() and JSON.parse() for JSON.
const json = JSON.stringify(data);
const data = JSON.parse(json);Wrap JSON.parse() for untrusted input.
function parseJSON(value) {
try {
return JSON.parse(value);
} catch {
return null;
}
}Use replacer and reviver when needed.
JSON.stringify(value, null, 2);Remember JSON does not support undefined, functions, symbols, BigInt, cyclic references, Map, Set, or rich object prototypes.
Use structuredClone() for platform-supported structured cloning.
Use FormData, URLSearchParams, Blob, File, ArrayBuffer, and typed arrays for browser-native data handling where appropriate.
DOM Selection And Manipulation
Use querySelector() and querySelectorAll() for CSS selector-based queries.
const button = document.querySelector("[data-submit]");
const items = document.querySelectorAll(".item");Check for null when an element may not exist.
const button = document.querySelector("button");
if (button) {
button.disabled = true;
}Prefer textContent for text.
element.textContent = label;Use innerHTML only with trusted or sanitized content.
Avoid injecting unsanitized user content into HTML.
Use classList for classes.
element.classList.add("active");
element.classList.toggle("hidden", isHidden);Use dataset for data-* attributes.
const id = element.dataset.id;Use setAttribute() and removeAttribute() for attributes where properties are not appropriate.
Use DOM properties for common reflected properties.
input.value = "";
button.disabled = true;Use createElement() for creating elements.
const item = document.createElement("li");
item.textContent = name;Use DocumentFragment or batch DOM updates for large insertions.
const fragment = document.createDocumentFragment();Use replaceChildren() to replace content.
list.replaceChildren(...items);Use closest() for ancestor lookup.
const row = event.target.closest("[data-row]");Use matches() for selector checks.
if (element.matches(".active")) {}Avoid layout thrashing by batching reads and writes.
const width = element.offsetWidth;
element.style.width = `${width + 10}px`;Use requestAnimationFrame() for visual updates.
requestAnimationFrame(() => {
element.style.transform = "translateX(10px)";
});Use MutationObserver, ResizeObserver, and IntersectionObserver instead of polling when appropriate.
Events
Use addEventListener().
button.addEventListener("click", handleClick);Remove listeners when no longer needed.
button.removeEventListener("click", handleClick);Use event delegation for many similar child elements.
list.addEventListener("click", (event) => {
const button = event.target.closest("[data-action]");
if (!button) return;
});Use options such as once, passive, and signal.
element.addEventListener("click", handler, { once: true });
const controller = new AbortController();
element.addEventListener("click", handler, { signal: controller.signal });
controller.abort();Use passive listeners for scroll/touch events when preventDefault() is not needed.
window.addEventListener("scroll", onScroll, { passive: true });Understand event bubbling and capturing.
Use event.currentTarget when referring to the element the listener is attached to.
Use event.target when referring to the originating element.
Avoid inline HTML event handlers.
Fetch And Networking
Use fetch() for HTTP requests.
const response = await fetch("/api/users");Check response.ok; fetch does not reject for HTTP error status codes.
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}Parse based on content type or expected response format.
const data = await response.json();Send JSON with explicit headers.
await fetch("/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(user),
});Use URL and URLSearchParams for URLs.
const url = new URL("/api/search", location.origin);
url.searchParams.set("q", query);Use AbortController to cancel requests.
const controller = new AbortController();
const response = await fetch(url, {
signal: controller.signal,
});Be careful with credentials.
fetch(url, { credentials: "include" });Understand CORS behavior rather than trying to bypass it client-side.
Avoid putting secrets in browser JavaScript.
Use exponential backoff or controlled retry logic for transient failures.
Avoid retrying non-idempotent requests unless designed for it.
Browser Storage
Use localStorage for small, non-sensitive persistent string data.
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme");Use sessionStorage for tab/session-lifetime string data.
Do not store sensitive tokens or secrets in localStorage if avoidable.
Wrap storage access because it can throw in privacy modes or quota situations.
try {
localStorage.setItem("key", value);
} catch {}Use IndexedDB for larger structured client-side data.
Use Cache Storage for request/response caching, typically with service workers.
Use cookies only when their HTTP behavior is needed.
Set cookie security attributes server-side where possible: HttpOnly, Secure, SameSite.
Web Components
Use custom elements when native component encapsulation and framework independence are useful.
class UserCard extends HTMLElement {
connectedCallback() {
this.textContent = "User";
}
}
customElements.define("user-card", UserCard);Use shadow DOM for style and DOM encapsulation.
const root = this.attachShadow({ mode: "open" });Use templates for reusable markup.
Use attributes for string-based configuration and properties for rich values.
Clean up side effects in disconnectedCallback.
Avoid web components when a project’s framework component model is clearly the better local fit.
Forms
Use semantic form elements.
Use FormData to read form values.
const formData = new FormData(form);
const email = formData.get("email");Use built-in constraint validation where appropriate.
if (!form.checkValidity()) {
form.reportValidity();
}Use proper name attributes.
Use button type explicitly.
<button type="submit">Save</button>
<button type="button">Cancel</button>Prevent default form submission only when handling submission in JavaScript.
form.addEventListener("submit", async (event) => {
event.preventDefault();
});Do not rely only on client-side validation. Validate on the server too.
Accessibility Defaults
Use semantic HTML first.
Use buttons for actions and links for navigation.
Do not replace native controls with custom ones unless necessary.
Preserve keyboard access.
Manage focus intentionally for dialogs, menus, and route changes.
Use ARIA only when native HTML cannot express the behavior.
Do not use ARIA to change semantics incorrectly.
Keep accessible names clear.
Use aria-live for dynamic status updates when needed.
Respect reduced motion preferences.
const prefersReducedMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;Ensure JavaScript-enhanced experiences still fail gracefully where possible.
Internationalization
Use Intl.NumberFormat for numbers, currency, and percentages.
const formatter = new Intl.NumberFormat(undefined, {
style: "currency",
currency: "USD",
});Use Intl.DateTimeFormat for dates.
Use Intl.RelativeTimeFormat for relative time.
const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" });Use Intl.PluralRules for pluralization logic.
Do not concatenate translated strings from fragments when grammar may vary.
Avoid assuming English word order, decimal separators, currency position, or plural rules.
Performance
Prefer clarity first, then optimize measured bottlenecks.
Avoid unnecessary work in hot paths.
Debounce frequent user input.
function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}Throttle high-frequency events when appropriate.
Use requestAnimationFrame() for animation work.
Use requestIdleCallback() as progressive enhancement for non-urgent background work.
Use IntersectionObserver for lazy visibility work.
Use ResizeObserver for size changes.
Avoid repeated DOM queries in tight loops when references can be reused.
Avoid forced synchronous layout by interleaving DOM reads and writes.
Use event delegation for many repeated elements.
Use lazy loading and dynamic imports for large optional code.
Use web workers for CPU-heavy work that would block the main thread.
Use transferables for large binary data where appropriate.
Prefer built-in browser APIs over large dependencies for simple tasks.
Measure with browser performance tools before complex optimization.
Security
Never trust client input.
Escape or sanitize untrusted HTML.
Prefer textContent over innerHTML.
Avoid eval(), new Function(), and string-based timers.
setTimeout(callback, 100);not:
setTimeout("callback()", 100);Avoid inline scripts where Content Security Policy matters.
Do not expose secrets in frontend code.
Validate URLs before navigating or fetching if they come from untrusted input.
Be careful with open redirects.
Use rel="noopener noreferrer" for untrusted new-window links.
Understand XSS, CSRF, CORS, clickjacking, and supply-chain risks.
Use Subresource Integrity for third-party scripts where applicable.
Keep dependencies updated.
Minimize dependency surface.
Clean Code Principles
Write code for readers.
Use names that describe intent.
const activeSubscriptionCount = subscriptions.filter((s) => s.active).length;Avoid vague names like data, obj, tmp, and val unless the scope is tiny and obvious.
Keep functions focused.
Avoid deeply nested control flow.
Prefer explicit data flow over hidden mutation.
Prefer simple conditionals over clever expressions.
Do not over-abstract early.
Remove dead code.
Avoid comments that restate the code.
Use comments to explain why, constraints, non-obvious tradeoffs, or external quirks.
Keep related code close together.
Separate pure computation from side effects.
Make invalid states hard to represent where possible.
Normalize data at boundaries.
Avoid boolean traps.
Prefer clear configuration objects.
Handle edge cases deliberately.
Fail loudly in developer-facing code when invariants are violated.
Use consistent formatting.
Let automated formatters handle style.
Type Awareness Without TypeScript
Even in plain JavaScript:
- Keep value shapes consistent.
- Avoid functions that return many unrelated types.
- Document complex object shapes with JSDoc if TypeScript is not used.
- Validate unknown inputs.
- Avoid excessive dynamic property access.
- Prefer predictable APIs.
Example JSDoc:
/**
* @param {{ id: string, name: string }} user
* @returns {string}
*/
function getDisplayName(user) {
return user.name;
}Use TypeScript for larger codebases when static checking would reduce risk.
API Design
Design functions around clear contracts.
Keep public APIs small.
Prefer predictable return values.
Avoid returning null, undefined, false, and throwing for similar failure modes in the same API.
Use options objects for extensibility.
function search(query, { limit = 20, signal } = {}) {}Use stable naming conventions.
Use async APIs consistently when operations may become asynchronous.
Avoid exposing internal mutable data.
getItems() {
return [...this.#items];
}Version APIs when consumers depend on them.
Data Validation
Validate at trust boundaries:
- User input.
- Network responses.
- Local storage.
- URL parameters.
- Messages from workers, frames, or extensions.
- Third-party library output.
Use schema validation for complex external data.
For simple validation, use clear checks.
function isUser(value) {
return (
value !== null &&
typeof value === "object" &&
typeof value.id === "string" &&
typeof value.name === "string"
);
}Avoid assuming JSON responses match expected shape.
URL And Navigation APIs
Use URL for parsing and composing URLs.
const url = new URL(location.href);
const page = url.searchParams.get("page");Use URLSearchParams.
const params = new URLSearchParams({ q: query, page: "1" });Use History API for client-side navigation.
history.pushState({ page: "settings" }, "", "/settings");Listen for popstate.
window.addEventListener("popstate", handlePopState);Use location.assign() for navigation when appropriate.
Use location.replace() when the current page should not remain in history.
Timers And Scheduling
Use setTimeout() for delayed work.
Use setInterval() carefully and clear it when done.
const intervalId = setInterval(tick, 1000);
clearInterval(intervalId);Prefer recursive setTimeout() when work duration matters.
async function poll() {
await refresh();
setTimeout(poll, 5000);
}Use queueMicrotask() for microtask scheduling.
queueMicrotask(() => {
notifyObservers();
});Understand that promises schedule microtasks and timers schedule macrotasks.
Workers And Messaging
Use web workers for CPU-heavy tasks.
const worker = new Worker(new URL("./worker.js", import.meta.url), {
type: "module",
});Use postMessage() for communication.
Use structured clone-compatible data.
Use transferables for large buffers.
worker.postMessage(buffer, [buffer]);Terminate workers when no longer needed.
worker.terminate();Validate messages received from workers or other contexts.
Clipboard, Files, And Binary Data
Use Async Clipboard API where available and permission-appropriate.
await navigator.clipboard.writeText(text);Use File, Blob, and FileReader or modern blob methods.
const text = await file.text();
const buffer = await file.arrayBuffer();Use object URLs for local previews and revoke them.
const url = URL.createObjectURL(file);
URL.revokeObjectURL(url);Use streams for large data where appropriate.
Streams
Use Web Streams for incremental data processing when useful.
const reader = response.body.getReader();Prefer higher-level APIs unless streaming is needed.
Use TextEncoder and TextDecoder.
const decoder = new TextDecoder();
const text = decoder.decode(buffer);Use ReadableStream, WritableStream, and TransformStream for advanced streaming workflows.
Canvas, Media, And Graphics
Use <canvas> for immediate-mode 2D drawing.
Use requestAnimationFrame() for animation loops.
function frame() {
draw();
requestAnimationFrame(frame);
}Use OffscreenCanvas as progressive enhancement where supported.
Use WebGL or WebGPU for advanced graphics when appropriate.
Use media APIs such as HTMLMediaElement, MediaStream, and MediaRecorder with permission and compatibility handling.
Always handle permissions and user gestures for media features.
Progressive Enhancement
Feature-detect APIs before use.
if ("clipboard" in navigator) {}
if ("IntersectionObserver" in window) {}Use fallback behavior.
if ("showOpenFilePicker" in window) {
// enhanced file picker
} else {
// input[type=file]
}Avoid browser sniffing unless absolutely necessary.
Load polyfills conditionally when practical.
Enhance from semantic HTML and functional basics.
Use CSS feature queries and JS feature checks for newer features.
Modern Cutting-Edge APIs Worth Considering With Fallbacks
These can be recommended when they improve UX and have fallback paths:
Temporalfor robust date/time when available or via polyfill.- View Transitions API for progressive route/page transitions.
- File System Access API with file input fallback.
- Web Share API with clipboard or manual fallback.
- Async Clipboard API with selection/manual fallback.
- Compression Streams for browser-native compression where available.
- WebGPU for advanced graphics with WebGL/canvas fallback.
- OffscreenCanvas for worker-based rendering fallbacking to main-thread canvas.
- Navigation API for advanced client-side routing where supported.
scheduler.postTask()for prioritized task scheduling where available.- Popover API for native popovers with custom fallback.
- Declarative Shadow DOM when server-rendering web components.
- Import maps where supported or build-tool fallback.
- Speculation Rules API for progressive prerender/prefetch.
- Sanitizer API where available, with established sanitizer library fallback.
- Shared Storage, FedCM, and privacy-related APIs only for suitable use cases and with careful support checks.
Testing
Write tests for behavior, not implementation details.
Use unit tests for pure logic.
Use integration tests for module interactions.
Use end-to-end tests for critical user flows.
Test edge cases and failure paths.
Test async behavior deterministically.
Avoid brittle tests based on timing where possible.
Use fake timers when appropriate.
Mock network boundaries deliberately.
Do not over-mock the code under test.
Use representative fixtures.
Keep tests readable and maintainable.
Debugging
Use browser DevTools.
Use breakpoints instead of excessive logging for complex issues.
Use console.log, console.warn, console.error, console.table, and console.time intentionally.
Remove noisy debug logging before production.
Preserve useful operational logging where appropriate.
Use source maps in development and production error reporting where safe.
Inspect network requests, performance profiles, layout shifts, memory, and event listeners.
Tooling Defaults
Use a formatter such as Prettier for style consistency.
Use a linter such as ESLint for bug-prone patterns.
Use TypeScript or JSDoc checking for larger projects.
Use modern bundlers only when needed for dependencies, transforms, optimization, or developer experience.
Prefer native ESM where practical.
Use package lockfiles.
Use npm scripts or equivalent task runners for common commands.
Keep dependencies minimal and justified.
Audit dependency health before adding packages.
Dependency Use
Prefer platform APIs for standard capabilities.
Add dependencies when they provide meaningful value:
- Complex date/time manipulation.
- Schema validation.
- Internationalization frameworks.
- Rich UI components.
- State management at scale.
- Parsing.
- Cryptography wrappers around native primitives.
- Specialized algorithms.
Avoid dependencies for trivial utilities.
Check bundle size, maintenance, security, license, and API stability.
Prefer tree-shakeable packages.
Avoid importing entire libraries for one small function.
Browser Compatibility
Use broadly shipped features by default when targeting modern browsers.
Use transpilation/polyfills only according to project browser support policy.
Understand the difference between syntax transforms and runtime polyfills.
Feature-detect runtime APIs.
Avoid assuming all embedded browsers are current.
Check compatibility for APIs that are newer, mobile-specific, permission-gated, or behind secure-context requirements.
Remember many modern APIs require HTTPS.
Node-Compatible JavaScript
When writing JavaScript that may run in Node too:
- Use ESM or CommonJS consistently.
- Prefer standard Web APIs available in modern Node where appropriate.
- Use
node:specifiers for built-in modules.
import fs from "node:fs/promises";- Avoid browser globals unless guarded.
- Avoid Node globals in browser-targeted code.
- Keep environment-specific code isolated.
- Use
process.envonly in server/build contexts. - Do not leak server secrets into browser bundles.
Common Pitfalls
Avoid accidental assignment in conditionals.
if (value === expected) {}Avoid comparing objects by value with ===.
{} === {}; // falseAvoid mutating state in place when consumers expect immutability.
Avoid stale closures in async callbacks and UI code.
Avoid unhandled promise rejections.
Avoid forgetting return in block-bodied arrow functions.
items.map((item) => {
return item.id;
});Avoid using forEach when you need break, continue, or await.
Avoid relying on object key order for core logic.
Avoid using array indexes as persistent IDs.
Avoid parsing numbers without validating the result.
Avoid using Date parsing for non-ISO strings.
Avoid assuming fetch rejects on 404 or 500.
Avoid using innerHTML with untrusted data.
Avoid memory leaks from lingering timers, observers, subscriptions, workers, or event listeners.
Avoid excessive abstraction.
Avoid clever one-liners that hide control flow or error handling.
Default Style Preferences
Use clear, boring code.
Prefer this:
function getActiveUserNames(users) {
return users
.filter((user) => user.active)
.map((user) => user.name);
}Over this:
const getActiveUserNames = (u) => u.filter((x) => x.active).map((x) => x.name);Prefer guard clauses.
Prefer named intermediate values when they clarify intent.
const hasValidEmail = email.includes("@") && email.includes(".");
if (!hasValidEmail) return;Avoid compressing too much logic into a single expression.
Use descriptive errors.
Keep side effects visible.
Prefer explicit imports.
Prefer stable APIs over experimental APIs unless progressive enhancement is clear.
Summary Of Common-Knowledge Defaults
I would generally consider the following redundant in a project-specific JavaScript guide unless the project needs stricter local policy:
- Use
const/letinstead ofvar. - Use modules.
- Use strict equality.
- Use template literals.
- Use destructuring, spread, optional chaining, and nullish coalescing.
- Use promises and
async/await. - Use
fetchwithresponse.okchecks. - Use
Map/Setwhen appropriate. - Use modern array methods.
- Avoid mutation unless intentional.
- Prefer semantic DOM APIs.
- Use
textContentfor untrusted text. - Avoid
eval. - Validate external input.
- Prefer small functions and clear names.
- Avoid premature abstraction.
- Use feature detection for newer browser APIs.
- Use
Intlfor user-facing formatting. - Use
AbortControllerfor cancellable async work. - Use observers instead of polling.
- Use standard browser APIs before dependencies.
- Keep code readable, explicit, and maintainable.
JavaScript Knowledge Mirror: Standard Best Practices & APIs
This guide represents a baseline of modern JavaScript knowledge, covering syntax, standard library features, and clean code principles.
1. Variables and Scope
- `const` and `let`: Use
constby default for all variable declarations. Useletonly when the variable must be reassigned. Never usevar. - Block Scope: Understand that
letandconstare block-scoped, preventing hoisting issues and accidental global leakage. - Global Avoidance: Minimize the use of the global scope. Wrap code in modules or functions.
2. Functions and Modern Syntax
- Arrow Functions: Use arrow functions (
() => {}) for short callbacks and when you need to preserve the lexicalthiscontext. - Function Declarations: Use
functiondeclarations for top-level logic or when hoisting is intentionally required (rare). - Default Parameters: Use default values in function signatures (
function fn(a = 10) {}) instead of conditional checks inside the body. - Rest Parameters: Use
...argsto handle an indefinite number of arguments as an array. - Destructuring:
- Objects:
const { name, age } = user; - Arrays:
const [first, second] = list; - Function Arguments:
function print({ id, label }) {} - Spread Operator: Use
...for shallow copying objects/arrays or merging them ({ ...defaults, ...overrides }). - Template Literals: Use backticks for string interpolation and multi-line strings: `
Value: ${val}`.
3. Objects and Arrays
- Shorthand Properties: Use
{ name }instead of{ name: name }. - Computed Property Names: Use
{[key]: value}for dynamic keys. - Optional Chaining (`?.`): Safely access deeply nested properties:
user?.profile?.email. - Nullish Coalescing (`??`): Use
??to provide fallback values fornullorundefined, avoiding the pitfalls of||with falsy values like0or"". - Array Methods:
- Iteration:
forEach(side effects),map(transformation). - Filtering/Searching:
filter,find,findIndex,some,every,includes. - Reducing:
reduce(for complex aggregations). - Flattening:
flat(),flatMap(). - Access:
.at(-1)for the last element. - Object Methods:
Object.keys(),Object.values(),Object.entries(), andObject.fromEntries(). - `Object.groupBy()`: (Modern) Use for categorizing items in an array.
4. Asynchronous Programming
- Promises: Use Promises for all asynchronous operations.
- `async/await`: Use
async/awaitfor cleaner, more readable asynchronous code. Always wrap intry/catchfor error handling. - Promise Concurrency:
-
Promise.all(): Fails fast if any promise rejects. -
Promise.allSettled(): Waits for all to finish, regardless of outcome. -
Promise.any(): Returns the first successful promise. -
Promise.race(): Returns the result of the first settled promise (resolve or reject). - `AbortController`: Use
AbortControllerandAbortSignalto cancelfetchrequests or other async tasks (e.g., timeouts).
5. Classes and Modules
- ES Modules (ESM): Use
importandexport. Prefer named exports for better tooling support, usedefaultexport sparingly. - Dynamic Imports: Use
import()for code-splitting and lazy-loading. - Class Syntax: Use the
classkeyword. - Private Fields: Use the
#prefix for truly private class members:#internalState. - Static Blocks: Use
static {}for complex static initialization. - Inheritance: Use
extendsandsuper().
6. Standard Web APIs
- Fetch API: Use
fetch()for network requests. Remember it doesn't reject on 4xx/5xx errors; checkresponse.ok. - URL and URLSearchParams: Use the
URLAPI to parse and manipulate URLs and query parameters instead of string manipulation. - `structuredClone()`: Use for deep cloning objects (native alternative to
JSON.parse(JSON.stringify())or Lodash). - Intl API: Use
Intl.NumberFormat,Intl.DateTimeFormat, andIntl.RelativeTimeFormatfor localization. - DOM Manipulation:
- Use
querySelectorandquerySelectorAll. - Use
classList(add,remove,toggle,contains) for CSS classes. - Use
datasetfordata-*attributes. - Use
addEventListenerwith options like{ once: true }or{ signal }. - Intersection Observer: Use for lazy-loading or scroll-triggered animations.
- Resize Observer: Use for responding to element size changes.
7. Error Handling and Debugging
- Custom Errors: Extend the
Errorclass for domain-specific errors. - Error Cause: Use the
causeproperty when re-throwing errors to maintain the stack trace:new Error("Failed", { cause: originalErr }). - `console` methods: Beyond
log, usewarn,error,table,group/groupEnd, andtime/timeEnd.
8. Clean Code and Best Practices
- Naming:
-
camelCasefor variables and functions. -
PascalCasefor classes and components. -
SCREAMING_SNAKE_CASEfor constants. - Use descriptive, verb-based names for functions (e.g.,
getUserData,isEmailValid). - Early Returns: Use guard clauses to exit functions early, reducing nesting.
- Immutability: Avoid mutating state or function arguments. Return new objects/arrays instead.
- Pure Functions: Aim for functions with no side effects that return the same output for the same input.
- Avoid Magic Numbers: Extract literals to named constants.
- Module Size: Keep modules focused (Single Responsibility Principle).
- Comments: Use comments to explain why something is done, not what is being done (the code should be self-documenting).
JavaScript Unified Lowest Common Denominator (LCD) Mirror
This document represents the intersection of standard JavaScript knowledge and best practices consistently evidenced across three independent source mirrors.
1. Variables and Scope
- Declaration Strategy: Use
constby default for all variables. Useletonly when reassignment is explicitly required. - Avoid `var`: Do not use
vardue to its function-scoping and hoisting behaviors. - Block Scoping: Leverage the block-scoped nature of
letandconstto prevent accidental global leakage and hoisting issues.
2. Functions and Modern Syntax
- Arrow Functions: Use arrow functions (
() => {}) for callbacks and to preserve the lexicalthiscontext. - Function Declarations: Use the
functionkeyword for top-level logic or when hoisting is intentionally required. - Default Parameters: Assign default values in function signatures (
param = value) to handle missing arguments. - Rest Parameters: Use the rest syntax (
...args) to capture an indefinite number of arguments as an array. - Destructuring: Use destructuring to extract values from objects and arrays:
-
const { key } = object; -
const [first] = array; - Spread Operator: Use the
...syntax for shallow copying or merging objects and arrays. - Template Literals: Use backticks for string interpolation (
${value}) and creating multi-line strings.
3. Objects and Arrays
- Property Enhancements:
- Shorthand Properties: Use
{ name }when the key and variable name match. - Computed Property Names: Use
{[key]: value}for dynamic property keys. - Safety Operators:
- Optional Chaining (`?.`): Safely access nested properties (e.g.,
user?.profile?.id). - Nullish Coalescing (`??`): Provide fallback values specifically for
nullorundefined. - Standard Methods:
- Object Methods:
Object.keys(),Object.values(),Object.entries(), andObject.fromEntries(). - Array Iteration/Transformation:
forEach,map,filter,reduce. - Array Searching:
find,findIndex,some,every,includes. - Array Utility:
flat(),flatMap(), andat()(for relative/negative indexing).
4. Asynchronous Programming
- Promises: Use Promises for managing all asynchronous operations.
- Async/Await: Use
async/awaitfor readable asynchronous control flow. - Error Handling: Always wrap asynchronous logic in
try/catchblocks. - Promise Concurrency:
-
Promise.all(): Fails fast if any promise rejects. -
Promise.allSettled(): Waits for all promises to finish regardless of outcome. -
Promise.any(): Returns the first fulfilled promise. -
Promise.race(): Returns the first settled promise (resolve or reject).
5. Classes and Modules
- ES Modules (ESM): Use
importandexportstatements for modularity. - Class Syntax: Use the
classkeyword for stateful entities. - Private Fields: Use the
#prefix for private class members (e.g.,#state). - Inheritance: Use
extendsandsuper()for class-based inheritance.
6. Web APIs
- Fetch API: Use
fetch()for network requests. Always checkresponse.okasfetchdoes not reject on HTTP error statuses (4xx/5xx). - AbortController: Use
AbortControllerandAbortSignalto cancel asynchronous tasks like network requests. - URL API: Use
URLandURLSearchParamsto manipulate URLs and query strings. - DOM Manipulation:
- Selection:
querySelectorandquerySelectorAll. - Attributes/Classes:
classList(add, remove, toggle) anddataset. - Events:
addEventListener(including options like{ once: true }or{ signal }). - Deep Cloning: Use
structuredClone()for deep copies of objects. - Observers: Use
IntersectionObserverandResizeObserverfor responding to layout and visibility changes. - Intl API: Use the
Intlnamespace (e.g.,DateTimeFormat,NumberFormat) for localized formatting.
7. Error Handling and Debugging
- Custom Errors: Extend the
Errorclass to create domain-specific error types. - Error Cause: Use the
causeproperty when re-throwing to preserve original stack traces:new Error("msg", { cause: err }). - Console Methods: Utilize
log,warn,error,table,group, andtimefor development and debugging.
8. Clean Code Principles
- Descriptive Naming: Use clear, intention-revealing names for variables and functions.
- Early Returns: Use guard clauses to handle edge cases early and reduce function nesting.
- Immutability: Avoid mutating objects, arrays, or function arguments; return new instances instead.
- Pure Functions: Prioritize functions that return consistent outputs for given inputs without side effects.
- Comments: Use comments to explain why complex logic exists, rather than what the code is doing.
JavaScript Unified Lowest Common Denominator (LCD) Mirror (Revised)
This document represents the absolute intersection of JavaScript knowledge consistently evidenced across the Gemini, Claude, and Codex source mirrors. It strictly excludes any feature, API, or best practice missing from any one of the source documents.
1. Variables and Scope
- Declaration Strategy: Use
constby default for variables. Useletonly when the variable must be reassigned. - Avoid `var`: Do not use
varfor variable declarations. - Scoped Declarations: Understand that
letandconstprovide block-scoped declarations, which prevents accidental global leakage and issues associated with hoisting.
2. Functions and Modern Syntax
- Arrow Functions: Use arrow functions (
() => {}) for callbacks and when preserving the lexicalthiscontext is required. - Function Declarations: Use traditional
functiondeclarations for top-level logic or when hoisting is required. - Default Parameters: Use default values in function signatures (
param = value) to handle missing arguments. - Rest Parameters: Use the rest syntax (
...args) to capture an indefinite number of arguments into a single array. - Destructuring: Use destructuring to extract data from objects and arrays:
-
const { key } = object; -
const [first] = array; - Destructuring is also applicable to function parameters.
- Spread Operator (Arrays/Objects): Use the spread syntax (
...) for shallow copying or merging objects and arrays. - Template Literals: Use backticks for string interpolation (
${value}) and for defining multi-line strings.
3. Objects and Arrays
- Object Literals:
- Shorthand Properties: Use
{ key }when the variable name matches the property key. - Computed Property Names: Use
{[key]: value}for dynamic property keys. - Safe Access and Defaults:
- Optional Chaining (`?.`): Safely access deeply nested properties (e.g.,
user?.address?.city). - Nullish Coalescing (`??`): Provide fallback values specifically for
nullorundefinedinputs. - Core Methods:
- Object Static Methods:
Object.keys(),Object.values(),Object.entries(), andObject.fromEntries(). - Array Transformation/Iteration:
map,filter,forEach,reduce. - Array Searching:
find,findIndex,some,every,includes. - Array Utility:
flat(),flatMap(), andat()(for relative indexing, such as the last element).
4. Asynchronous Programming
- Promises: Use Promises for all asynchronous operations.
- Async/Await: Use
async/awaitfor readable asynchronous control flow. - Error Handling: Wrap asynchronous logic in
try/catchblocks to handle failures. - Promise Combinators:
-
Promise.all(): Continues only if all promises fulfill. -
Promise.allSettled(): Waits for all promises to finish regardless of outcome. -
Promise.any(): Continues as soon as the first promise fulfills. -
Promise.race(): Continues as soon as the first promise settles (fulfills or rejects).
5. Classes and Modules
- ES Modules (ESM): Use
importandexportstatements. Named exports are preferred, while default exports should be used when a module has a primary concept. - Dynamic Imports: Use
import()for lazy-loading or code-splitting. - Class Syntax: Use the
classkeyword for defining stateful entities. - Private Fields: Use the
#prefix (e.g.,#field) for truly private class members. - Inheritance: Use
extendsandsuper()to implement class inheritance.
6. Standard Web APIs
- Fetch API: Use
fetch()for network requests. Always checkresponse.okbecausefetchonly rejects on network failure, not on HTTP error status codes (4xx/5xx). - AbortController: Use
AbortControllerandAbortSignalto cancel asynchronous tasks such as network requests. - URL API: Use the
URLandURLSearchParamsobjects to parse and manipulate URLs and query parameters. - Deep Cloning: Use
structuredClone()for deep cloning objects. - Internationalization (Intl): Use the
IntlAPI (e.g.,DateTimeFormat,NumberFormat) for locale-aware formatting. - DOM Selection and Interaction:
- Use
querySelectorandquerySelectorAllfor element selection. - Use
classList(add, remove, toggle) for CSS class management. - Use
datasetfor accessingdata-*attributes. - Use
addEventListenerfor event handling (including options likeonceorsignal). - Observers: Use
IntersectionObserverandResizeObserverfor responding to element visibility and size changes.
7. Error Handling and Debugging
- Custom Errors: Extend the
Errorclass to create specific error types. - Error Cause: Use the
causeproperty when throwing errors to maintain a chain of responsibility:new Error("message", { cause: originalError }). - Console Methods: Use
console.log,console.warn,console.error,console.table, andconsole.time/timeEnd.
8. Clean Code and Best Practices
- Intention-Revealing Naming: Use names that clearly describe the intent of variables and functions. Avoid vague names.
- Early Returns: Use guard clauses to handle edge cases early and reduce the depth of nested logic.
- Immutability: Avoid mutating objects, arrays, or function arguments. Return new data structures instead.
- Pure Functions: Aim for functions that produce the same output for the same input and have no side effects.
- Magic Numbers: Extract literal values into named constants for better readability.
- Single Responsibility: Keep modules and functions focused on a single responsibility.
- Comments: Use comments to explain why a piece of code exists or follows a specific pattern, rather than what the code is doing.
JavaScript Unified Lowest Common Denominator (LCD) Mirror (Final)
This document represents the absolute literal intersection of the Gemini, Claude, and Codex mirrors. Only features and guidelines explicitly evidenced in all three sources are included.
1. Variables and Scope
- Declarations: Use
constby default. Useletonly when reassignment is necessary. - Avoid `var`: Do not use
var. - Block Scope: Utilize
letandconstfor block-scoped declarations to prevent global scope leakage and issues with hoisting.
2. Functions and Modern Syntax
- Function Types:
- Use arrow functions (
() => {}) for callbacks and to preserve lexicalthis. - Use
functiondeclarations for top-level logic or when hoisting is needed. - Parameters:
- Default Parameters: Use
param = valuein signatures to handle missing arguments. - Rest Parameters: Use
...argsto capture multiple arguments as an array. - Destructuring: Use destructuring to extract values from objects and arrays (applicable to variables and function parameters).
- Spread Operator: Use
...for shallow copying and merging of objects and arrays. - Template Literals: Use backticks for string interpolation (
${value}) and multi-line strings.
3. Objects and Arrays
- Object Literals:
- Shorthand Properties: Use
{ key }when the variable name matches the property name. - Computed Property Names: Use
{[key]: value}for dynamic keys. - Safety and Defaults:
- Optional Chaining (`?.`): Safely access nested properties.
- Nullish Coalescing (`??`): Provide fallbacks for
nullorundefined. - Static Object Methods:
Object.keys(),Object.values(),Object.entries(), andObject.fromEntries(). - Array Methods:
- Iteration/Transformation:
forEach,map,filter,reduce. - Searching/Validation:
find,findIndex,some,every,includes. - Utility:
flat(),flatMap(), andat()(for relative indexing).
4. Asynchronous Programming
- Execution: Use
async/awaitand Promises for asynchronous control flow. - Error Handling: Use
try/catchblocks to manage asynchronous failures. - Promise Combinators:
-
Promise.all(): Continues if all fulfill. -
Promise.allSettled(): Waits for all to finish regardless of outcome. -
Promise.any(): Returns the first fulfilled promise. -
Promise.race(): Returns the first settled promise.
5. Classes and Modules
- ES Modules: Use
importandexport. Prefer named exports. - Dynamic Imports: Use
import()for lazy loading. - Class Syntax: Use the
classkeyword. - Private Fields: Use the
#prefix for private members. - Inheritance: Use
extendsandsuper().
6. Standard Web APIs
- Networking: Use
fetch(). Always checkresponse.ok(it does not reject on 4xx/5xx). - Cancellation: Use
AbortControllerandAbortSignal. - URLs: Use
URLandURLSearchParamsfor manipulation. - Cloning: Use
structuredClone()for deep copies. - Internationalization (Intl): Use
Intlfor locale-aware formatting (specificallyDateTimeFormat,NumberFormat, andRelativeTimeFormat). - DOM:
- Selection:
querySelectorandquerySelectorAll. - Attributes:
classListanddataset. - Events:
addEventListener(including options likeonceandsignal). - Observers: Use
IntersectionObserverandResizeObserver.
7. Error Handling and Debugging
- Errors: Extend the
Errorclass for custom types and use thecauseproperty for re-throwing. - Console: Use
console.logandconsole.error.
8. Clean Code Principles
- Naming: Use descriptive, intention-revealing names.
- Flow Control: Use guard clauses (early returns) to reduce nesting.
- Immutability: Prefer non-mutating operations; avoid mutating arguments.
- Functions: Aim for pure functions and adhere to the Single Responsibility Principle.
- Constants: Replace literal values (magic numbers) with named constants.
- Documentation: Use comments to explain why something is done, not what is done.
import fs from 'node:fs';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
// Use native Node.js .env support if available (Node 20.6.0+)
if (typeof process.loadEnvFile === 'function') {
try {
process.loadEnvFile();
} catch (e) {
// .env might not exist, that's fine
}
}
async function callCli(prompt: string, cliId: string): Promise<string> {
const clis: Record<string, { env: string; defaultBin: string; buildArgs: (p: string) => string[] }> = {
gemini: {
env: 'GEMINI_CLI_BIN',
defaultBin: 'gemini',
buildArgs: (p) => ['-p', p, '-o', 'text', '--skip-trust'],
},
claude: {
env: 'CLAUDE_CODE_CLI_BIN',
defaultBin: 'claude',
buildArgs: (p) => ['-p', p, '--dangerously-skip-permissions', '--output-format', 'text'],
},
codex: {
env: 'CODEX_CLI_BIN',
defaultBin: 'codex',
buildArgs: (p) => ['exec', p],
},
};
const config = clis[cliId];
if (!config) {
console.error(`Unknown CLI tool ID: ${cliId}`);
return '';
}
const command = process.env[config.env] || config.defaultBin;
const args = config.buildArgs(prompt);
console.log(`Executing ${cliId} CLI: ${command} ...`);
const result = spawnSync(command, args, { encoding: 'utf8' });
if (result.error) {
console.error(`Error spawning ${cliId} CLI:`, result.error);
return '';
}
if (result.status !== 0) {
console.error(`${cliId} CLI failed with exit code ${result.status}:`, result.stderr);
return '';
}
return result.stdout || '';
}
async function main() {
const discipline = process.argv[2];
if (!discipline) {
console.log('Usage: node generate_mirrors.ts <discipline_name> (e.g., "JavaScript", "CSS")');
process.exit(1);
}
// Load prompt template from the markdown file
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
const promptTemplatePath = path.join(scriptDir, '../mirror_prompt.md');
if (!fs.existsSync(promptTemplatePath)) {
console.error(`Prompt template not found at: ${promptTemplatePath}`);
process.exit(1);
}
const promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
const prompt = promptTemplate.replace(/\{\{\s*discipline\s*\}\}/g, discipline);
// Create mirrors/ and discipline directory inside the project-discipline-guides folder
const mirrorsDir = path.resolve(scriptDir, '../mirrors');
const disciplineDir = path.join(mirrorsDir, discipline.toLowerCase());
if (!fs.existsSync(mirrorsDir)) fs.mkdirSync(mirrorsDir, { recursive: true });
if (!fs.existsSync(disciplineDir)) fs.mkdirSync(disciplineDir, { recursive: true });
const geminiFile = path.join(disciplineDir, 'gemini_mirror.md');
const claudeFile = path.join(disciplineDir, 'claude_mirror.md');
const codexFile = path.join(disciplineDir, 'codex_mirror.md');
console.log(`--- Generating Knowledge Mirror for ${discipline} (Gemini) ---`);
const geminiResult = await callCli(prompt, 'gemini');
console.log(`--- Generating Knowledge Mirror for ${discipline} (Claude) ---`);
const claudeResult = await callCli(prompt, 'claude');
console.log(`--- Generating Knowledge Mirror for ${discipline} (Codex) ---`);
const codexResult = await callCli(prompt, 'codex');
const generatedFiles = [];
if (geminiResult) {
fs.writeFileSync(geminiFile, geminiResult);
generatedFiles.push(geminiFile);
} else {
console.warn(`⚠️ Skipping ${geminiFile} due to failure.`);
}
if (claudeResult) {
fs.writeFileSync(claudeFile, claudeResult);
generatedFiles.push(claudeFile);
} else {
console.warn(`⚠️ Skipping ${claudeFile} due to failure.`);
}
if (codexResult) {
fs.writeFileSync(codexFile, codexResult);
generatedFiles.push(codexFile);
} else {
console.warn(`⚠️ Skipping ${codexFile} due to failure.`);
}
if (generatedFiles.length > 0) {
console.log(`\n✅ Knowledge Mirrors generated:`);
generatedFiles.forEach(file => console.log(`- ${file}`));
} else {
console.error(`\n❌ No Knowledge Mirrors were successfully generated.`);
process.exit(1);
}
// --- INTERSECTION STEP (all three are required core mirrors) ---
if (!geminiResult || !claudeResult || !codexResult) {
console.warn('⚠️ Skipping Redundancy Mirror generation because not all three mirrors were successfully generated.');
return;
}
console.log(`\n--- Generating Unified Redundancy Mirror for ${discipline} ---`);
const intersectionTemplatePath = path.join(scriptDir, '../intersection_prompt.md');
if (!fs.existsSync(intersectionTemplatePath)) {
console.error(`Intersection template not found at: ${intersectionTemplatePath}`);
process.exit(1);
}
const intersectionTemplate = fs.readFileSync(intersectionTemplatePath, 'utf8');
const intersectionPrompt = intersectionTemplate
.replace(/\{\{\s*discipline\s*\}\}/g, discipline)
.replace(/\{\{\s*gemini_mirror\s*\}\}/g, geminiResult)
.replace(/\{\{\s*claude_mirror\s*\}\}/g, claudeResult)
.replace(/\{\{\s*codex_mirror\s*\}\}/g, codexResult);
// Use Gemini to perform the intersection
const lcdResult = await callCli(intersectionPrompt, 'gemini');
const lcdFile = path.join(disciplineDir, 'mirror.md');
if (lcdResult) {
fs.writeFileSync(lcdFile, lcdResult);
console.log(`\n✅ Unified Redundancy Mirror generated:`);
console.log(`- ${lcdFile}`);
} else {
console.error(`\n❌ Failed to generate Unified Redundancy Mirror.`);
}
}
main().catch(console.error);