frontend-js-best-practices — скилл для Claude Code
Задаёт 17 правил оптимизации и стиля для JavaScript и TypeScript. Помогает писать быстрые циклы, избегать лишних аллокаций и убирать мутации массивов при рефакторинге.
npx skills add sergiodxa/agent-skills --skill frontend-js-best-practices -a claude-codeЧто делает frontend-js-best-practices
Скилл содержит набор инструкций для агента по оптимизации JavaScript и TypeScript кода. Он переключает проверки на алгоритмическую сложность: предлагает собирать Set и Map для поиска за O(1) вместо вложенных includes или find, заменять мутирующий метод sort() на toSorted() и объединять цепочки filter() и map() в один проход цикла for.
Инструкции затрагивают и типизацию. Скилл запрещает утверждения типов через as Type, требуя взамен явные тайпгарды или схемы Zod. Для функций задаётся именованный синтаксис function вместо стрелок, а дефолтные экспорты блокируются везде, кроме маршрутов Remix.
Правила требуют объявлять переменные внутри функций исключительно через let. Из-за этого приверженцам правила линтера prefer-const придётся постоянно спорить с агентом.
Какие задачи решает
- ускорить поиск элементов в массивах заменой на Set и Map
- убрать мутации данных при сортировке через toSorted()
- избавиться от небезопасных приведений типов as Type с помощью Zod
- объединить цепочки filter и map в один проход цикла
- закэшировать обращения к свойствам объектов и чтение из localStorage
Примеры запросов агенту
- Проверь этот модуль на узкие места производительности в циклах и выборках данных.
- Отрефактори функцию обработки заказов, объединив вызовы filter в один проход.
- Убери утверждения as из парсинга ответа API и добавь валидацию через Zod.
Как установить frontend-js-best-practices
1Через Skills CLI (Vercel)
npx skills add sergiodxa/agent-skills --skill frontend-js-best-practices -a claude-code2Вручную: положить файлы
~/.claude/skills/frontend-js-best-practices/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/frontend-js-best-practices/
1Через Skills CLI (Vercel)
npx skills add sergiodxa/agent-skills --skill frontend-js-best-practices -a codex2Вручную: положить файлы
.agents/skills/frontend-js-best-practices/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add sergiodxa/agent-skills --skill frontend-js-best-practices -a cursor2Вручную: положить файлы
.cursor/skills/frontend-js-best-practices/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install sergiodxa/agent-skills frontend-js-best-practices2Вручную: положить файлы
.github/skills/frontend-js-best-practices/SKILL.md1Через Skills CLI (Vercel)
npx skills add sergiodxa/agent-skills --skill frontend-js-best-practices -a gemini-cli2Командой платформы
gemini skills install https://github.com/sergiodxa/agent-skills --path skills/frontend-js-best-practicesВручную: положить файлы
.devin/skills/frontend-js-best-practices/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add sergiodxa/agent-skills --skill frontend-js-best-practices -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о frontend-js-best-practices
Как установить frontend-js-best-practices в Claude Code?
Выполните в терминале команду npx skills add sergiodxa/agent-skills --skill frontend-js-best-practices -a claude-code.
Что делает frontend-js-best-practices?
Он задаёт правила написания кода на JavaScript и TypeScript: заменять O(n) поиски на Map и Set, кэшировать повторяющиеся вычисления и избегать лишних итераций.
Какие соглашения по синтаксису задаёт этот скилл?
Он требует объявлять переменные внутри функций через let, использовать обычные объявления function для именованных функций и отказаться от экспортов по умолчанию.
Исходный текст
SKILL.md · MIT
---
name: frontend-js-best-practices
description: JavaScript performance optimization guidelines. Use when writing, reviewing, or refactoring JavaScript/TypeScript code to ensure optimal performance patterns. Triggers on tasks involving loops, data structures, DOM manipulation, or general JS optimization.
---
# JavaScript Best Practices
Performance optimization and code style patterns for JavaScript and TypeScript code. Contains 17 rules focused on reducing unnecessary computation, optimizing data structures, and maintaining consistent conventions.
## When to Apply
Reference these guidelines when:
- Writing loops or array operations
- Working with data structures (Map, Set, arrays)
- Manipulating the DOM directly
- Caching values or function results
- Optimizing hot code paths
- Declaring variables or functions
## Rules Summary
### const-let-usage (MEDIUM) — @rules/const-let-usage.md
Use `const` at module level, `let` inside functions.
```typescript
// Module level: const with UPPER_SNAKE_CASE for primitives
const MAX_RETRIES = 3;
const userCache = new Map<string, User>();
// Inside functions: always let
function process(items: Item[]) {
let total = 0;
let result = [];
for (let item of items) {
total += item.price;
}
return { total, result };
}
```
### function-declarations (MEDIUM) — @rules/function-declarations.md
Prefer function declarations over arrow functions for named functions.
```typescript
// Good: function declaration
function calculateTotal(items: Item[]): number {
let total = 0;
for (let item of items) {
total += item.price;
}
return total;
}
// Good: arrow for inline callbacks
let active = users.filter((u) => u.isActive);
// Good: arrow when type requires it
const handler: ActionFunction = async ({ request }) => {
// ...
};
```
### no-default-exports (MEDIUM) — @rules/no-default-exports.md
Use named exports. Avoid default exports (except Remix route components).
```typescript
// Bad: default export
export default function formatCurrency(amount: number) { ... }
// Good: named export
export function formatCurrency(amount: number) { ... }
// Exception: Remix routes use default export named "Component"
export default function Component() { ... }
```
### no-as-type-casts (HIGH) — @rules/no-as-type-casts.md
Avoid `as Type` casts. Use type guards or Zod validation instead.
```typescript
// Bad: type assertion
let user = response.data as User;
// Good: Zod validation
let user = UserSchema.parse(response.data);
// Good: type guard
if (isUser(response.data)) {
let user = response.data;
}
```
### comments-meaningful-only (MEDIUM) — @rules/comments-meaningful-only.md
Only comment when adding info the code cannot express.
```typescript
// Bad: restates the code
// Set the user's name
let userName = user.name;
// Good: explains business rule
// Transactions under $250 don't require written acknowledgment per policy
if (transaction.amount < 250) {
return { requiresAcknowledgment: false };
}
```
### set-map-lookups (LOW-MEDIUM) — @rules/set-map-lookups.md
Use Set/Map for O(1) lookups instead of Array methods.
```typescript
// Bad: O(n) per check
const allowedIds = ["a", "b", "c"];
items.filter((item) => allowedIds.includes(item.id));
// Good: O(1) per check
const allowedIds = new Set(["a", "b", "c"]);
items.filter((item) => allowedIds.has(item.id));
```
### index-maps (LOW-MEDIUM) — @rules/index-maps.md
Build Map once for repeated lookups.
```typescript
// Bad: O(n) per lookup = O(n*m) total
orders.map((order) => ({
...order,
user: users.find((u) => u.id === order.userId),
}));
// Good: O(1) per lookup = O(n+m) total
const userById = new Map(users.map((u) => [u.id, u]));
orders.map((order) => ({
...order,
user: userById.get(order.userId),
}));
```
### tosorted-immutable (MEDIUM-HIGH) — @rules/tosorted-immutable.md
Use `toSorted()` instead of `sort()` to avoid mutation.
```typescript
// Bad: mutates original array
const sorted = users.sort((a, b) => a.name.localeCompare(b.name));
// Good: creates new sorted array
const sorted = users.toSorted((a, b) => a.name.localeCompare(b.name));
```
### combine-iterations (LOW-MEDIUM) — @rules/combine-iterations.md
Combine multiple filter/map into one loop.
```typescript
// Bad: 3 iterations
const admins = users.filter((u) => u.isAdmin);
const testers = users.filter((u) => u.isTester);
const inactive = users.filter((u) => !u.isActive);
// Good: 1 iteration
const admins: User[] = [],
testers: User[] = [],
inactive: User[] = [];
for (const user of users) {
if (user.isAdmin) admins.push(user);
if (user.isTester) testers.push(user);
if (!user.isActive) inactive.push(user);
}
```
### cache-property-access (LOW-MEDIUM) — @rules/cache-property-access.md
Cache object properties in loops.
```typescript
// Bad: repeated lookups
for (let i = 0; i < arr.length; i++) {
process(obj.config.settings.value);
}
// Good: cached lookup
const value = obj.config.settings.value;
const len = arr.length;
for (let i = 0; i < len; i++) {
process(value);
}
```
### cache-function-results (MEDIUM) — @rules/cache-function-results.md
Cache expensive function results in module-level Map.
```typescript
const slugifyCache = new Map<string, string>();
function cachedSlugify(text: string): string {
if (!slugifyCache.has(text)) {
slugifyCache.set(text, slugify(text));
}
return slugifyCache.get(text)!;
}
```
### cache-storage (LOW-MEDIUM) — @rules/cache-storage.md
Cache localStorage/sessionStorage reads in memory.
```typescript
const storageCache = new Map<string, string | null>();
function getLocalStorage(key: string) {
if (!storageCache.has(key)) {
storageCache.set(key, localStorage.getItem(key));
}
return storageCache.get(key);
}
```
### early-exit (LOW-MEDIUM) — @rules/early-exit.md
Return early when result is determined.
```typescript
// Bad: continues after finding error
function validate(users: User[]) {
let error = "";
for (const user of users) {
if (!user.email) error = "Email required";
}
return error ? { error } : { valid: true };
}
// Good: returns immediately
function validate(users: User[]) {
for (const user of users) {
if (!user.email) return { error: "Email required" };
}
return { valid: true };
}
```
### length-check-first (MEDIUM-HIGH) — @rules/length-check-first.md
Check array length before expensive comparison.
```typescript
// Bad: always sorts even when lengths differ
function hasChanges(a: string[], b: string[]) {
return a.sort().join() !== b.sort().join();
}
// Good: early return if lengths differ
function hasChanges(a: string[], b: string[]) {
if (a.length !== b.length) return true;
let aSorted = a.toSorted();
let bSorted = b.toSorted();
return aSorted.some((v, i) => v !== bSorted[i]);
}
```
### min-max-loop (LOW) — @rules/min-max-loop.md
Use loop for min/max instead of sort.
```typescript
// Bad: O(n log n)
const latest = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)[0];
// Good: O(n)
let latest = projects[0];
for (const p of projects) {
if (p.updatedAt > latest.updatedAt) latest = p;
}
```
### hoist-regexp (LOW-MEDIUM) — @rules/hoist-regexp.md
Hoist RegExp creation outside loops.
```typescript
// Bad: creates regex every iteration
items.forEach(item => {
if (/pattern/.test(item.text)) { ... }
})
// Good: create once
const PATTERN = /pattern/
items.forEach(item => {
if (PATTERN.test(item.text)) { ... }
})
```
### batch-dom-css (MEDIUM) — @rules/batch-dom-css.md
Batch DOM reads before writes to avoid layout thrashing.
```typescript
// Bad: interleaved reads/writes force reflows
element.style.width = "100px";
const width = element.offsetWidth; // forces reflow
element.style.height = "200px";
// Good: batch writes, then read
element.style.width = "100px";
element.style.height = "200px";
const { width, height } = element.getBoundingClientRect();
```
### result-type (MEDIUM) — @rules/result-type.md
Use an explicit `Result` type for success/failure.
```typescript
let result = success(data);
if (isFailure(result)) return handleError(result.error);
```
© sergiodxa · MIT · sergiodxa/agent-skills