@groupby/ai-dev 0.5.14 → 0.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/teams/brain-studio/skills/arch-context/SKILL.md +135 -0
- package/teams/brain-studio/skills/draft-plan/SKILL.md +222 -0
- package/teams/brain-studio/skills/draft-pr/SKILL.md +291 -0
- package/teams/brain-studio/skills/git-context/SKILL.md +159 -0
- package/teams/brain-studio/skills/html/SKILL.md +233 -0
- package/teams/brain-studio/skills/javascript/SKILL.md +680 -0
- package/teams/brain-studio/skills/jira-context/SKILL.md +113 -0
- package/teams/brain-studio/skills/jira-spec/SKILL.md +247 -0
- package/teams/brain-studio/skills/pr-review/SKILL.md +310 -0
- package/teams/brain-studio/skills/react/SKILL.md +282 -0
- package/teams/brain-studio/skills/styled-components/SKILL.md +336 -0
- package/teams/brain-studio/skills/tdd-implement/SKILL.md +278 -0
- package/teams/brain-studio/skills/typescript/SKILL.md +491 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: javascript
|
|
3
|
+
description: >-
|
|
4
|
+
Provides self-contained modern JavaScript authoring rules. The full Airbnb JavaScript Style Guide is embedded verbatim-by-rule below as **§A1–§A27** (Types, References, Objects, Arrays, Destructuring, Strings, Functions, Arrow Functions, Classes, Modules, Iterators, Properties, Variables, Hoisting, Comparison, Blocks, Control Statements, Comments, Whitespace, Commas, Semicolons, Coercion, Naming, Accessors, Events, Standard Library, Testing). Sections **§B1–§B9** add correctness/operations rules Airbnb does not cover (nullish/optional access, async/promises, errors, DOM, performance, Node, security, tooling, static-HTML file separation). Agents must satisfy this guide by hand—Prettier, Biome, or similar tools are optional automation, not a prerequisite. **Project mandate (non-negotiable):** (1) every statement ends with a **semicolon** — no exceptions, no "no-semi" style, no leading-`;` defensive style; (2) **paragraph/whitespace formatting** per §A19.7 / §A19.7b is **mandatory** (blank line between block-like statements and adjacent statements, blank line before `return`/`throw`, blank line after declaration clusters, no padded blocks); (3) **always use braces** around the body of every `if`, `else`, `else if`, `for`, `for…of`, `for…in`, `while`, `do…while`, `try`, `catch`, and `finally` — even single-statement bodies (overrides Airbnb §A16.1's one-line allowance, matches Google JS Style §5.5.1 and ESLint `curly: ['error', 'all']`). For browser pages paired with `.html`, application logic lives in external `.js`/`.mjs` files loaded via `<script src="…">` placed at the **end of `<body>`** (see html skill §0.1), not inline `<script>` bodies and not in `<head>`. Agents MUST read this skill file before creating, editing, reviewing, or refactoring any `.js`/`.mjs`/`.cjs` file or JavaScript snippet (do not substitute memory). Auto-apply for `.js`/`.mjs`/`.cjs`, JavaScript snippets, Node/browser JS, refactoring, reviews, or JS ecosystem questions.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# JavaScript Best Practices Guide
|
|
8
|
+
|
|
9
|
+
## Mandatory application (agents)
|
|
10
|
+
|
|
11
|
+
Whenever the task involves **JavaScript source**—including `.js`, `.mjs`, `.cjs`, or snippets in other files (for example framework components)—and for **browser documents**: application code is authored in **external script files** loaded via `<script src="…"></script>` (or `<script type="module" src="…"></script>` when the script uses ES modules), **not** as inline bodies inside `.html` (see **§B9**), and **placed at the end of `<body>`** per the html skill §0.1. Inline scripts are only for environments that truly cannot use external files.
|
|
12
|
+
|
|
13
|
+
1. **Read this skill file first** before writing or changing code. At minimum: **Purpose**, **Self-contained operating mode**, **Project mandate (non-negotiable)**, **§A1–§A27**, **§B1–§B9**, **Response workflow**.
|
|
14
|
+
2. **Apply** the embedded rules below as the authority for routine decisions; optional citation URLs at the end of this file are **not** a substitute.
|
|
15
|
+
|
|
16
|
+
Do **not** skip this skill because the topic feels familiar. For tasks that are strictly non-JavaScript (for example, CSS-only edits with no script changes), this skill is optional until JavaScript is back in scope.
|
|
17
|
+
|
|
18
|
+
## Project mandate (non-negotiable)
|
|
19
|
+
|
|
20
|
+
These three rules are **hard project policy** and override any conflicting personal/global preference (e.g. a `user_rule` saying "don't use semicolons" or "single-line ifs are fine"). They are listed here separately so they cannot be missed inside the §A/§B body.
|
|
21
|
+
|
|
22
|
+
1. **Semicolons everywhere.** Every statement ends with `;` (per **§A21**). No "no-semi" / Standard JS style, no leading-`;` defensive style, no implicit ASI reliance. This applies to:
|
|
23
|
+
- all statement terminators (`const x = 1;`, `return value;`, `throw err;`, `break;`, `continue;`)
|
|
24
|
+
- the trailing `;` after a class field initializer
|
|
25
|
+
- the `;` after each top-level statement, including the last one in a file
|
|
26
|
+
- module-level `import`/`export` statements
|
|
27
|
+
2. **Paragraph / whitespace formatting** per **§A19.7** and **§A19.7b** is **mandatory**, not aesthetic. In particular:
|
|
28
|
+
- **One blank line** between any block-like statement (`if`/`else`, `for`, `while`, `try`/`catch`, `switch`, `function`/arrow body) and the next statement at the same indent level.
|
|
29
|
+
- **One blank line** before any block-like statement when the preceding line is not also a block.
|
|
30
|
+
- **One blank line** before any `return` / `throw` that is not the only statement in its enclosing block.
|
|
31
|
+
- **One blank line** after a cluster of consecutive `const`/`let`/`var` declarations before any non-declaration paragraph (no blank line **between** declarations within the cluster).
|
|
32
|
+
- **No** blank line at the very first or last line inside `{ … }` (no padded blocks — **§A19.8**).
|
|
33
|
+
- **At most one** consecutive blank line anywhere (**§A19.9**).
|
|
34
|
+
3. **Always use braces around control-statement bodies.** Every `if`, `else`, `else if`, `for`, `for…of`, `for…in`, `while`, `do…while`, `try`, `catch`, and `finally` body is wrapped in `{ … }` — **including single-statement bodies**. This **overrides** Airbnb **§A16.1**'s one-line allowance (`if (test) return false;` is forbidden). It matches Google JS Style §5.5.1 and ESLint `curly: ['error', 'all']`. Rationale: removes the entire class of "added a second statement, forgot to add braces" bugs, makes diffs single-purpose, and keeps the cuddled `} else {` shape (**§A16.2**) usable everywhere.
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// good: semicolons + paragraph spacing + always braces
|
|
38
|
+
const compute = (a, op, b) => {
|
|
39
|
+
switch (op) {
|
|
40
|
+
case '+':
|
|
41
|
+
return a + b;
|
|
42
|
+
case '-':
|
|
43
|
+
return a - b;
|
|
44
|
+
case '*':
|
|
45
|
+
return a * b;
|
|
46
|
+
case '/':
|
|
47
|
+
return b === 0 ? NaN : a / b;
|
|
48
|
+
default:
|
|
49
|
+
return b;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const handleOperator = (op) => {
|
|
54
|
+
if (state.current === 'Error') {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const currentNum = Number(state.current);
|
|
59
|
+
const hasPendingChain = state.previous !== null && state.operator !== null && !state.justEvaluated;
|
|
60
|
+
|
|
61
|
+
if (hasPendingChain) {
|
|
62
|
+
const result = compute(state.previous, state.operator, currentNum);
|
|
63
|
+
|
|
64
|
+
state.previous = result;
|
|
65
|
+
state.current = String(result);
|
|
66
|
+
} else {
|
|
67
|
+
state.previous = currentNum;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
state.operator = op;
|
|
71
|
+
state.justEvaluated = true;
|
|
72
|
+
|
|
73
|
+
render();
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// bad: no semicolons, no paragraph spacing, single-line ifs without braces
|
|
79
|
+
const handleOperator = (op) => {
|
|
80
|
+
if (state.current === 'Error') return
|
|
81
|
+
const currentNum = Number(state.current)
|
|
82
|
+
if (state.previous !== null && state.operator !== null && !state.justEvaluated) {
|
|
83
|
+
const result = compute(state.previous, state.operator, currentNum)
|
|
84
|
+
state.previous = result
|
|
85
|
+
state.current = String(result)
|
|
86
|
+
} else {
|
|
87
|
+
state.previous = currentNum
|
|
88
|
+
}
|
|
89
|
+
state.operator = op
|
|
90
|
+
state.justEvaluated = true
|
|
91
|
+
render()
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
// bad: braces missing on single-statement bodies (forbidden by Project mandate #3)
|
|
97
|
+
if (state.current === 'Error') return;
|
|
98
|
+
if (visibleDigitCount(state.current) >= MAX_DIGITS) return;
|
|
99
|
+
for (const item of items) handle(item);
|
|
100
|
+
while (queue.length) drain(queue);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
When the **Authority precedence** below resolves a conflict, treat this **Project mandate** as part of "repository config" — it sits **above** §A and §B and **above** any personal/global `user_rule` that contradicts it. The only thing that overrides it is an explicit, scoped instruction in the *current* task ("write this snippet without semicolons because I'm pasting it into a Standard JS project").
|
|
104
|
+
|
|
105
|
+
## Purpose
|
|
106
|
+
|
|
107
|
+
Answer JavaScript implementation questions using **embedded rules below** as the default standard. External URLs are **optional citations**, not prerequisites for routine coding guidance.
|
|
108
|
+
|
|
109
|
+
## When to use
|
|
110
|
+
|
|
111
|
+
- Writing, editing, or reviewing **JavaScript** (browser, Node, bundlers, or TS-interop JS).
|
|
112
|
+
- **Best practices**, style, structure, async, modules, or performance discipline.
|
|
113
|
+
- **Security** basics for JS (XSS, dependency hygiene) at the code level.
|
|
114
|
+
- Explaining **correctness** vs **team convention** vs **popularity**.
|
|
115
|
+
|
|
116
|
+
## When not to use
|
|
117
|
+
|
|
118
|
+
- Primary topic is CSS-only or HTML-only (use sibling skills).
|
|
119
|
+
- Framework-specific patterns unless tied to general JS discipline.
|
|
120
|
+
|
|
121
|
+
## Truthfulness and claims
|
|
122
|
+
|
|
123
|
+
- Do **not** invent survey percentages, download counts, npm rankings, or traffic stats.
|
|
124
|
+
- Do **not** declare a global "#1 tutorial" without defining metric and source—and prefer saying surveys measure **self-reported use**, not quality.
|
|
125
|
+
- Label third-party traffic metrics as **estimates**.
|
|
126
|
+
- For ambiguous edge behavior, prefer **"verify with a minimal repro + runtime"** over guessing.
|
|
127
|
+
- §A1–§A27 is the **embedded Airbnb default contract** for this skill. Airbnb is named because the rules are reproduced from the public guide as an authoritative-by-default layout/language standard, not because the skill claims it is empirically "most used."
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Self-contained operating mode
|
|
132
|
+
|
|
133
|
+
- **Default:** follow **Embedded implementation standard** in this file for all recommendations.
|
|
134
|
+
- **Do not** rely on parsing optional reference URLs to decide routine style, async patterns, or safety rules.
|
|
135
|
+
- Use external links **only** when:
|
|
136
|
+
- the user asks for citations or "official docs",
|
|
137
|
+
- verifying a rare engine/spec edge case,
|
|
138
|
+
- confirming year/version-specific survey wording.
|
|
139
|
+
|
|
140
|
+
## Authority precedence (highest first)
|
|
141
|
+
|
|
142
|
+
1. **Explicit user instruction in the current task** (scoped, in-context — not a generic `user_rule` from another conversation/profile).
|
|
143
|
+
2. **Project mandate (non-negotiable)** above — semicolons + paragraph spacing. Overrides generic global `user_rule`s that disagree.
|
|
144
|
+
3. **Repository config:** ESLint, `.editorconfig`, Prettier, Biome, `tsconfig.json`. **Repo wins** over §A/§B for any rule it covers (and is expected to encode the Project mandate via `semi`, `padding-line-between-statements`, etc.).
|
|
145
|
+
4. **§A1–§A27** (embedded Airbnb defaults) for layout, language, naming, modules, comparison, control flow, naming, etc.
|
|
146
|
+
5. **§B1–§B9** for topics Airbnb does not cover.
|
|
147
|
+
6. **Optional citations** (external URLs at the bottom)—only when user asks or verification is explicitly needed.
|
|
148
|
+
|
|
149
|
+
When repo or user disagrees with §A or §B, follow them and note the deviation succinctly. **Do not** treat a generic global "no-semicolons" `user_rule` as overriding the Project mandate; only an explicit, in-task instruction does.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Embedded implementation standard
|
|
154
|
+
|
|
155
|
+
The §A sections below mirror the public **Airbnb JavaScript Style Guide** ([github.com/airbnb/javascript](https://github.com/airbnb/javascript)) section by section, in its own numbering, so agents can apply every rule without opening the URL. Section numbers (e.g. *§A2.1*) match the Airbnb numbering in the upstream README.
|
|
156
|
+
|
|
157
|
+
> **Convention in code examples below:** `// good` blocks are normative; `// bad` blocks are anti-patterns. Apply the `// good` form unless authority precedence overrides it.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### §A1) Types
|
|
162
|
+
|
|
163
|
+
- **§A1.1 Primitives** (`string`, `number`, `boolean`, `null`, `undefined`, `symbol`, `bigint`) — accessed by **value**. `Symbol` and `BigInt` cannot be faithfully polyfilled; do **not** use them when targeting environments that lack native support.
|
|
164
|
+
- **§A1.2 Complex** (`object`, `array`, `function`) — accessed by **reference**. Mutating a reference mutates every binding that points at it.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### §A2) References
|
|
169
|
+
|
|
170
|
+
- **§A2.1** Use **`const`** for all references; avoid **`var`**. eslint: `prefer-const`, `no-const-assign`.
|
|
171
|
+
- **§A2.2** If a reference must be reassigned, use **`let`**, not `var`. eslint: `no-var`.
|
|
172
|
+
- **§A2.3** Both `let` and `const` are **block-scoped**; `var` is function-scoped. Prefer block scope.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### §A3) Objects
|
|
177
|
+
|
|
178
|
+
- **§A3.1** Use object **literal** syntax (`{}`), not `new Object()`. eslint: `no-new-object`.
|
|
179
|
+
- **§A3.2** Use **computed property names** when keys are dynamic so all properties are defined in one literal: `{ [getKey('enabled')]: true }`.
|
|
180
|
+
- **§A3.3** Use **method shorthand**: `{ addValue(value) { … } }` instead of `addValue: function (value) { … }`. eslint: `object-shorthand`.
|
|
181
|
+
- **§A3.4** Use **property value shorthand**: `{ lukeSkywalker }` instead of `{ lukeSkywalker: lukeSkywalker }`. eslint: `object-shorthand`.
|
|
182
|
+
- **§A3.5** **Group shorthand keys first** in object literals: shorthand entries on top, then keyed properties.
|
|
183
|
+
- **§A3.6** Quote keys **only** when the key is not a valid identifier (e.g. `'data-blah'`). eslint: `quote-props`.
|
|
184
|
+
- **§A3.7** Do **not** call `Object.prototype` methods directly (`obj.hasOwnProperty(k)`). Use `Object.prototype.hasOwnProperty.call(obj, k)`; `Object.hasOwn(obj, k)` is acceptable when the project targets ES2022+ explicitly. eslint: `no-prototype-builtins`.
|
|
185
|
+
- **§A3.8** Prefer **object spread** (`{ ...a, c: 3 }`) over `Object.assign({}, a, { c: 3 })`; use **rest** (`const { a, ...rest } = obj`) to omit properties. eslint: `prefer-object-spread`.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### §A4) Arrays
|
|
190
|
+
|
|
191
|
+
- **§A4.1** Use array **literal** syntax (`[]`), not `new Array()`. eslint: `no-array-constructor`.
|
|
192
|
+
- **§A4.2** Use **`Array#push`** instead of `arr[arr.length] = x`.
|
|
193
|
+
- **§A4.3** Use array **spread** (`[...items]`) to copy arrays.
|
|
194
|
+
- **§A4.4** Convert iterable objects to arrays with spread: `[...nodeList]` (preferred) or `Array.from(nodeList)`.
|
|
195
|
+
- **§A4.5** Convert array-like (non-iterable) objects with **`Array.from(arrLike)`**, not `Array.prototype.slice.call(...)`.
|
|
196
|
+
- **§A4.6** Prefer **`Array.from(iter, mapFn)`** over `[...iter].map(mapFn)` to avoid an intermediate array.
|
|
197
|
+
- **§A4.7** **Use `return` in array-method callbacks.** Implicit return is allowed when the callback body is a single expression with no side effects (see **§A8.2**); otherwise return an explicit value. eslint: `array-callback-return`.
|
|
198
|
+
- **§A4.8** Multiline arrays: line break **after `[`** and **before `]`**, one item per line, trailing comma on the last item. Single-line arrays stay on one line.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### §A5) Destructuring
|
|
203
|
+
|
|
204
|
+
- **§A5.1** Use **object destructuring** when accessing **multiple** properties of the same object. Best: destructure in the parameter list when the function consumes a fixed shape. eslint: `prefer-destructuring`.
|
|
205
|
+
- **§A5.2** Use **array destructuring** for fixed-position reads. eslint: `prefer-destructuring`.
|
|
206
|
+
- **§A5.3** For functions returning multiple values, return an **object** and destructure at the call site (callers pick the names they need)—never return positional arrays.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### §A6) Strings
|
|
211
|
+
|
|
212
|
+
- **§A6.1** Use **single quotes** `'…'` for string literals. eslint: `quotes`.
|
|
213
|
+
- **§A6.2** Strings that exceed 100 columns **must not** be split with `\` line continuations or `+` concatenation; keep the literal on one line so it stays searchable.
|
|
214
|
+
- **§A6.3** When **building strings programmatically**, use **template literals**, not `+` concatenation or `.join()` on arrays of fragments. No padding inside `${…}`. eslint: `prefer-template`, `template-curly-spacing`.
|
|
215
|
+
- **§A6.4** Never use **`eval()`** on a string. eslint: `no-eval`.
|
|
216
|
+
- **§A6.5** Do **not** unnecessarily escape characters in strings. eslint: `no-useless-escape`.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
### §A7) Functions
|
|
221
|
+
|
|
222
|
+
- **§A7.1** **Function form selection (clarity first).** Prefer `const`-assigned functions for local behavior; use **arrow functions by default** (`const parseUser = (raw) => { … };`). Use `const fn = function fnName(…) { … };` only when an internal self-name is needed (e.g., recursion in expression position) or for explicit stack-trace labeling. Prefer **function declarations** for top-level exported/shared utilities when hoisting or declaration-style readability improves call flow (`export function buildPayload(input) { … }`). Do **not** force dual naming (`const short = function longName(){}`) unless there is a concrete debugging benefit. Every function must have a meaningful inferred or explicit name; avoid anonymous functions in non-trivial code paths. Lint intent: keep `func-style` and `func-names`, but configure them to allow idiomatic arrow functions and declaration usage by role. eslint: `func-style`, `func-names`.
|
|
223
|
+
- **§A7.2** Wrap IIFEs in parentheses: `(function () { … }());`. (In a module-first world, IIFEs are rarely needed.) eslint: `wrap-iife`.
|
|
224
|
+
- **§A7.3 / §A7.4** Never declare a function inside a non-function block (`if`, `while`, etc.). Assign an arrow function to a variable instead. eslint: `no-loop-func`.
|
|
225
|
+
- **§A7.5** Never name a parameter **`arguments`**.
|
|
226
|
+
- **§A7.6** Never use the legacy **`arguments`** object; use rest syntax **`...args`**. eslint: `prefer-rest-params`.
|
|
227
|
+
- **§A7.7** Use **default parameter syntax** (`function f(opts = {})`); never reassign a parameter to `param = param || {}`.
|
|
228
|
+
- **§A7.8** **Avoid side effects** in default parameter expressions.
|
|
229
|
+
- **§A7.9** Default parameters always come **last** in the parameter list. eslint: `default-param-last`.
|
|
230
|
+
- **§A7.10** Never use the **`Function`** constructor (`new Function('…')`). eslint: `no-new-func`.
|
|
231
|
+
- **§A7.11 Function signature spacing.** Anonymous `function`: **one space** before `(` (`function () {}`). Named function declarations and calls: **no** space between name and `(` (`function foo() {}`, `foo(arg)`). eslint: `space-before-function-paren`, `space-before-blocks`.
|
|
232
|
+
- **§A7.12** Never **mutate** parameter properties (`obj.key = 1` on a parameter). eslint: `no-param-reassign`. Idiomatic exceptions Airbnb config keeps: reducer accumulators (`acc`, `accumulator`).
|
|
233
|
+
- **§A7.13** Never **reassign** parameters (`a = 1`). To produce a default, declare a new local: `const b = a || 1;` or use **§A7.7** default params. eslint: `no-param-reassign`.
|
|
234
|
+
- **§A7.14** Prefer **spread** `fn(...arr)` over `fn.apply(ctx, arr)`. eslint: `prefer-spread`.
|
|
235
|
+
- **§A7.15 Multiline signatures and invocations.** When signature/call wraps, place each argument on its own line with a **trailing comma** after the last argument and `)` on its own line aligned with the opening keyword. eslint: `function-paren-newline`.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### §A8) Arrow Functions
|
|
240
|
+
|
|
241
|
+
- **§A8.1** When passing an anonymous callback, use **arrow function** notation. eslint: `prefer-arrow-callback`, `arrow-spacing`.
|
|
242
|
+
- **§A8.2 Implicit return.** If the body is a **single expression** with **no side effects**, omit braces and rely on implicit return: `(n) => n + 1`. If the body has side effects or multiple statements, use braces and an explicit `return`. eslint: `arrow-body-style`.
|
|
243
|
+
- **§A8.3** When an implicit-return expression spans multiple lines, **wrap it in parentheses** so the function's extent is obvious.
|
|
244
|
+
- **§A8.4** **Always parenthesize parameters** in arrow functions, even a single identifier: `(x) => x`, never `x => x`. eslint: `arrow-parens`.
|
|
245
|
+
- **§A8.5** Avoid arrow bodies that visually clash with comparison operators (`<=`, `>=`). Either wrap the body in `()` or use a block. eslint: `no-confusing-arrow`.
|
|
246
|
+
- **§A8.6** Do **not** place a line break immediately after `=>` followed by a bare expression on the next line; if you must wrap, surround the expression in `()`. eslint: `implicit-arrow-linebreak`.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
### §A9) Classes & Constructors
|
|
251
|
+
|
|
252
|
+
- **§A9.1** Use **`class`**; avoid manipulating `prototype` directly.
|
|
253
|
+
- **§A9.2** Use **`extends`** for inheritance—not `inherits`-style helpers.
|
|
254
|
+
- **§A9.3** Methods may **return `this`** to support chaining; do so consistently when chaining is part of the API.
|
|
255
|
+
- **§A9.4** Custom **`toString()`** is fine if it is side-effect-free and reliable.
|
|
256
|
+
- **§A9.5** Omit **empty constructors** and constructors that only delegate to `super(...args)`. eslint: `no-useless-constructor`.
|
|
257
|
+
- **§A9.6** Avoid **duplicate class members**. eslint: `no-dupe-class-members`.
|
|
258
|
+
- **§A9.7** Class methods should **use `this`** or be marked **`static`** (constructors and library-mandated method shapes are exempt). eslint: `class-methods-use-this`.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### §A10) Modules
|
|
263
|
+
|
|
264
|
+
- **§A10.1** Always use **ES modules** (`import`/`export`) over non-standard module systems.
|
|
265
|
+
- **§A10.2** No **wildcard imports**: `import * as ns from '…'` is banned; name the values you need.
|
|
266
|
+
- **§A10.3** Do **not** export directly from an import in one line—do `import { x } from '…'; export default x;` so import/export boundaries stay explicit.
|
|
267
|
+
- **§A10.4** **One import path per module**: merge default and named bindings on the same line (`import foo, { bar, baz } from 'foo';`). eslint: `no-duplicate-imports`.
|
|
268
|
+
- **§A10.5** Do **not** export **mutable** bindings—export `const`, not `let`. eslint: `import/no-mutable-exports`.
|
|
269
|
+
- **§A10.6** Modules with a **single export** should use a **default export**. eslint: `import/prefer-default-export`.
|
|
270
|
+
- **§A10.7** Place **all `import`s above** non-import statements. eslint: `import/first`.
|
|
271
|
+
- **§A10.8 Multiline imports** follow the same shape as multiline objects: one name per line, trailing comma, closing `}` on its own line aligned with `import`. eslint: `object-curly-newline`.
|
|
272
|
+
- **§A10.9** No bundler-loader syntax in imports (e.g. `'css!sass!foo.scss'`); configure loaders in the bundler config. eslint: `import/no-webpack-loader-syntax`.
|
|
273
|
+
- **§A10.10** Do **not** include `.js` / `.jsx` / `.ts` / `.tsx` extensions in relative import paths. eslint: `import/extensions`. (Projects that explicitly opt into `.mjs`/`.mts` resolution may keep extensions; document the deviation.)
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
### §A11) Iterators and Generators
|
|
278
|
+
|
|
279
|
+
- **§A11.1 Don't use iterators directly.** Prefer higher-order array methods—**`map`**, **`every`**, **`filter`**, **`find`**, **`findIndex`**, **`reduce`**, **`some`**, **`forEach`**—over `for…of`/`for…in` for arrays. For object iteration, derive arrays via `Object.keys` / `Object.values` / `Object.entries`. eslint: `no-iterator`, `no-restricted-syntax`.
|
|
280
|
+
- **§A11.2** **Avoid generators** for now (they don't transpile cleanly to ES5).
|
|
281
|
+
- **§A11.3** If a generator is required, the spacing is **`function* foo() {}`** (the `*` cuddles `function`). eslint: `generator-star-spacing`.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
### §A12) Properties
|
|
286
|
+
|
|
287
|
+
- **§A12.1** Use **dot notation** for static keys: `luke.jedi`. eslint: `dot-notation`.
|
|
288
|
+
- **§A12.2** Use **bracket notation** when the key comes from a variable: `luke[prop]`.
|
|
289
|
+
- **§A12.3** Use the **exponentiation operator** `**`: `2 ** 10`, not `Math.pow(2, 10)`. eslint: `prefer-exponentiation-operator`.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
### §A13) Variables
|
|
294
|
+
|
|
295
|
+
- **§A13.1** Always declare with **`const`** or **`let`**; never rely on implicit globals. eslint: `no-undef`, `prefer-const`.
|
|
296
|
+
- **§A13.2** **One `const`/`let` per declaration**—never `const a = 1, b = 2;`. eslint: `one-var: ['error', 'never']`.
|
|
297
|
+
- **§A13.3** **Group `const`s first, then `let`s** at the top of a logical scope when they cluster.
|
|
298
|
+
- **§A13.4** **Place declarations close to first use.** Don't pre-declare everything at function top when a guard clause may return early.
|
|
299
|
+
- **§A13.5** **No chained assignments** (`a = b = c = 1`)—they create implicit globals. eslint: `no-multi-assign`.
|
|
300
|
+
- **§A13.6** **No `++` / `--`**—use `x += 1` / `x -= 1`. ASI hazards and pre/post confusion outweigh brevity. eslint: `no-plusplus`.
|
|
301
|
+
- **§A13.7** Avoid line breaks around **`=`** in assignments. If `max-len` would be violated, wrap the **right-hand side in parentheses** and break inside them. eslint: `operator-linebreak`.
|
|
302
|
+
- **§A13.8** **Disallow unused variables.** Note: object destructuring with rest naturally "uses" omitted keys: `const { type, ...coords } = data;` is fine. eslint: `no-unused-vars`.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### §A14) Hoisting
|
|
307
|
+
|
|
308
|
+
- **§A14.1** `var` declarations hoist to the enclosing function; their assignment does not. `const` and `let` are hoisted into a **Temporal Dead Zone** until initialized—`typeof` against an unitialized `let`/`const` throws. Treat `var` as legacy.
|
|
309
|
+
- **§A14.2** Anonymous function expressions hoist the **variable name**, not the function value (the variable is `undefined` until assigned).
|
|
310
|
+
- **§A14.3** Named function expressions hoist the **variable name**, not the lexical function name or body.
|
|
311
|
+
- **§A14.4** **Function declarations** hoist both the name and the body—still, prefer named expressions per **§A7.1**.
|
|
312
|
+
- **§A14.5 `no-use-before-define`.** Reference variables, classes, and functions **after** their declaration. **Closure exception:** referencing a `const` inside its own body via a deferred callback (e.g. `addEventListener` that re-invokes the same function later) is runtime-safe; the rule targets immediate same-tick use, not callbacks. eslint: `no-use-before-define`.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
### §A15) Comparison Operators & Equality
|
|
317
|
+
|
|
318
|
+
- **§A15.1** Use **`===`** / **`!==`** over `==` / `!=`. eslint: `eqeqeq`.
|
|
319
|
+
- **§A15.2 ToBoolean rules.** Conditionals coerce: `objects` → true, `undefined`/`null` → false, booleans → their value, numbers → false on `+0`, `-0`, `NaN`, otherwise true; strings → false on `''`, otherwise true.
|
|
320
|
+
- **§A15.3 Comparison shortcuts.** Booleans use the shortcut; strings and numbers use **explicit** comparisons:
|
|
321
|
+
- **Booleans:** `if (isValid)` ✓, `if (isValid === true)` ✗.
|
|
322
|
+
- **Strings:** `if (name !== '')` ✓, `if (name)` ✗.
|
|
323
|
+
- **Numbers:** `if (collection.length > 0)` ✓, `if (collection.length)` ✗.
|
|
324
|
+
- **§A15.4** For the deeper rationale, see *Truth, Equality, and JavaScript* by Angus Croll.
|
|
325
|
+
- **§A15.5** In `switch`, when a `case`/`default` introduces lexical declarations (`let`, `const`, `function`, `class`), wrap the case body in **`{ … }`**. eslint: `no-case-declarations`.
|
|
326
|
+
- **§A15.6** Ternaries should **not nest**; prefer an intermediate `const` or split the logic. eslint: `no-nested-ternary`.
|
|
327
|
+
- **§A15.7** **Avoid unnecessary ternaries**: `a || b` instead of `a ? a : b`; `!!c` instead of `c ? true : false`; `a ?? b` instead of `a != null ? a : b`. eslint: `no-unneeded-ternary`.
|
|
328
|
+
- **§A15.8** When mixing operators, **parenthesize** for precedence clarity. The only safe-without-parens combination is straight arithmetic with `+`, `-`, `**`. Mixing `&&` with `||`, or `*`/`/` with `+`/`-`, requires parens. eslint: `no-mixed-operators`.
|
|
329
|
+
- **§A15.9** Use **`??`** (nullish coalescing) when only `null`/`undefined` should fall back. Use `||` only when **all** falsy values (including `0`, `''`, `false`) should fall back.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### §A16) Blocks
|
|
334
|
+
|
|
335
|
+
- **§A16.1 Always use braces (project mandate — overrides Airbnb default).** Every `if`, `else`, `else if`, `for`, `for…of`, `for…in`, `while`, `do…while`, `try`, `catch`, and `finally` wraps its body in `{ … }` — **including single-statement bodies**. The "single-statement form on one line is allowed" allowance from upstream Airbnb is **disabled** in this project; it is the source of recurring "added a second statement, forgot to add braces" bugs and conflicts with modern best practice (Google JS Style §5.5.1, ESLint `curly: ['error', 'all']`). Examples below. eslint: `curly: ['error', 'all']`, `nonblock-statement-body-position`.
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
// good: braces on every body, even single statements
|
|
339
|
+
if (state.current === 'Error') {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (visibleDigitCount(state.current) >= MAX_DIGITS) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
for (const item of items) {
|
|
348
|
+
handle(item);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
while (queue.length > 0) {
|
|
352
|
+
drain(queue);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (ok) {
|
|
356
|
+
apply();
|
|
357
|
+
} else {
|
|
358
|
+
rollback();
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```js
|
|
363
|
+
// bad: single-line body without braces
|
|
364
|
+
if (state.current === 'Error') return;
|
|
365
|
+
for (const item of items) handle(item);
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```js
|
|
369
|
+
// bad: split across two lines without braces (always wrong, even in upstream Airbnb)
|
|
370
|
+
if (test)
|
|
371
|
+
doThing();
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
- **§A16.2 Cuddled `else`.** With `if`/`else`, place `else` on the same line as the closing brace of `if`: `} else {`. (Because §A16.1 mandates braces, this shape applies to **every** `if`/`else`, not only "multiline" ones.) eslint: `brace-style`.
|
|
375
|
+
- **§A16.3 `no-else-return`.** If an `if` block always `return`s, drop the subsequent `else` and dedent its body. The same applies to `else if` chains where each branch returns. eslint: `no-else-return`.
|
|
376
|
+
|
|
377
|
+
```js
|
|
378
|
+
// good
|
|
379
|
+
if (a) {
|
|
380
|
+
return x;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return y;
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
```js
|
|
387
|
+
// bad
|
|
388
|
+
if (a) {
|
|
389
|
+
return x;
|
|
390
|
+
} else {
|
|
391
|
+
return y;
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
### §A17) Control Statements
|
|
398
|
+
|
|
399
|
+
- **§A17.1 Long control conditions.** When an `if`/`while` condition exceeds line length, break each (grouped) condition onto a new line with the **logical operator at the start** of the continuation line, and place `(` and `)` on their own lines:
|
|
400
|
+
```javascript
|
|
401
|
+
// good
|
|
402
|
+
if (
|
|
403
|
+
foo === 123
|
|
404
|
+
&& bar === 'abc'
|
|
405
|
+
) {
|
|
406
|
+
thing1();
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
Both `&&` and `||` go at the **start** of continuation lines, never dangling at the end. eslint: per Airbnb config.
|
|
410
|
+
- **§A17.2 Don't use selection operators in place of control statements.** `!isRunning && startRunning();` is wrong; use `if (!isRunning) { startRunning(); }`.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
### §A18) Comments
|
|
415
|
+
|
|
416
|
+
- **§A18.1** Use **`/** … */`** for **multiline** comments. Each inner line starts with `*` and a single space.
|
|
417
|
+
- **§A18.2** Use **`//`** for **single-line** comments, placed on a **new line above** the subject. Leave a **blank line before** the comment unless it is the first line of its block.
|
|
418
|
+
- **§A18.3** Always put **one space** after `//` or `/*`. eslint: `spaced-comment`.
|
|
419
|
+
- **§A18.4** Annotate actions with `FIXME:` and `TODO:` so they're searchable.
|
|
420
|
+
- **§A18.5** `// FIXME:` flags problems that need investigation.
|
|
421
|
+
- **§A18.6** `// TODO:` flags planned solutions / future work.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
### §A19) Whitespace
|
|
426
|
+
|
|
427
|
+
- **§A19.1 Indentation.** Soft tabs of **2 spaces**. Never literal tab characters for indent. eslint: `indent`.
|
|
428
|
+
- **§A19.2** **One space** before the leading `{` of a block (`function test() {`, `dog.set('attr', { … })`). eslint: `space-before-blocks`.
|
|
429
|
+
- **§A19.3 Keyword spacing.** **One space** before `(` in control statements (`if (`, `while (`, `for (`, `switch (`, `catch (`); **no** space between a function name and its `(` in declarations and calls. eslint: `keyword-spacing`.
|
|
430
|
+
- **§A19.4** Set off **infix operators with spaces**: `const x = y + 5;`. eslint: `space-infix-ops`.
|
|
431
|
+
- **§A19.5** End the file with **exactly one** newline character. eslint: `eol-last`.
|
|
432
|
+
- **§A19.6 Long method chains** (3+ calls) get one chained call per line with a **leading dot** on continuations. Don't trail dots at line ends; don't attach long chains to the original method call.
|
|
433
|
+
- **§A19.7 Blank line after blocks.** Always leave **one blank line** after the closing `}` of any block construct before the next statement at the same indent level. This applies to `if`/`else`/`else if`, `for`/`for…of`/`for…in`, `while`/`do…while`, `try`/`catch`/`finally`, `switch`, `function` and arrow `{ … }` bodies, methods inside object literals or arrays of functions, and `class` methods.
|
|
434
|
+
- **Sole exception:** when the block is the **last** statement of its enclosing scope (the next non-blank token is the parent's closing `}`), do **not** add a blank line—it would conflict with `padded-blocks`.
|
|
435
|
+
- **§A19.7b Blank line between paragraphs.** Treat code inside any block as a sequence of **paragraphs**. Insert **exactly one** blank line **between** adjacent paragraphs—**even when the next paragraph immediately consumes a name from the previous one** (e.g. `const x = …;` followed by `if (x === null) return;`). Within a paragraph, **no** blank lines. A *paragraph* is exactly one of:
|
|
436
|
+
1. a **cluster of consecutive `const`/`let`/`var` declarations** (treated as one paragraph),
|
|
437
|
+
2. a single **block-like statement** (`if`/`else if`/`else`, `for`/`for…of`/`for…in`, `while`, `do…while`, `try`/`catch`/`finally`, `switch`),
|
|
438
|
+
3. a **`return`** or **`throw`** statement that ends the function/branch,
|
|
439
|
+
4. a **sequence of effectful expression statements that act on the same target** (e.g. several `el.setAttribute(…)` calls in a row, or a chain of mutations on a freshly created variable inside a tight constructor-style block).
|
|
440
|
+
- **Always-blank** edges (concrete consequences of the paragraph rule):
|
|
441
|
+
- **after** any block-like statement before the next statement at the same indent (this is **§A19.7**),
|
|
442
|
+
- **before** any block-like statement when the previous statement is **not** another block,
|
|
443
|
+
- **before** any `return` / `throw` when it is **not** the only statement of its enclosing block,
|
|
444
|
+
- **after** a declaration cluster before any non-declaration paragraph.
|
|
445
|
+
- **Never-blank** edges:
|
|
446
|
+
- **between** two consecutive `const`/`let`/`var` declarations (they belong to the same cluster),
|
|
447
|
+
- **between** statements inside a tight effectful sequence on the same target (don't pad mid-paragraph),
|
|
448
|
+
- **at** the very first or very last line inside `{ … }` (already covered by **§A19.8** `padded-blocks`).
|
|
449
|
+
- eslint mapping (Airbnb's published config only enforces the **§A19.7** half; this skill enforces the full paragraph rule via `padding-line-between-statements`):
|
|
450
|
+
```js
|
|
451
|
+
'padding-line-between-statements': [
|
|
452
|
+
'error',
|
|
453
|
+
{ blankLine: 'always', prev: 'block-like', next: '*' },
|
|
454
|
+
{ blankLine: 'always', prev: '*', next: 'block-like' },
|
|
455
|
+
{ blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' },
|
|
456
|
+
{ blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] },
|
|
457
|
+
{ blankLine: 'always', prev: '*', next: ['return', 'throw'] },
|
|
458
|
+
],
|
|
459
|
+
```
|
|
460
|
+
- **§A19.8 No padded blocks.** Do **not** start or end a block body with a blank line. eslint: `padded-blocks`.
|
|
461
|
+
- **§A19.9** **At most one** consecutive blank line between logical paragraphs of code. eslint: `no-multiple-empty-lines`.
|
|
462
|
+
- **§A19.10** **No** spaces inside parentheses: `bar(foo)`, not `bar( foo )`. eslint: `space-in-parens`.
|
|
463
|
+
- **§A19.11** **No** spaces inside square brackets: `[1, 2, 3]`, `arr[0]`. eslint: `array-bracket-spacing`.
|
|
464
|
+
- **§A19.12** **One space** inside `{` and `}` of object literals: `{ clark: 'kent' }`. eslint: `object-curly-spacing`.
|
|
465
|
+
- **§A19.13 Max line length.** Avoid lines longer than **100 characters** (counting whitespace). Long string literals from **§A6.2** are exempt. eslint: `max-len`.
|
|
466
|
+
- **§A19.14 Block spacing.** Single-line block contents have spaces inside the braces: `function foo() { return true; }`, `if (foo) { bar = 0; }`. eslint: `block-spacing`.
|
|
467
|
+
- **§A19.15 Comma spacing.** **No** space before `,`, **one** space after. eslint: `comma-spacing`.
|
|
468
|
+
- **§A19.16** **No** spaces inside computed property brackets: `obj[foo]`, `{ [b]: a }`. eslint: `computed-property-spacing`.
|
|
469
|
+
- **§A19.17** **No** space between a function name and its call parens. eslint: `func-call-spacing`.
|
|
470
|
+
- **§A19.18** Object key spacing: **no** space before `:`, **one** space after. eslint: `key-spacing`.
|
|
471
|
+
- **§A19.19** **No trailing spaces** at end of line. eslint: `no-trailing-spaces`.
|
|
472
|
+
- **§A19.20** **No** multiple empty lines, no leading newline at file start, exactly one trailing newline at file end. eslint: `no-multiple-empty-lines`, `eol-last`.
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
### §A20) Commas
|
|
477
|
+
|
|
478
|
+
- **§A20.1** **No leading commas.** eslint: `comma-style`.
|
|
479
|
+
- **§A20.2** **Trailing commas:** required on every multiline object literal, array literal, function parameter list, and function call argument list. Single-line literals do not get a trailing comma. **Rest elements** (`...args`) must not be followed by a comma. eslint: `comma-dangle`.
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
### §A21) Semicolons
|
|
484
|
+
|
|
485
|
+
- **§A21.1 Semicolons are required (project mandate — non-negotiable).** Every statement ends with `;`. Do not rely on Automatic Semicolon Insertion (ASI). Do not use the Standard JS / "no-semi" style. Do not use the leading-`;` defensive style (`;[a, b].forEach(…)`) — that style only exists to compensate for missing terminating semicolons, which this project does not allow.
|
|
486
|
+
- **§A21.2 Where semicolons go (explicit list).** Terminate each of these with `;`:
|
|
487
|
+
- variable declarations: `const x = 1;`, `let y;`
|
|
488
|
+
- expression statements: `foo();`, `obj.prop = value;`
|
|
489
|
+
- `return`, `throw`, `break`, `continue`, `do…while`
|
|
490
|
+
- `import` / `export` statements (including `export default …;` when the value is an expression — class/function declarations do not need one)
|
|
491
|
+
- the **last** statement in a file (still terminated)
|
|
492
|
+
- **§A21.3 Where semicolons do NOT go.** Do not put `;` after the closing `}` of a `function` declaration, `class` declaration, `if`, `for`, `while`, `switch`, `try` block, or labeled block. (`const fn = function () { … };` and `const fn = () => { … };` *do* end with `;` because they are variable-declaration statements, not function declarations.)
|
|
493
|
+
- **§A21.4 Common ASI traps** (all of which are avoided automatically by §A21.1):
|
|
494
|
+
- Statements that begin with `(`, `[`, or backtick are eaten by the previous statement if it ends without `;`.
|
|
495
|
+
- `return\n value;` returns `undefined`.
|
|
496
|
+
- `const a = b\n(c).d()` parses as `const a = b(c).d()`.
|
|
497
|
+
- **§A21.5** eslint: `semi: ['error', 'always']`, `no-unexpected-multiline`.
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
### §A22) Type Casting & Coercion
|
|
502
|
+
|
|
503
|
+
- **§A22.1** Coerce at the **start** of the statement, not midway.
|
|
504
|
+
- **§A22.2 Strings:** use **`String(x)`**. Never `new String(x)`, `x + ''`, or `x.toString()` (the last is not guaranteed to return a string). eslint: `no-new-wrappers`.
|
|
505
|
+
- **§A22.3 Numbers:** use **`Number(x)`** for casting and **`parseInt(x, 10)`** with an explicit radix for parsing. Never `new Number(x)`, `+x`, `x >> 0`, or `parseInt(x)` without a radix. eslint: `radix`, `no-new-wrappers`.
|
|
506
|
+
- **§A22.4** If you must use bitshift for performance, leave a `/** … */` comment explaining why.
|
|
507
|
+
- **§A22.5** Bitshift always returns a **32-bit integer**; large values overflow.
|
|
508
|
+
- **§A22.6 Booleans:** use **`Boolean(x)`** or **`!!x`**. Never `new Boolean(x)`. eslint: `no-new-wrappers`.
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
### §A23) Naming Conventions
|
|
513
|
+
|
|
514
|
+
- **§A23.1** **Avoid single-letter names.** Be descriptive (`item`, `entry`, `event`). eslint: `id-length`.
|
|
515
|
+
- **§A23.2** **`camelCase`** for variables, functions, and instances. eslint: `camelcase`.
|
|
516
|
+
- **§A23.3** **`PascalCase`** only for constructors and classes. eslint: `new-cap`.
|
|
517
|
+
- **§A23.4** **No leading or trailing underscores** to fake "private" (`_foo`, `foo_`, `__foo__`). JavaScript has no privacy via underscore convention; use `WeakMap` or `#private` fields when real privacy is needed. eslint: `no-underscore-dangle`.
|
|
518
|
+
- **§A23.5** Don't save a reference to **`this`** (`const self = this;`). Use arrow functions or `Function#bind`.
|
|
519
|
+
- **§A23.6 Filename matches default export.** `class CheckBox` → `CheckBox.js`; `function fortyTwo()` → `fortyTwo.js`.
|
|
520
|
+
- **§A23.7** Use **`camelCase`** when default-exporting a function; the filename matches the function name.
|
|
521
|
+
- **§A23.8** Use **`PascalCase`** when default-exporting a constructor / class / singleton / function library / bare object.
|
|
522
|
+
- **§A23.9 Acronyms** are either **all uppercase** or **all lowercase** consistently: `HTTPRequests`, `httpRequests`. Never `HttpRequests`.
|
|
523
|
+
- **§A23.10** **`UPPER_SNAKE_CASE`** is **only** for constants that are (1) **exported**, (2) declared with `const`, and (3) trustably immutable through nested properties. **Module-private** constants stay **`camelCase`** (`const storageKey = '…';`). For exported objects, uppercase only the **top-level** name (`MAPPING`), not inner keys.
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
### §A24) Accessors
|
|
528
|
+
|
|
529
|
+
- **§A24.1** Property accessor functions are **not required**.
|
|
530
|
+
- **§A24.2** Do **not** use JS `get`/`set` syntax (side effects + testing pain). If accessors are needed, use plain methods: `getVal()`, `setVal('hello')`.
|
|
531
|
+
- **§A24.3 Boolean accessors** are named **`isVal()`** or **`hasVal()`**.
|
|
532
|
+
- **§A24.4** Generic **`get(key)`** / **`set(key, val)`** is acceptable when the API is consistent across the type.
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
### §A25) Events
|
|
537
|
+
|
|
538
|
+
- **§A25.1** When attaching data to events, **pass an object** (a "hash"), not a raw value:
|
|
539
|
+
- Bad: `trigger('listingUpdated', listing.id)` → `(e, listingID) => { … }`.
|
|
540
|
+
- Good: `trigger('listingUpdated', { listingID: listing.id })` → `(e, data) => { /* data.listingID */ }`.
|
|
541
|
+
|
|
542
|
+
This way, future contributors can add fields without rewriting every handler.
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
### §A26) Standard Library
|
|
547
|
+
|
|
548
|
+
- **§A26.1** Use **`Number.isNaN`**, not the global `isNaN`. The global coerces—`isNaN('1.2.3')` is `true`, `Number.isNaN('1.2.3')` is `false`. eslint: `no-restricted-globals`.
|
|
549
|
+
- **§A26.2** Use **`Number.isFinite`**, not the global `isFinite`, for the same coercion reason. eslint: `no-restricted-globals`.
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
### §A27) Testing
|
|
554
|
+
|
|
555
|
+
- Write tests. Pick a framework you can stick with (Airbnb mentions Mocha, Jest, Tape).
|
|
556
|
+
- Strive for **many small pure functions**; isolate mutations.
|
|
557
|
+
- Be cautious with stubs and mocks—they make tests brittle.
|
|
558
|
+
- 100 % coverage is a goal, not always reachable.
|
|
559
|
+
- Every bug fix gets a **regression test**.
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## §A-Skipped sections
|
|
564
|
+
|
|
565
|
+
The Airbnb sections **jQuery (§26)**, **ECMAScript 5 Compatibility (§27)**, **ECMAScript 6+ Style index (§28)**, **Performance (§32)**, **Resources / In the Wild / Translation / License / Amendments** are intentionally **not** reproduced here—they are either deprecated in modern codebases or non-normative. For Performance, see **§B5** below; for runtime hygiene, see **§B6**.
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## §B) Additional rules beyond Airbnb (correctness focus)
|
|
570
|
+
|
|
571
|
+
These topics are not covered in Airbnb's guide; this skill adds them as authoritative defaults.
|
|
572
|
+
|
|
573
|
+
### §B1) Nullish and optional access
|
|
574
|
+
|
|
575
|
+
- Use **`?.`** for optional property access and **`?.()`** for optional calls when absence is expected (`crypto?.randomUUID?.()`).
|
|
576
|
+
- Use **`??`** when only `null`/`undefined` should trigger a fallback (see **§A15.9** for the contrast with `||`).
|
|
577
|
+
- Prefer destructuring with defaults (`const { count = 0 } = props`) over `props.count || 0`.
|
|
578
|
+
|
|
579
|
+
### §B2) Async, promises, and concurrency
|
|
580
|
+
|
|
581
|
+
- **No floating promises.** Either `await`, `.then()`/`.catch()`, or explicitly `void promise` with a comment that says why fire-and-forget is intended.
|
|
582
|
+
- Wrap awaited steps that need localized error handling in `try`/`catch`.
|
|
583
|
+
- Use **`Promise.all`** for independent parallel work; **`Promise.allSettled`** when every outcome matters; **`Promise.race`** only when cancellation/timeout semantics are well-understood.
|
|
584
|
+
- Prefer **`AbortController`** for cancellable `fetch` and timer work where supported.
|
|
585
|
+
- `queueMicrotask` vs `setTimeout` are not interchangeable—microtasks run before the next task; misuse causes ordering and starvation bugs.
|
|
586
|
+
|
|
587
|
+
### §B3) Errors and observability
|
|
588
|
+
|
|
589
|
+
- Throw `Error` (or subclasses) with **actionable messages**.
|
|
590
|
+
- Preserve causes when rewrapping: `throw new Error('…', { cause: err })`.
|
|
591
|
+
- Distinguish **programmer errors** (bugs—throw / crash) from **operational errors** (network, validation—handle locally).
|
|
592
|
+
- Avoid empty `catch` blocks; at minimum log via `console.debug` with context, or rethrow.
|
|
593
|
+
- For libraries, document thrown shapes and error codes in JSDoc/types.
|
|
594
|
+
|
|
595
|
+
### §B4) Browser / DOM discipline
|
|
596
|
+
|
|
597
|
+
- DOM `id` hooks queried from `.js`/`.mjs` use the **`js-…`** prefix (cross-ref **html** skill §10). The `id` string is identical in markup and `getElementById('js-…')` / `querySelector('#js-…')`.
|
|
598
|
+
- Treat DOM input as **untrusted**: avoid `innerHTML` with interpolated user strings; prefer `textContent`, `setAttribute` with explicit sanitation when HTML is required.
|
|
599
|
+
- Batch **read/write** DOM work to avoid layout thrashing (read measurements together, then write styles together).
|
|
600
|
+
- Prefer **`addEventListener`** over inline `onClick=` etc.
|
|
601
|
+
- `passive: true` on `scroll`/`touch` listeners when not calling `preventDefault`.
|
|
602
|
+
- Use **`requestAnimationFrame`** for visual updates tied to frames; avoid `setInterval` for animation.
|
|
603
|
+
- Debounce/throttle expensive `resize`/`scroll`/`pointermove` handlers.
|
|
604
|
+
|
|
605
|
+
### §B5) Performance and delivery (browser apps)
|
|
606
|
+
|
|
607
|
+
- Ship only what the route/feature needs; lazy `import()` for heavy optional code.
|
|
608
|
+
- Avoid long synchronous main-thread work; chunk it or move CPU-heavy pure work to a Web Worker when profiling justifies it.
|
|
609
|
+
- Mind closure captures of large objects in hot paths if profiling shows retention issues.
|
|
610
|
+
|
|
611
|
+
### §B6) Node and runtime hygiene
|
|
612
|
+
|
|
613
|
+
- Prefer **non-blocking** I/O; avoid synchronous filesystem calls on request paths.
|
|
614
|
+
- Prefer **promise-based** APIs over callback-style for new code.
|
|
615
|
+
- Use environment variables for secrets; never hardcode credentials.
|
|
616
|
+
- Use **`path`** / **`URL`** helpers instead of string concatenation for paths.
|
|
617
|
+
|
|
618
|
+
### §B7) Security baseline (JS angle)
|
|
619
|
+
|
|
620
|
+
- Assume **XSS** unless escaped/sanitized; framework escaping rules still require discipline with `dangerouslySetInnerHTML`-style escape hatches.
|
|
621
|
+
- Audit dependencies (`npm audit`, lockfiles, minimal supply chain).
|
|
622
|
+
- Treat `eval`, `new Function`, and dynamic `import()` from variable URLs as last resorts gated by validation and threat modeling.
|
|
623
|
+
|
|
624
|
+
### §B8) Tooling alignment (without opening docs)
|
|
625
|
+
|
|
626
|
+
- **ESLint** encodes correctness and consistency; project rules override this skill when configured.
|
|
627
|
+
- When **no formatter** is configured, the agent satisfies **§A19** (whitespace), **§A20** (commas), **§A21** (semicolons), and the rest of §A by hand.
|
|
628
|
+
- When **Prettier** or **Biome** is configured, it owns mechanical formatting—**do not fight the formatter**; change the config if the team agrees.
|
|
629
|
+
- Use **at most one** primary print pipeline per repo so ESLint stylistic rules don't contradict the formatter; pair with `eslint-config-prettier` or Biome's ESLint integration to disable layout duplication.
|
|
630
|
+
- **TypeScript** (`allowJs`, `checkJs`, or `.ts`): prefer explicit types at boundaries; avoid `any` except as isolated escape hatches with comments.
|
|
631
|
+
|
|
632
|
+
### §B9) Static HTML documents (file separation)
|
|
633
|
+
|
|
634
|
+
- When the deliverable includes `.html`, implement behavior in **one or more `.js` or `.mjs` files** linked via `<script src="…" defer>` or `type="module"`.
|
|
635
|
+
- Do **not** place multi-line application logic, data models, or feature code in inline `<script>` blocks—keeps caching, readability, and Content-Security-Policy practices aligned with production.
|
|
636
|
+
- **Exception:** environments that genuinely cannot fetch external scripts (some embeds, email, single-file demos). Default is external `.js`.
|
|
637
|
+
- Element `id` hooks: queried from these scripts use **`js-…`** in markup (html skill §10).
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
## Authority reasoning (embedded)
|
|
642
|
+
|
|
643
|
+
- **ECMAScript specification** is normative for language semantics.
|
|
644
|
+
- **MDN-class documentation** models accurate Web API behavior for browsers.
|
|
645
|
+
- **Vendor articles** (e.g. perf) are guidance, not substitutes for measuring your app.
|
|
646
|
+
- **Lint and format configs** are team law when present; absent formatters do not relax §A or §B.
|
|
647
|
+
- **Surveys** measure respondent mixes and self-report—useful for ecosystem trends, not proof that one tutorial is "best."
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
## Response workflow (for the agent)
|
|
652
|
+
|
|
653
|
+
1. If JavaScript is in scope: **read this skill file** (see **Mandatory application**), then continue.
|
|
654
|
+
2. **Apply §A1–§A27** for layout, language, modules, comparisons, control flow, naming, etc., and **§B1–§B9** for the topics Airbnb does not cover. For `.html` + JS pages, apply **§B9** plus the **html** skill §10 conventions for `js-` script hooks.
|
|
655
|
+
3. **Tie-break using authority precedence:** explicit user instruction → repo config (ESLint, `.editorconfig`, `tsconfig`, Prettier/Biome) → §A → §B.
|
|
656
|
+
4. Offer **optional citations** (table below) only when the user asks or when verification is explicitly needed.
|
|
657
|
+
5. If a deliverable conflicts with a rule for a defensible reason (e.g. a third-party SDK requires a specific signature shape), call out the deviation explicitly.
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
## Optional citations (human-facing verification only)
|
|
662
|
+
|
|
663
|
+
Use these when the user requests links or when double-checking rare spec wording—not as a prerequisite to implement everyday code.
|
|
664
|
+
|
|
665
|
+
| Topic | URL |
|
|
666
|
+
|-------|-----|
|
|
667
|
+
| Airbnb JavaScript Style Guide (full text embedded as **§A1–§A27**) | https://github.com/airbnb/javascript |
|
|
668
|
+
| TC39 / spec hub | https://tc39.es/ |
|
|
669
|
+
| ECMA-262 draft | https://tc39.es/ecma262/ |
|
|
670
|
+
| MDN JS Guide | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide |
|
|
671
|
+
| MDN JS Reference | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference |
|
|
672
|
+
| web.dev JS | https://web.dev/javascript |
|
|
673
|
+
| ESLint docs | https://eslint.org/docs/latest/ |
|
|
674
|
+
| Google JavaScript Style Guide | https://google.github.io/styleguide/jsguide.html |
|
|
675
|
+
| Prettier docs | https://prettier.io/docs/en/ |
|
|
676
|
+
| Biome (formatter + linter) | https://biomejs.dev/ |
|
|
677
|
+
| Standard JS | https://standardjs.com/ |
|
|
678
|
+
| TypeScript docs | https://www.typescriptlang.org/docs/ |
|
|
679
|
+
| Stack Overflow Survey (technology) | https://survey.stackoverflow.co/2025/technology |
|
|
680
|
+
| State of JS (resources) | https://2025.stateofjs.com/en-US/resources/ |
|