plugins/languages/typescript/skills/nodejs/SKILL.md
TypeScript / JavaScript Node.js 后端开发规范,覆盖 Node 22-24 LTS 原生 strip-types、ESM 模块、原生 fetch、fs/promises、stream pipeline、worker_threads、Hono 4 / Fastify 5 框架、Drizzle ORM、env Zod 验证、undici 高性能 HTTP。Use when 开发 Node.js 服务端、CLI、API 服务、迁移 CommonJS→ESM、env 校验,或用户提到 "Node.js"、"fastify"、"hono"、"ESM"、"worker thread"、"process.env"、"backend"。
npx skillsauth add lazygophers/ccplugin typescript-nodejsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
本 skill 同时覆盖 JavaScript 项目;示例以 TS 为主,JS 项目去掉类型注解即可。
Node 22 LTS 起原生支持 strip-types (node --experimental-strip-types / 22.18+ 默认);Node 24 进一步打磨。新项目可不依赖 tsc 直接 run。
node file.ts 直接运行 (无类型检查)// package.json
{ "type": "module" }
// tsconfig.json (TS 项目)
{
"compilerOptions": {
"target": "ES2025",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"verbatimModuleSyntax": true
}
}
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
// ESM 中获取目录路径 (Node 21.2+)
const dir = import.meta.dirname;
const file = import.meta.filename;
import { readFile, writeFile, mkdir, readdir } from "node:fs/promises";
async function loadConfig(p: string): Promise<Config> {
const content = await readFile(p, "utf-8");
const data: unknown = JSON.parse(content);
return ConfigSchema.parse(data);
}
async function saveData(p: string, data: unknown): Promise<void> {
await mkdir(path.dirname(p), { recursive: true });
await writeFile(p, JSON.stringify(data, null, 2), "utf-8");
}
async function* walkDir(dir: string): AsyncGenerator<string> {
const entries = await readdir(dir, { withFileTypes: true });
for (const e of entries) {
const full = path.join(dir, e.name);
if (e.isDirectory()) yield* walkDir(full);
else yield full;
}
}
async function getUser(id: string): Promise<User> {
const r = await fetch(`https://api.example.com/users/${id}`, {
headers: { "Content-Type": "application/json" },
signal: AbortSignal.timeout(5000),
});
if (!r.ok) throw new Error(`HTTP ${r.status}: ${r.statusText}`);
const data: unknown = await r.json();
return UserSchema.parse(data);
}
// 高吞吐:undici 直连 + keepalive
import { request, Agent } from 'undici';
const agent = new Agent({ keepAliveTimeout: 60_000 });
import { createReadStream, createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
import { Transform } from "node:stream";
async function processLargeFile(inPath: string, outPath: string): Promise<void> {
const transform = new Transform({
transform(chunk: Buffer, _enc, cb) {
cb(null, chunk.toString("utf-8").toUpperCase());
},
});
await pipeline(createReadStream(inPath), transform, createWriteStream(outPath));
}
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
function runWorker<T, R>(workerPath: string, data: T): Promise<R> {
return new Promise((resolve, reject) => {
const w = new Worker(workerPath, { workerData: data });
w.on("message", resolve);
w.on("error", reject);
w.on("exit", (code) => {
if (code !== 0) reject(new Error(`Worker exited ${code}`));
});
});
}
if (!isMainThread && parentPort) {
parentPort.postMessage(heavyComputation(workerData));
}
import { z } from "zod";
const EnvSchema = z.object({
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
PORT: z.coerce.number().int().min(1).max(65535).default(3000),
DATABASE_URL: z.url(),
API_KEY: z.string().min(32),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
});
export const env = EnvSchema.parse(process.env);
import { Hono } from "hono";
import { zValidator } from "@hono/zod-validator";
const app = new Hono();
app.get("/api/users/:id", async (c) => {
const id = c.req.param("id");
const user = await db.user.findUnique({ where: { id } });
return user ? c.json(user) : c.json({ error: "Not found" }, 404);
});
app.post("/api/users", zValidator("json", CreateUserSchema), async (c) => {
const data = c.req.valid("json");
return c.json(await db.user.create({ data }), 201);
});
export default app;
替代:Elysia (Bun 原生) / Fastify 5 (生态成熟) / tRPC (无 REST 类型契约) / Express 5 (legacy)。
import { drizzle } from "drizzle-orm/postgres-js";
import { eq } from "drizzle-orm";
import { users } from "./schema";
const db = drizzle(connectionString);
const user = await db.select().from(users).where(eq(users.id, id));
Drizzle = 零运行时、SQL-like、TypeScript-first;Prisma = 全功能但运行时引擎重;Kysely = query builder。
所有 Node API 在 JS 项目同样可用。env 校验、Hono、Drizzle 都支持纯 JS,去掉类型即可:
import { z } from 'zod';
const EnvSchema = z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.url(),
});
export const env = EnvSchema.parse(process.env);
JSDoc + jsconfig.json (checkJs: true) 提供类型提示,详见 typescript-core JS 兜底章节。
| 现象 | 问题 | 严重 |
|------|------|------|
| require() | 用 ESM import | 高 |
| fs.readFileSync | 用 fs/promises | 中 |
| node-fetch 包 | Node 22+ 内置 fetch | 低 |
| CJS __dirname | 用 import.meta.dirname | 中 |
| 直接读 process.env | 必须 Zod 验证 | 高 |
| Express 4 新项目 | 考虑 Hono / Fastify | 中 |
| 无连接池 HTTP | undici Agent + keepalive | 中 |
"type": "module" + TS: module: "NodeNext"node: 前缀导入内置模块fs/promises (无 sync API)AbortSignal.timeoutpnpm install --frozen-lockfile)tools
UI/UX 与布局设计——做界面布局/结构/导航/组件/交互的设计决策。触发:做UI/UX/布局/排版/导航/组件/交互/栅格/响应式/图表选型/字体配对。按媒介路由 HTML/Web、原生 App(iOS/Android/桌面)、CLI、TUI。需后端动态系统不适用;配色/主题/色板走姊妹 skill design-color。
tools
主题与配色设计——做颜色搭配/调色板/主题/品牌色阶/暗模式的设计决策。触发:选配色/调色/主题/色板/品牌色/暗模式/对比度/色盲/UI风格。按媒介路由 HTML/Web(CSS变量)、原生App(平台token)、CLI(ANSI)、TUI(真彩/256/16降级)。保证可访问性(对比度/色盲安全)。需后端动态系统不适用;UI/UX 布局/组件/交互走姊妹 skill design-uiux。
tools
跨任意组件(plugin/skill/agent/command)的验证驱动优化循环纪律 skill。当用户要优化某个已有组件却无明确方向、或要防止改了反而更差(自评乐观偏差 / 多维同改归因失效 / 为凑分加废话膨胀)、或要把一套通用「评分→单变量改→改后验证严格更好才留否则回滚→触顶停」的纪律套到任意组件上时使用。管优化过程本身的纪律(validation gate / ratchet / 独立验证 / 触顶停),不评单组件深度(交 skill-dev),不查插件接线(交 plugin-dev)。仅手动 /optimize-any 触发。
data-ai
两层规则记忆 (基于 .skein/spec)。planning 时 recall 召回相关规则、task finish 后 sediment 沉淀学习 + prune 自动精简过期/重复/断链规则。core 常驻硬规 + recall 按需召回, 经判定门自动写盘 (不逐次问用户)。产出 .skein/spec 下 core/recall 规则文件 + index。另支持空仓 bootstrap 播种规则基线、记忆大面积失效 (大重构/换栈) 时 reconstruct 可逆归档后按项目类型分型重建、maintain 手动体检 (超预算/stale/断链/重复/废弃, --apply 自动修复)、auto-fix (Stop hook 写 .pending-fix 标记 → main 派 skein-specer bg 跑 maintain --apply 全自动修, 断链只报告)。