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
--- name: trellisx-workspace description: 维护 `.trellis/task.md` 任务看板 —— trellis 缺的跨任务总览。**一个表格, 一行一个任务**, 列为 id/名称/描述/状态/阶段/进度/worktree (状态/阶段中文显示)。在 task create/start/阶段切换/archive 后**及时更新**对应行; 并**自动清理超 7 天的已完成行**防膨胀。保持看板与 task.json 实时一致。 when_to_use: 维护 / 创建 / 更新 `.trellis/task.md` 任务看板时; task 生命周期任一节点 (create/start/阶段推进/archive) 之后同步看板时; 用户问"当前有哪些任务 / 任务进度 / 任务看板"时。被 trellisx-flow 与 trellisx-apply 注入的流程引用。 user-invocable: true argument-hint: [show|update|sync|cleanup ...] [task id] arguments:
testing
强制以 Trellis task 闭环处理用户指定的请求 (自判新建/并入 → plan→exec→check→finish 全程不跳步)。**仅用户显式主动调用** (/trellisx-flow 或明确要求"强制走 task 处理这个"); **禁止自动 / 被动 / 推断式调用** —— 不要因为某个请求"看起来该建 task"就自动触发本 skill, 那是 apply 注入的 no_task 倾向的职责。
testing
把 强推task + subtask拆分 + worktree隔离 + 闭环收尾 四维度增量注入当前项目 .trellis/ (workflow.md 的 no_task/planning/in_progress 块 + spec 背书文档 + trellis 生命周期 hook worktree 自动化)。强推 task 与闭环为纯 prompt 软约束 (非平台 hook 硬拦截)。**纯增量追加, 绝不替换 trellis 原生文本** (no_task 分类+征同意/check/finish/前缀全保留)。幂等 (marker 包裹)。
development
Claude Code 会话历史整理 — 扫 ~/.claude/projects/**/*.jsonl 全部 session transcripts, 提取学习增量 (用户校正/决策/踩坑/L0 规则) → 全局记忆库 ~/.cortex/.wiki/memory/. 默认 --apply 落盘 (--dry-run opt-in 仅出 JSON plan 预览). 与 cortex-extract (L4-inbox 内部) 互补.