feat(skills): add coding-dev.md and per-language rules under code-languages/
Generic coding rules and preferences distilled from existing agent rule files, project AGENTS.md/.rules, and saved memories: - strictness maximal; suppressions must be reported to the user - never filter build/lint/test output through grep/tail/head - reuse > fork > self-implement; generic code becomes its own package - 1000-line file / 100-line function limits, split by concern - regression test every fixed bug; never test against real user data - events over polling; scope discipline; honest reporting Per-language: rust, js-typescript, bash, python, go, csharp-dotnet, c-cpp. Each extends coding-dev.md rather than repeating it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba95f171b2
commit
57968904bf
9 files changed
+1499
-1
No files matched your search
@@ -0,0 +1,202 @@
|
||||
---
|
||||
name: js-typescript
|
||||
description: >-
|
||||
Use this skill when writing, building, linting, testing, or releasing JavaScript or
|
||||
TypeScript — including Node, Bun, Deno, React, Next.js, and Astro projects. Covers the
|
||||
TypeScript-over-JSDoc-over-JS preference, strict compiler settings, banned constructs,
|
||||
React/hooks rules, resource lifecycle, and package/tooling choices.
|
||||
---
|
||||
|
||||
# TypeScript / JavaScript — Rules & Preferences
|
||||
|
||||
Extends [`../coding-dev.md`](../coding-dev.md). Everything there applies; this adds JS/TS specifics.
|
||||
|
||||
---
|
||||
|
||||
## 1. Language Choice — A Strict Preference Order
|
||||
|
||||
1. **TypeScript** — always the default for new code.
|
||||
2. **JavaScript with JSDoc type annotations** — acceptable when a project genuinely cannot
|
||||
adopt a build step, and `checkJs` is enabled so the annotations are actually enforced.
|
||||
3. **Plain untyped JavaScript** — last resort only.
|
||||
|
||||
Migrating an existing JS project to TS is worth proposing; do not do it unasked (scope rule).
|
||||
In a plain-JS codebase, **match it** — do not introduce a lone `.ts` file and a build step
|
||||
into a project that has neither.
|
||||
|
||||
---
|
||||
|
||||
## 2. Strict Compiler Settings
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"isolatedModules": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`tsc --noEmit` must pass with 0 errors**, and it belongs in the build gate alongside lint
|
||||
and tests — not as a thing someone runs occasionally in their editor.
|
||||
|
||||
> [!NOTE]
|
||||
> Some established codebases deliberately relax `noImplicitAny` or `noUncheckedIndexedAccess`
|
||||
> because their idioms depend on it. Respect the project's `tsconfig.json`; the table above is
|
||||
> the default for **new** projects.
|
||||
|
||||
---
|
||||
|
||||
## 3. Banned Constructs
|
||||
|
||||
| Never | Use instead |
|
||||
| :--- | :--- |
|
||||
| `any` | `unknown` + narrowing, or a real generic |
|
||||
| `as unknown as T` | a type guard, or fix the source type |
|
||||
| `@ts-ignore` | `@ts-expect-error` with a one-line reason, only when unavoidable |
|
||||
| `!` non-null assertion | narrow with `if` or an early return |
|
||||
| `enum` / `const enum` | `as const` object + literal union |
|
||||
| `namespace` | ES modules |
|
||||
| Boxed primitives (`Number`, `String`, `Object`, `{}`, `Function`) | the primitive types |
|
||||
| `../../..` relative climbs | the configured path alias (`@/*`) |
|
||||
| `JSON.parse(JSON.stringify(x))` | `structuredClone` or `lodash.cloneDeep` |
|
||||
| `await new Promise(r => setTimeout(r, ms))` | a `sleep` helper from your utils |
|
||||
| `console.log` in shipped code | a real logger with levels |
|
||||
| Empty `catch {}` | handle it, or let it propagate |
|
||||
|
||||
- **`satisfies T` over `as T`** when the value already conforms. `as` is for genuine narrowing
|
||||
only — it is an assertion that silences the checker, not a conversion.
|
||||
- **Zero `any` in shared model types.** Frontend types that mirror a backend schema must stay
|
||||
structurally in sync with it and fully typed.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use What Exists
|
||||
|
||||
**Search the codebase's own `utils/`, `common/`, and shared modules before writing anything.**
|
||||
Re-implementing an export that already exists is the single most common avoidable diff.
|
||||
|
||||
Never hand-roll: debounce/throttle (`lodash`), deep clone, clamp, date formatting
|
||||
(`date-fns` / `dayjs` / project standard), URL parsing, regex escaping, UUID generation,
|
||||
retry/backoff, or a semver comparator.
|
||||
|
||||
In a UI framework or client mod, **never recreate a component the host already provides** —
|
||||
a hand-rolled `<button>` loses theming, focus rings, and accessibility.
|
||||
|
||||
---
|
||||
|
||||
## 5. React
|
||||
|
||||
- **Conditional rendering returns `null`**, never `undefined` or `false`.
|
||||
- **List keys are stable identifiers.** Never `key={index}` on a mutable list.
|
||||
- **Don't mirror external state into `useState`.** Subscribe to the store with its own hook.
|
||||
- No `useMemo` / `useCallback` on cheap values that do not feed a dependency array. They are
|
||||
not free, and they obscure what is actually expensive.
|
||||
- **Hoist constant dependency arrays** to module scope (`const KEYS = [...] as const`). A fresh
|
||||
array literal in the render body re-subscribes on every render.
|
||||
- Prefer `<>…</>` fragments; `React.Fragment` only when the fragment needs a `key`.
|
||||
- Type props with a named `interface`, not `React.FC`.
|
||||
- Wrap anything injected into a host application's tree in an error boundary — an
|
||||
uncaught throw in injected code takes down the host's render, not just yours.
|
||||
- Components used as dynamic render targets must live at **module scope**. Defining a
|
||||
component inside another component remounts its whole subtree every render.
|
||||
|
||||
---
|
||||
|
||||
## 6. Resource Lifecycle — Everything Started Must Be Stopped
|
||||
|
||||
Every `setInterval`, long `setTimeout`, `addEventListener`, subscription, `ResizeObserver`,
|
||||
`IntersectionObserver`, `AbortController`, and `WebSocket` created in a setup path must be
|
||||
torn down in the matching teardown path — `stop()`, `useEffect` cleanup, or `finally`.
|
||||
|
||||
> [!CAUTION]
|
||||
> **`addEventListener` needs a named function reference.** An inline arrow passed to
|
||||
> `addEventListener` cannot be matched by `removeEventListener`, so the listener leaks
|
||||
> silently and accumulates across every mount.
|
||||
|
||||
```ts
|
||||
let intervalId: ReturnType<typeof setInterval> | undefined;
|
||||
|
||||
function start() { intervalId = setInterval(tick, 30_000); }
|
||||
function stop() { if (intervalId !== undefined) { clearInterval(intervalId); intervalId = undefined; } }
|
||||
```
|
||||
|
||||
Attach an `AbortController` to every long-lived `fetch` and abort it in cleanup.
|
||||
|
||||
---
|
||||
|
||||
## 7. Structure & Naming
|
||||
|
||||
- **Components**: PascalCase `.tsx`.
|
||||
- **Modules / utilities**: kebab-case `.ts`.
|
||||
- **Imports**: prefer the configured alias (`@/…`) over relative climbs.
|
||||
- Indentation and quote/semicolon style: **match the file you are editing**. Do not introduce
|
||||
a new formatter or lint config into a project that already has one.
|
||||
- **One file until it earns a split** — around 250 lines for a self-contained plugin/feature,
|
||||
or when a second file is genuinely reusable (`utils.ts`, `types.ts`, `components/`).
|
||||
Hard limits from the general rules still apply.
|
||||
|
||||
---
|
||||
|
||||
## 8. Async & Data
|
||||
|
||||
- `async`/`await` over raw `.then()` chains. No mixing both in one flow.
|
||||
- **Never leave a promise unawaited** unless you explicitly mean to, and mark it (`void p`)
|
||||
when you do.
|
||||
- `Promise.all` for independent work; sequential `await` only when there is a real dependency.
|
||||
- **Subscribe to events; never poll.** (Global rule — it applies with full force here.)
|
||||
- **Never store secrets in `localStorage` / `sessionStorage`.** Use the platform's real
|
||||
storage API, and namespace your keys.
|
||||
|
||||
---
|
||||
|
||||
## 9. Node / Runtime Boundaries
|
||||
|
||||
- In any sandboxed or privileged-split architecture (Electron, browser extensions, plugin
|
||||
hosts), the untrusted side must **never** import `fs`, `path`, `child_process`, `net`,
|
||||
`http`, or `electron` directly. Route through the designated IPC/native boundary.
|
||||
- Every IPC handler argument is **untrusted** — validate type, length, and content.
|
||||
- Filesystem paths from the other side: `normalize()` then assert `startsWith(<allowed root>)`
|
||||
before any read or write.
|
||||
- Subprocesses: `spawn` with an argv **array**. Never `exec` a constructed string, never
|
||||
`shell: true` with untrusted input.
|
||||
- Network: allowlist by origin, cap response size, cap redirect depth.
|
||||
- Errors crossing the boundary must be scrubbed — a raw `Error` leaks absolute paths.
|
||||
|
||||
---
|
||||
|
||||
## 10. Tooling & Tests
|
||||
|
||||
- Respect the project's runtime and package manager (`npm` / `pnpm` / `yarn` / `bun`) and its
|
||||
lockfile. Do not switch package managers or add a second lockfile.
|
||||
- Test runner: whatever the project uses (`vitest`, `bun test`, `jest`). Tests live beside the
|
||||
code as `*.test.ts(x)` unless the project says otherwise.
|
||||
- **Add unit tests for new logic and route tests for new handlers.** Prioritize coverage on
|
||||
auth, persistence, and anything that talks to a third party.
|
||||
- Lint with the project's config (`eslint.config.mjs` and friends). **Do not add a new lint or
|
||||
format config** to a repo that already has one.
|
||||
- **Know the project's expected test baseline.** Some inherited suites are not all-green on a
|
||||
clean checkout; judge regressions against the documented baseline rather than a raw run —
|
||||
and if a baseline file exists, use it.
|
||||
|
||||
---
|
||||
|
||||
## 11. Framework Versions Move Fast
|
||||
|
||||
> [!WARNING]
|
||||
> Next.js, Astro, React, and the major bundlers ship breaking changes regularly, and their
|
||||
> current APIs frequently differ from what is in a model's training data. **Read the version's
|
||||
> own docs in `node_modules/<pkg>/dist/docs/` or the project's pinned documentation before
|
||||
> writing code against them.** Heed deprecation notices rather than pattern-matching from memory.
|
||||
|
||||
---
|
||||
|
||||
## 12. Related
|
||||
- [`../coding-dev.md`](../coding-dev.md) — the general rules this extends.
|
||||
- [`bash.md`](./bash.md) — for the build/release scripts around these projects.
|
||||
Reference in new issue
Block a user