ESLint
eslint-plugin-textjs: JSX سازگار با کنسول، گلیفهای همعرض سلول، زمانسنجهای هماهنگ با فریم و بررسیهای App Router.
افزونهٔ eslint-plugin-textjs اشتباهات Text.js را در ویرایشگر شناسایی میکند: تگهای DOM در کامپوننتهای کنسول، جعبههای داخل متن، نویسههایی که Menlo ندارد، فراخوانیهای نابجای console.log و خروجیهای App Router که بیصدا نادیده گرفته میشوند.
راهاندازی
bun add -d eslint eslint-plugin-textjs typescript-eslintimport { defineConfig } from "eslint/config";import tseslint from "typescript-eslint";import textjs from "eslint-plugin-textjs";export default defineConfig(tseslint.configs.recommended, textjs.configs.recommended);این افزونه از ESLint 9 و 10 با پیکربندی تخت پشتیبانی میکند. قواعد نحوی هستند و به اطلاعات نوع یا parserOptions.project نیاز ندارند. هر تجزیهگری که TypeScript و JSX را بفهمد کار میکند.
پیکربندیها
| پیکربندی | مواردی که فعال میکند |
|---|---|
textjs.configs.recommended | همهٔ قواعد درستی بهصورت خطا؛ قواعد کنسول، زمانسنج و آهنگ فریم بهصورت هشدار |
textjs.configs.strict | همهچیز بهصورت خطا، بهعلاوهٔ prefer-use-keymap بهصورت هشدار |
بهجای آن، قواعد را جداگانه انتخاب کنید:
import textjs from "eslint-plugin-textjs";export default [ { plugins: { textjs }, rules: { "textjs/no-box-in-text": "error", "textjs/no-console-in-components": ["warn", { allow: ["error"], logger: "log" }], }, },];فایلهای کنسول
بیشتر قواعد فقط روی فایلهایی اعمال میشوند که در کنسول رندر میکنند: فایلهایی که یک کامپوننت یا هوک (Box، Text، useInput، …) را از @textjs/app یا @textjs/react وارد میکنند. فایلهایی که react-dom یا @textjs/react/dom را وارد میکنند کد صفحه محسوب میشوند و تغییری نمیکنند، بنابراین یک وبسایت میتواند صفحات DOM و برنامههای کنسول را در یک پروژه ترکیب کند. وارد کردن یک ابزار کمکی مانند tokenize یا parseMarkdown فایل را به فایل کنسول تبدیل نمیکند.
import textjs from "eslint-plugin-textjs";export default [ textjs.configs.recommended, { settings: { textjs: { packages: ["@acme/console-ui"], // also re-exports Text.js components consoleFiles: "imports", // or "all" }, }, },];قواعد App Router بر اساس نام فایل (page.tsx، layout.tsx، route.ts، …) در هر دایرکتوری app/، از جمله src/app/، عمل میکنند. اگر دایرکتوری شما نام دیگری دارد، مقدار { appDir: "routes" } را به قواعد valid-route-module، valid-metadata و route-handler-exports بدهید.
قواعد
| قاعده | پیشنهادی | اصلاح |
|---|---|---|
no-dom-elements | خطا | پیشنهاد |
no-box-in-text | خطا | |
no-unsupported-glyphs | خطا | اصلاح خودکار، پیشنهاد |
no-console-in-components | هشدار | پیشنهاد |
prefer-use-interval | هشدار | پیشنهاد |
no-sub-frame-timers | هشدار | |
prefer-use-keymap | فقط در حالت سختگیرانه | |
valid-route-module | خطا | |
valid-metadata | خطا | |
route-handler-exports | خطا | پیشنهاد |
no-dom-elements
تگهای <div>، <span>، عناصر سفارشی و دیگر تگهای DOM در کنسول وجود ندارند. تطبیقدهنده فقط گرههای میزبان خودش را میشناسد و برای هر چیز دیگری خطای TX003 میدهد.
<div className="flex-col"> // ✗<Box className="flex-col"> // ✓<strong>Deployed</strong> // ✗<Text bold>Deployed</Text> // ✓پیشنهاد، تگ را با نزدیکترین کامپوننت جایگزین میکند (div → Box، span/p → Text، strong → Text bold، a → Link، input → Input، hr → Divider، …) و دستور وارد کردن آن را اضافه میکند.
no-box-in-text
کامپوننت <Text> دنبالهای از spanهای سبکدار است؛ نمیتواند جعبهای در خود داشته باشد. قرار دادن <Box> یا هر کامپوننتی با ریشهٔ جعبهای (<ScrollBox>، <Input>، <Code>، <ProgressBar>، <Divider>، …) درون آن، هنگام رندر خطای TX004 میدهد. این قاعده آن را در ویرایشگر پیدا میکند، از جمله درون &&، عبارتهای سهتایی، فرگمنتها و .map().
<Text>Status <Box /></Text> // ✗<Box className="flex-row gap-1"><Text>Status</Text><Box /></Box> // ✓<Text>Press <Kbd>⌘K</Kbd> <Spinner /></Text> // ✓ inline componentsno-unsupported-glyphs
Text.js هر نویسه را در یک شبکهٔ ثابت سلولی میچیند. نویسههایی که Menlo، قلم کنسول DevTools در macOS، ندارد با قلم جایگزینی رندر میشوند که از یک سلول عریضتر است، در نتیجه همهٔ سلولهای بعد از آنها جابهجا میشوند.
- بریل (U+2800–U+28FF، فریمهای رایج نشانگر چرخان ترمینال): از نویسههای بلوکی یا ربعی (
▘▝▗▖)، کامپوننت<Spinner variant="dots">یاmarker="quadrant"در نمودارها استفاده کنید. برای برنامههایی که فقط با دستورtext runدر ترمینال اجرا میشوند، مقدار{ allowBraille: true }را تنظیم کنید. - نمادها و پیکانهای متفرقه (U+2B00–U+2BFF، مانند مربع کوچک U+2B1D): پیشنهادها معادل Menlo را ارائه میکنند (
·،●،←). - نمایش ایموجی برای نمادهای تکسلولی (
▶یا↩با U+FE0F پس از آن): انتخابگر گونه آنها را به ایموجیهای دوسلولی تبدیل میکند، در حالی که شبکه یک سلول رزرو میکند. اصلاح خودکار آن را حذف میکند.
ایموجیهای عریض (🚀، ✅) مشکلی ندارند: Text.js آنها را دو سلول اندازهگیری میکند.
no-console-in-components
هر فریم یک فراخوانی console.log است. گزارشهای خود شما بین فریمها قرار میگیرند، با فریم بعدی از دید خارج میشوند و در نمای چسبیده به پایین پنهان میمانند. فراخوانی console.clear() خود برنامه را پاک میکند.
"textjs/no-console-in-components": ["warn", { allow: ["error"], logger: "log" }]پیامها را با useToast() یا یک <ScrollBox> برای گزارشها در رابط کاربری نمایش دهید، یا وضعیت را در پوشش توسعهٔ [▼] با میانبر (Ctrl+`) بررسی کنید. با logger، پیشنهاد فراخوانی console.warn(…) را به log.warn(…) بازنویسی میکند. فایلهای آزمون و رندرهای @textjs/app/testing نادیده گرفته میشوند.
prefer-use-interval
یک setInterval خام در کامپوننت به یک effect، یک cleanup و یک ref نیاز دارد تا از وضعیت کهنه جلوگیری کند. هوک useInterval هر سه کار را انجام میدهد و مقدار null آن را متوقف میکند.
useEffect(() => { if (paused) return; const id = setInterval(tick, 1000); return () => clearInterval(id);}, [paused]);useInterval(tick, paused ? null : 1000); // the suggestionبرای پویانمایی، هوک useAnimationFrame از آهنگ فریم رندرکننده پیروی میکند و هنگام توقف خروجی متوقف میشود. به هوکهای کمکی مراجعه کنید.
no-sub-frame-timers
Text.js در هر 66 ms حداکثر یک فریم تولید میکند (حدود 15 fps). بازهٔ 16 ms برای هر فریم خروجی چهار بار رندر میکند؛ فریمهای اضافی ادغام میشوند و فقط CPU مصرف میکنند. تأخیرهای setInterval/useInterval کمتر از minInterval (پیشفرض 66) و استفاده از useAnimationFrame با مقدار fps بالاتر از 15 را گزارش میکند.
prefer-use-keymap
مقایسهٔ دستی e.key کار میکند، اما میانبرها نامرئی هستند. هوک useKeymap نگاشتهای برچسبدار را در <KeyHelp>، در <KeyHints> (نگاشتهای دارای hint: true) و در پالت فرمان قرار میدهد، دنبالههایی مانند g g را مدیریت میکند و انتشار رویداد را برای کلیدهای منطبق متوقف میکند. کنترلگرهای useInput را که دو یا چند مقدار لفظی کلید (minKeys) را مقایسه میکنند گزارش میکند و کنترلگرهایی را که برای تایپ مقدار e.input را میخوانند نادیده میگیرد.
useKeymap({ "up, k": { label: "Increment", run: inc, hint: true }, "down, j": { label: "Decrement", run: dec, hint: true },});به میانبرهای صفحهکلید مراجعه کنید.
valid-route-module
- فایلهای
page،layout،loading،errorوnot-foundباید یک کامپوننت را بهصورت پیشفرض صادر کنند (TX015). - مقدار
export const dynamicباید"auto"،"force-static"یا"force-dynamic"باشد (TX031). Text.js از مقدار"error"در Next.js پشتیبانی نمیکند. این مقدار از صفحات، چیدمانها و کنترلگرهای مسیر خوانده میشود. در صفحات و چیدمانها آن را بهصورت یک مقدار لفظی رشتهای ساده بنویسید: دستورtext buildآن را بدون اجرای ماژول از کد منبع میخواند.
valid-metadata
Metadata ابتدا از چیدمانها و سپس از صفحه (یا not-found) ادغام میشود. این قاعده مواردی را گزارش میکند که مسیریاب بیصدا نادیده میگیرد:
metadataیاgenerateMetadataدر فایلهایloadingیاerror- هر دو خروجی در یک فایل
async generateMetadata: بهصورت همگام فراخوانی میشود، بنابراین Promise هرگز عنوان را تنظیم نمیکندtitleکه رشته یا{ default, template?, absolute? }نیست، یا کلیدی با املای نادرستtitle.templateبدون%s، یا در صفحه که اثری نداردdescriptionغیررشتهای
route-handler-exports
فایلهای route.ts فقط به متدهای HTTP که صادر میکنند پاسخ میدهند: GET، HEAD، POST، PUT، PATCH، DELETE و OPTIONS. نامهای get یا Post با حروف کوچک هرگز فراخوانی نمیشوند (درخواستها پاسخ 405 میگیرند)؛ پیشنهاد نام آنها را تغییر میدهد. خروجیهای پیشفرض، کنترلگرهای غیرتابعی و دیگر خروجیها گزارش میشوند، بهجز dynamic و revalidate در پیکربندی بخش مسیر. کد مشترک را به lib/ منتقل کنید، یا نامهای اضافی را با { allow: [...] } فهرست کنید.
export function get() { … } // ✗ never calledexport function GET() { … } // ✓