رفتن به محتوا

تله‌ها و نگهبانشان

هر چیزی که یک بار بی‌صدا خراب شد و دوباره خواهد شد. ستونِ نگهبان می‌گوید امروز چه چیزی می‌گیردش: تله‌ای که نگهبان دارد اینجا برای فهمیدنِ پیامِ همان نگهبان است، و تله‌ای که «—» دارد فقط همین سند و حواسِ تو را دارد — همان‌ها در CLAUDE.md هم هستند تا پیش از چشم باشند.

جایِ این سند: فاز ۹.۳ گفته بود جدول در conventions.md بنشیند. ننشست، چون آن سند «زبان، کامیت، کامنت» است و به انگلیسی نوشته شده تا هر تازه‌وارد بخواندش؛ فهرستِ تله‌ها نه قرارداد است نه انگلیسی. یک سند برای یک چیز.

تله نگهبان
dist ِ کهنه: آزمون و typecheck از src می‌خوانند، ولی pnpm e2e، pnpm size و node apps/cli/dist/darzsaz.mjs از بیلد — پس از تغییرِ کد اول pnpm -r build، وگرنه کدِ دیروز را سبز می‌بینی —
tsconfig.json ِ هر بسته فقط فهرستِ برنامه‌هاست (files: [])؛ tsc --noEmit رویش هیچ نمی‌سنجد. tsc -p tsconfig.app.json یا pnpm typecheck pnpm typecheck
await ِ سطح بالا در هر ماژولِ بستهٔ وب بهینه‌سازیِ تکه‌های rolldown را برای کل ساخت خاموش می‌کند (۱۴ کیلوبایت و ۲۸ پرونده در فاز ۱)؛ درون تابع async بگذار لینت: topLevelAwait
pnpm run/exec با node_modules ِ ناهمگام نمی‌راند (verifyDepsBeforeRun: error): پس از دست زدن به package.json اول pnpm install خودِ pnpm
هر pnpm install می‌تواند کتابخانهٔ برنامه را دو نمونه کند: ‎@lingui/core‎ در web و ui دو i18n شد، بی هیچ آزمونِ افتاده؛ همتا در ریشه سنجاق است scripts/single-instance.mjs
اسکریپتی هم‌نامِ فرمانِ داخلیِ pnpm با pnpm <نام> اجرا نمی‌شود — pnpm licenses خودِ pnpm را صدا می‌زند؛ نگهبان را با node scripts/… بران —
pnpm --filter darzsaz ریشه را هم می‌گیرد — ریشه و CLI هم‌نام‌اند و ریشه pnpm -r می‌زند. CLI تنها: --filter ./apps/cli —
بستهٔ ورک‌اسپیس در dependencies ِ CLI نه — جاسازی می‌شود و روی npm نیست؛ devDependencies scripts/release-check.mjs
رابط و چاپ دو فونت‌اند: یکان‌بخ فقط روی سایت؛ هر چه دانلود یا پخش می‌شود (گزارش چاپی، CLI روی npm) وزیرمتن — مجوزِ فونتِ تجاری پخشِ پرونده را نمی‌دهد scripts/licenses.mjs، site-fonts
نویسهٔ تازه در رابط ممکن است در فونتِ زیرمجموعه‌شده نباشد و بی‌صدا با فونت سامانه بیاید scripts/font-coverage.mjs
pkill -f <الگو> پوستهٔ خودش را هم می‌کشد اگر الگو در متنِ همان فرمان باشد —
انبارهٔ ESLint قاعدهٔ تیپ‌دار را کهنه نگه می‌دارد: pnpm lint با --cache-strategy content پرونده‌ای را که خودش عوض نشده دوباره نمی‌سنجد، حتی اگر تیپِ واردشده‌اش عوض شده باشد. تیپِ onChange ِ NumberField ِ کیت که number شد، if (v === null) ِ دو پرونده فقط بی انبار سرخ شد. پس از دست زدن به تیپِ کیت یا هسته، یک بار npx eslint . --suppressions-location scripts/lint-baseline.json (≈ ۱۵۰ ثانیه) —
تله نگهبان
متغیر CSS ِ تعریف‌نشده خطا نمی‌دهد — کلِ اعلان را بی‌صدا باطل می‌کند. توکن‌های رقم‌دار --line-2/--panel-2/--bg-2 هستند (با خط تیره) scripts/css-vars.mjs
CSS در لایه است (reset, tokens, kit, app): جای پرونده لایه را تعیین می‌کند و برنامه همیشه بر کیت می‌چربد. CSS ِ بی‌لایه بر همه برنده است؛ قاعدهٔ عنصر (button {}) را در @layer reset بگذار apps/web/vite/css.ts + بیلد
قاعدهٔ CSS ِ کنترلِ خام پس از مهاجرت به کیت می‌ماند و به درونِ کیت می‌رسد (.field input) — با مهاجرت، قاعده را هم بردار kit/no-raw-button، kit-usage
min-height: 0 در ستونِ فلکسِ بی‌پیمایش: فرزندِ فلکس min-height: auto دارد؛ بی آن پنلِ overflow: auto کلِ صفحه را دراز می‌کند و پیمایش به پنجره می‌رود e2e/layout.spec.ts
رنگِ تیره در rgba روی بوم و صحنه: زمینهٔ نیمه‌شفافِ سخت‌کد در پوستهٔ روشن کنتراست را ۱٫۰۵ می‌کند (نوارِ سیاهِ نسل پنجم). --surface-overlay e2e/contrast.ts
axe و filter: پس‌زمینهٔ پشت filter: brightness() را نمی‌بیند و کنتراست را غلط می‌گیرد؛ رنگ واقعی بده (--accent-hover) e2e/contrast.ts
axe روی WebGL و SVG «incomplete» می‌دهد و سبز می‌ماند — سنجش با سیاه و سفیدِ مطلق در هر سه پوسته e2e/contrast.ts
jsx-a11y نقش application را نااندرکنشی می‌داند؛ کنترل واقعی بده (دکمه)، نه میان‌بر پنهان روی div لینت: jsx-a11y
ارقام هم‌عرض برای ستون است، نه متن روان — tnum رقم «۱» را ۱۲۸٪ پهن‌تر می‌کند و «۳۰۰» شبیه «۳ ۰ ۰» می‌شود. فقط روی جدول و عددِ زنده —
میان‌بر را با e.key نسنج — با چیدمانِ فارسی K «ن» است؛ shortcutKey(e) از apps/web/src/lib/keys.ts. میان‌برِ فهرستِ راهنما بی فرمان کار نمی‌کند —
<text textAnchor="start"> در SVG جهت‌آگاه است — در راست‌به‌چپ «شروع» لبهٔ راست است؛ برچسبِ کنارِ نشان را middle کن —
گزینهٔ حالت‌دار در منو «✓ » ِ چسبیده به برچسب نیست — section و checked در فرمان، که menuitemradio/menuitemcheckbox با aria-checked می‌شوند e2e/a11y.spec.ts
کامپایلر React (react-hooks ۷): مقدارِ هوک را مستقیم تغییر نده؛ در r3f از attach/props استفاده کن، نه scene.x = … در افکت لینت: react-hooks
immer: تابع تغییر نباید مقدار برگرداند ((p) => (p.x = v) می‌اندازد). بدنهٔ بلوکی خودِ immer، هنگام اجرا
immer و structuredClone: تابع هسته‌ای که structuredClone می‌کند (گونه، پیشنهاد ورق) را روی پیش‌نویس صدا نزن؛ از وضعیت ساده حساب کن و با Object.assign(draft, next) بنشان —
پنجرهٔ دوم فقط پیرو است: وضعیت را از پنجرهٔ اصلی می‌گیرد و هرگز نمی‌نویسد — دو نویسنده روی یک پروژه یعنی رونویسیِ بی‌صدای کارِ یکی —
تله نگهبان
پنل پیش‌نمای پنهان نقاشی نمی‌کند و requestAnimationFrame در تبِ پنهان اجرا نمی‌شود؛ عکسِ آنجا سیاه است. رندر را با darzsaz render یا pnpm e2e بسنج —
عنصرِ تازهٔ three در JSX (<torusKnotGeometry>، مؤلفهٔ تازهٔ drei) کلاسش را در scene/three-catalogue.ts می‌خواهد — وگرنه فقط مرورگر با «not part of the THREE namespace» test/three-catalogue.test.ts
EffectComposer/پس‌پردازش drei انباشت رندر را می‌شکند؛ SoftShadows با three ۰٫۱۸۵ کار نمی‌کند —
گره‌ها یک بار در هر فریم به‌روز می‌شوند، و فریم را حلقهٔ requestAnimationFrame ِ رندرکننده می‌شمارد: چند رندر در یک تیک (نمونه‌های خروجی، بنچ) همان تصویرِ اول را می‌دهند. پیش از هر رندر advanceFrame e2e/render-accumulate.spec.ts، test/gpu.test.ts
هندسهٔ آزادشده دوباره داده نشود: رندرکنندهٔ WebGPU dispose ِ هندسه را فقط در بارگذاریِ اول گوش می‌دهد و بارِ دوم هرگز آزاد نمی‌شود e2e/scene-idle.spec.ts (حافظه)، test/boxgeo.test.ts
هدف‌های MRT ِ یک گذر جز اولی سیاهِ مات پاک می‌شوند و حتی اولی گاهی؛ آلفای جای خالی ۱ شد و زمینه پیدا نبود. setClearColor ِ هر هدف، صریح e2e/theme.spec.ts (T16)
حلِ TRAA جنسِ مات است و آلفا را ۱ می‌کند: هر چه باید پشتِ صحنه بنشیند پیش از TRAA بنشیند e2e/theme.spec.ts (T16)
@types/three صادره‌ای را اعلام می‌کند که بیلد ندارد (SRGBToLinear در 0.186): typecheck سبز، مرورگر «does not provide an export named» بیلد، e2e
three در برنامه three/webgpu است (افزونهٔ vite/three.ts) و فقط در src/lib/photo/ ساختِ کامل: وارد کردنِ نام‌دارِ چیزی WebGL-تنها (WebGLRenderer، ShaderLib، بشکهٔ drei) بیرون از آن پوشه بیلد را می‌اندازد — drei را تکه‌به‌تکه وارد کن، و ردیاب را فقط در lib/photo بیلد (MISSING_EXPORT)
ردیابِ پرتو روی macOS هر clearcoat را سیاه سایه می‌زند (Metal زیرِ ANGLE؛ three-gpu-pathtracer#711): جنسِ پوشش‌دار پیش از ردیاب ترجمه می‌شود (lib/photo/materials.ts) test/photo.test.ts، e2e/traced.spec.ts
ردیابی که صحنه یا دوربین و اندازهٔ دوم بگیرد غلط سایه می‌زند (three-gpu-pathtracer 0.0.24) — برای هر ردیابی ردیابِ تازه (engineOf)؛ و dispose ِ خودش دادهٔ صحنه را روی کارت جا می‌گذارد: release e2e/traced.spec.ts (کارتِ رنگ، پایدار)
ردیاب مشِ چندجنسی را غلط رنگ می‌کند (three-gpu-pathtracer 0.0.24): صحنه را یک گروه برای هر مش ادغام می‌کند و جنس‌های یک مش را پشتِ هم می‌شمارد، پس جنسِ هر مشِ بعدی جابه‌جا می‌شود؛ و رنگِ رأسِ سه‌تایی در کنارِ چهارتایی‌ها به مقصد نمی‌رسد — هر گروه مشی جدا (standIns)، رنگ RGBA test/photo-materials.test.ts
یک NaN در تصویرِ ردیاب کلِ کاشیِ حذف‌نوفه را سیاه می‌کند، و سیاهی NaN است نه صفر: پیش از شبکه finite، و lost NaN را هم می‌شمارد test/photo.test.ts
نمونه‌های پیش‌فرضِ ردیاب یک دنباله در هر فریم برای همهٔ پیکسل‌هاست (با جابه‌جاییِ نوفهٔ آبیِ ۶۴ پیکسلی): خطایشان روی یک دیوار هم‌جهت است، حذف‌نوفه نمی‌بردش و میانگینِ پیکسل‌ها کمش نمی‌کند — RANDOM_TYPE ِ ۰ (PCG) در engineOf؛ Sobol روی macOS کامپایل نمی‌شود e2e/traced.spec.ts (کارتِ رنگ)
oidn-web در Node وارد نمی‌شود (واردکردنِ بی‌پسوندِ ESM): آزمون holes.ts را وارد می‌کند نه denoise.ts vitest
تأخیرِ خواندنِ پیکسل زمانِ کارتِ گرافیک نیست: در WebGPU خواندن تا سرکشیِ بعدیِ مرورگر می‌ماند؛ برای داوریِ سرعت فاصلهٔ فریم‌های پیاپی را بسنج e2e/perf-gpu.spec.ts (PERF_GPU=1)
بنچمارک perf زیر بار دستگاه ۳۵–۴۰ میلی‌ثانیه می‌شود (آستانه ۳۴)؛ تنها اجرا کن: npx playwright test --project perf --no-deps —
تله نگهبان
jsdom چیدمان و setPointerCapture ندارد؛ پرکننده‌ها در apps/web/test/setup.ts. چیزی که به چیدمان واقعی وابسته است فقط در Playwright دیده می‌شود —
عکسِ مرجعِ آزمون تصویری فقط از کارِ دستیِ snapshots در CI می‌آید — رندرِ SwiftShader ِ دستگاه توسعه با تصویر رسمی یکی نیست؛ روی دستگاه آزمون کنار می‌رود، در CI نبودنش شکست است —
mergeConfig آرایه‌ها را به هم می‌چسباند: در vitest.shared.ts آرایه‌ای که بسته‌ها خودشان می‌دهند (include، exclude) نگذار —
Vitest ۴ و restoreMocks: فقط vi.spyOn را برمی‌گرداند؛ شمار و رفتارِ vi.fn() را mockReset ِ vitest.shared.ts پاک می‌کند —
هشدارِ act در آزمون می‌اندازد: تغییرِ مستقیمِ فروشگاه وقتی جزئی گوشش است، درونِ act(() => …) apps/web/test/setup.ts
CSS Module در Vitest صادرهٔ نام‌دار ندارد مگر css.include — import * as s + s.btn بی‌صدا undefined می‌شود؛ دسترسیِ پویا (s[x]) کلاسِ مرده را از knip پنهان می‌کند —
چک‌باکس و فهرستِ React Aria ورودیِ پنهان دارند؛ در Playwright روی برچسب کلیک کن، نه .check()/.selectOption() —
click() ِ Playwright خودش پیمایش می‌کند — گزینه‌ای که زیر لبهٔ منوی بُریده پنهان است با کلیک سبز می‌شود؛ «دیده می‌شود» را با toBeInViewport() بسنج —
axe داخل iframe ِ sandbox می‌شکند («Target page has been closed»)؛ پیش‌نمای چاپ را از اسکن کنار بگذار —
Playwright روی دستگاه توسعه کرومِ نصب‌شده را می‌خواهد (channel: 'chrome')؛ در CI تصویر رسمی scripts/test/chrome.test.mjs
برگهٔ برچسب page label-sheet است، نه page: آزمونی که برگه‌ها را با <section class="page"> می‌شمارد برچسب‌ها را نمی‌بیند —

جزئیاتِ هر کدام در i18n.md و conventions.md.

تله نگهبان
Lingui بی‌زبانِ فعال هیچ‌چیز رندر نمی‌کند — نه خطا، نه هشدار، فقط DOM خالی؛ آزمون‌ها در test/setup.ts زبان را فعال می‌کنند —
استخراج‌گر Lingui فقط i18n._ و شیءِ /*i18n*/ { id, message } را می‌شناسد، نه کمکی‌های خودت pnpm i18n:check
در جزء، i18n از useLingui() و عدد از useFmt() — ثابتِ سطح ماژول با زبانِ لحظهٔ بارگذاری قفل می‌شود و زیرِ memo متنِ زبانِ قبل می‌ماند لینت: web-i18n
برچسب ذخیره نکن — متنی که در وضعیت می‌ماند و بعد نشان داده می‌شود پیام است، نه رشتهٔ رندرشده (historyStep، errorMsg، partLabel)؛ دادهٔ کاربر با sayData استثناست لینت: sample-say
پیامِ /*i18n*/ شیء است، نه متن: message ِ آن را مستقیم نخوان — فارسیِ منبع بی‌صدا در برگهٔ انگلیسی می‌نشیند لینت: strings
دادهٔ کاربر یا نامِ محصول در JSX بی <bdi> در جهتِ دیگر می‌پیچد؛ در SVG unicodeBidi="plaintext"، و values ِ i18n._ با render(i18n, msg(D, …)) e2e/bidi.spec.ts، untypedValues
exactOptionalPropertyTypes: فیلد اختیاری را با ...(x === undefined ? {} : { x }) بده، نه x: undefined pnpm typecheck
تله نگهبان
اپ را اجرا کن و نگاه کن. چهار ایرادِ این نسل را هیچ آزمونی نگرفت و فقط نگاه به صفحه پیدا کرد (pnpm --filter @darzsaz/web dev) —
Prettier پیش از ویرایش خودکار: متن پرونده را همان لحظه بخوان؛ قالب‌بندی چندخطی جایگزینی‌های حدسی را بی‌صدا رد می‌کند —
هر ادعای عددی در سند با کد سنجیده می‌شود — مسیر، شمارهٔ خط و سقفِ اندازه scripts/doc-claims.mjs