Skip to content

Kỷ luật phụ thuộc ​

Ghi từ đợt rà thư viện 2026-08-25 (SRC-591). Bốn điều dưới đây đều là lỗi đã xảy ra thật trong chính đợt rà ấy, không phải lo xa.

1. Workspace nội bộ luôn là "*" ​

npm update --save đổi "@nemo12/design-system": "*" thành "^0.1.0" ở 10 package.json. Gói đó là private, không tồn tại trên registry — một dải phiên bản khiến npm đi tìm bên ngoài thay vì dùng bản trong repo.

Điều làm nó nguy hiểm: máy đã có node_modules thì không thấy gì cả. Nó chỉ nổ ở lần cài sạch, tức là ở CI, hoặc ở máy người mới. Cùng họ với bài học "check:docs local nói dối" trong CLAUDE.md.

Cổng: scripts/check-deps.mjs.

2. Một bản PATCH cũng đủ làm vỡ ​

@hono/zod-openapi 1.5.2 → 1.5.3 đổi kiểu Handler và làm đỏ 9 chỗ typecheck ở workers/api. Số hiệu phiên bản không hứa gì cả.

Vì thế gói này ghim tuyệt đối ("1.5.2", không ^ không ~). Muốn nâng thì nâng riêng nó, chạy npm run typecheck ngay, và sửa code trong cùng lần — không nâng kèm một lệnh update hàng loạt.

Cách truy khi một lệnh update làm đỏ: git stash phần package*.json → npm ci → typecheck. Sạch nghĩa là lỗi đến từ bản nâng, không phải từ code có sẵn.

Ghim vào package.json chưa đủ nếu package-lock.json còn giữ nhánh cũ trong workers/api/node_modules/... — phải gỡ khoá đó khỏi lock rồi npm install lại.

3. Cấm "latest" cho gói ngoài ​

Build phải tái lập được: cùng một commit, cài lại lúc nào cũng ra cùng cây phụ thuộc. "latest" phá đúng điều đó, và hỏng theo kiểu "hôm qua chạy được". Đã dọn @vitejs/plugin-react ở apps/web.

4. Lỗ hổng không phải lúc nào cũng vá được — và phải nói ra ​

npm audit báo 3 lỗ (2 moderate, 1 high) đến từ VitePress 1.6.4 kéo theo vite cũ và esbuild cũ. Bản 1.6.4 đã là mới nhất; đường vá duy nhất là 2.0.0-alpha.

Quyết định: không đưa alpha vào hai site tài liệu. Lý do đứng vững vì lỗ này thuộc dev server của esbuild — Pearl/Compass/Docs dựng tĩnh rồi đẩy lên Cloudflare, không chạy dev server ở đâu công khai. Ghi ra đây để lần audit sau không ai phải điều tra lại từ đầu; xem lại khi VitePress 2 ổn định.

4b. Nâng bốn major — làm được nhờ tra tài liệu TRƯỚC (SRC-592) ​

Đợt 2026-08-26 nâng ESLint 10 · TypeScript 7 · Vitest 4 · KaTeX 0.18, lần này có Context7 nên tra breaking change trước khi chạm vào package.json. Nhờ vậy biết ngay phần lớn không ảnh hưởng:

BảnCái nó bỏNemo12 có dính?
TypeScript 7moduleResolution: node/classic/node10, target: es5, module: amd/umd/system/nonekhông — 35 tsconfig đều bundler + ES2022
TypeScript 7baseUrl gỡ hẳncó — shadcn CLI vừa thêm hôm trước vào 6 tsconfig
Vitest 4option là tham số thứ BA của test/describe; workspace; coverage.all/extensionskhông
KaTeX 0.17API nội bộ __defineFunctionkhông
ESLint 10(thêm rule) no-useless-assignmentcó — 3 chỗ trong scripts/

Hai thứ phải sửa và cách sửa:

  • baseUrl: bỏ hẳn. TypeScript 7 giải paths tương đối với chính tsconfig.json, nên "@/*": ["./src/*"] vẫn đúng mà không cần baseUrl.
  • no-useless-assignment: cả 3 chỗ cùng một mẫu — let x = []; try { x = f(); } catch { continue; }. Giá trị khởi tạo không bao giờ được đọc. Khai let x; là đúng ý hơn: chưa có giá trị cho tới khi thử xong. Rule này bắt đúng, không phải càu nhàu phong cách.

Cách làm đáng giữ cho lần sau: tra tài liệu bản đích trước, đối chiếu với codebase bằng grep, rồi mới nâng. Nâng trước rồi đọc lỗi là cách chậm hơn và dễ hoảng.

5. Best practices đã rà trong đợt này ​

MặtKết quả
React 19 (forwardRef, React.FC, defaultProps, useFormState, ref callback trả ngầm)sạch — 0 chỗ
Context.Provider → <Context>sửa 1 chỗ (apps/learn/src/lang.tsx)
Tailwind v4 (config JS, @tailwind, class đã bỏ)sạch — đã CSS-first từ đầu
<img> thiếu altsạch — 0 chỗ
dangerouslySetInnerHTML5 chỗ: 4 là KaTeX (bắt buộc, đầu vào do hệ sinh), 1 là lỗ XSS thật đã vá — xem dưới

Lỗ XSS đã vá. apps/marlins/src/ChildReport.tsx đổi **…** thành <strong> rồi nhét qua dangerouslySetInnerHTML. Câu ấy do server sinh nhưng có nhúng dữ liệu thật: tên Unit, và ở các nhánh khác là tên trường mục tiêu — thứ phụ huynh tự gõ. Nay tách chuỗi rồi để React dựng phần tử (BoldText.tsx); React tự thoát ký tự nên không còn đường nào để chữ biến thành mã.

Test không chạy cũng là test không tồn tại. apps/marlins có 3 file .test.ts nhưng package.json không có script test — chúng chưa từng chạy lần nào. Bật lên: 11 test có thật. Khi thêm test vào một workspace, kiểm luôn rằng npm test ở gốc có gọi tới nó.