ESLint
eslint-plugin-textjs: konsola uygun JSX, hücre genişliğinde glifler, kare hızına uyumlu zamanlayıcılar ve App Router denetimleri.
eslint-plugin-textjs, Text.js hatalarını editörde yakalar: konsol bileşenlerindeki DOM etiketleri, metin içindeki kutular, Menlo’da bulunmayan glifler, araya giren console.log çağrıları ve sessizce yok sayılan App Router dışa aktarımları.
Kurulum
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);Eklenti, düz yapılandırma ile ESLint 9 ve 10’u destekler. Kurallar sözdizimseldir; tür bilgisine veya parserOptions.project ayarına ihtiyaç duymaz. TypeScript ve JSX’i anlayan her ayrıştırıcı çalışır.
Yapılandırmalar
| Yapılandırma | Etkinleştirdikleri |
|---|---|
textjs.configs.recommended | Tüm doğruluk kuralları hata; konsol, zamanlayıcı ve kare zamanlama kuralları uyarı olarak |
textjs.configs.strict | Her şey hata, ayrıca prefer-use-keymap uyarı olarak |
Bunun yerine kuralları tek tek seçin:
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" }], }, },];Konsol dosyaları
Çoğu kural yalnızca konsola render eden dosyalara uygulanır: @textjs/app veya @textjs/react paketinden bir bileşen ya da hook (Box, Text, useInput, …) içe aktaran dosyalar. react-dom veya @textjs/react/dom içe aktaran dosyalar sayfa kodudur ve bunlara dokunulmaz; böylece bir web sitesi aynı projede DOM sayfalarını ve konsol uygulamalarını bir arada kullanabilir. tokenize veya parseMarkdown gibi bir yardımcı işlevi içe aktarmak, dosyayı konsol dosyası yapmaz.
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 kuralları, src/app/ dahil herhangi bir app/ dizininde dosya adına (page.tsx, layout.tsx, route.ts, …) göre çalışır. Dizininizin adı farklıysa valid-route-module, valid-metadata ve route-handler-exports kurallarına { appDir: "routes" } geçirin.
Kurallar
| Kural | Önerilen | Düzeltme |
|---|---|---|
no-dom-elements | hata | öneri |
no-box-in-text | hata | |
no-unsupported-glyphs | hata | otomatik düzeltme, öneri |
no-console-in-components | uyarı | öneri |
prefer-use-interval | uyarı | öneri |
no-sub-frame-timers | uyarı | |
prefer-use-keymap | yalnızca strict | |
valid-route-module | hata | |
valid-metadata | hata | |
route-handler-exports | hata | öneri |
no-dom-elements
<div>, <span>, özel öğeler ve diğer DOM etiketleri konsolda bulunmaz. Uzlaştırıcı yalnızca kendi ana düğümlerini tanır ve diğer her şey için TX003 hatası fırlatır.
<div className="flex-col"> // ✗<Box className="flex-col"> // ✓<strong>Deployed</strong> // ✗<Text bold>Deployed</Text> // ✓Öneri, etiketi en yakın bileşenle (div → Box, span/p → Text, strong → Text bold, a → Link, input → Input, hr → Divider, …) değiştirir ve içe aktarımı ekler.
no-box-in-text
<Text>, biçimlendirilmiş span dizisidir; kutu içeremez. İçine <Box> veya kökü kutu olan herhangi bir bileşen (<ScrollBox>, <Input>, <Code>, <ProgressBar>, <Divider>, …) yerleştirmek, render sırasında TX004 hatası fırlatır. Bu kural ise hatayı editörde bulur; &&, üçlü koşul ifadeleri, fragment’ler ve .map() içindekiler de buna dahildir.
<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 her karakteri sabit bir hücre ızgarasına yerleştirir. macOS’teki DevTools konsol yazı tipi Menlo’da bulunmayan glifler, bir hücreden daha geniş olan yedek bir yazı tipiyle render edilir; bu yüzden ardından gelen tüm hücreler kayar.
- Braille (U+2800–U+28FF, klasik terminal yükleme göstergesi kareleri): blok veya çeyrek blok karakterlerini (
▘▝▗▖),<Spinner variant="dots">bileşenini ya da grafiklerdemarker="quadrant"ayarını kullanın. Yalnızca terminaldetext runile çalışan uygulamalar için{ allowBraille: true }ayarlayın. - Çeşitli semboller ve oklar (U+2B00–U+2BFF, küçük kare U+2B1D gibi): öneriler Menlo’daki karşılığını (
·,●,←) sunar. - Tek hücrelik sembollerde emoji sunumu (ardından U+FE0F gelen
▶veya↩): ızgara bir hücre ayırırken varyasyon seçicisi bunları iki hücrelik emojiye dönüştürür. Otomatik düzeltme bunu kaldırır.
Geniş emojiler (🚀, ✅) sorun oluşturmaz: Text.js bunları iki hücre olarak ölçer.
no-console-in-components
Her kare bir console.log çağrısıdır. Kendi günlükleriniz karelerin arasına girer, sonraki kareyle kaydırılıp gider ve alta sabitlenen görünümün altında gizlenir. console.clear() uygulamanın kendisini siler.
"textjs/no-console-in-components": ["warn", { allow: ["error"], logger: "log" }]Mesajları arayüzde useToast() veya bir günlük <ScrollBox> bileşeniyle gösterin ya da durumu [▼] Dev Overlay içinde inceleyin (Ctrl+`). logger ile öneri, console.warn(…) çağrısını log.warn(…) olarak yeniden yazar. Test dosyaları ve @textjs/app/testing render işlemleri atlanır.
prefer-use-interval
Bir bileşende doğrudan kullanılan setInterval, eski durum değerlerini kullanmamak için bir effect, bir temizleme işlemi ve bir ref gerektirir. useInterval üçünü de yapar ve null onu durdurur.
useEffect(() => { if (paused) return; const id = setInterval(tick, 1000); return () => clearInterval(id);}, [paused]);useInterval(tick, paused ? null : 1000); // the suggestionAnimasyon için useAnimationFrame, render motorunun kare zamanlamasını izler ve çıktı duraklatıldığında durur. Yardımcı hook’lar bölümüne bakın.
no-sub-frame-timers
Text.js her 66 ms’de en fazla bir kare üretir (yaklaşık 15 fps). 16 ms’lik bir aralık, üretilen her kare başına dört kez render eder; ek kareler birleştirilir ve yalnızca CPU tüketir. minInterval (varsayılan 66) altındaki setInterval/useInterval gecikmelerini ve fps değeri 15’in üzerindeki useAnimationFrame kullanımlarını bildirir.
prefer-use-keymap
e.key değerini elle karşılaştırmak çalışır, ancak kısayollar görünmez. useKeymap, etiketli tuş atamalarını <KeyHelp>, <KeyHints> (hint: true içeren atamalar) ve komut paletine yerleştirir, g g gibi dizileri işler ve eşleşen tuşlar için yayılımı durdurur. İki veya daha fazla tuş sabitini (minKeys) karşılaştıran useInput işleyicilerini bildirir ve yazı girişi için e.input okuyan işleyicileri atlar.
useKeymap({ "up, k": { label: "Increment", run: inc, hint: true }, "down, j": { label: "Decrement", run: dec, hint: true },});Klavye kısayolları bölümüne bakın.
valid-route-module
page,layout,loading,errorvenot-founddosyaları bir bileşeni varsayılan olarak dışa aktarmalıdır (TX015).export const dynamic,"auto","force-static"veya"force-dynamic"olmalıdır (TX031). Text.js, Next.js’in"error"değerini desteklemez. Bu değer sayfalardan, layout’lardan ve rota işleyicilerinden okunur. Sayfalarda ve layout’larda bunu düz bir dize sabiti olarak yazın:text buildmodülü çalıştırmadan kaynak koddan okur.
valid-metadata
Metadata önce layout’lardan, ardından sayfadan (veya not-found dosyasından) birleştirilir. Kural, yönlendiricinin sessizce yok sayacağı durumları bildirir:
loadingveyaerrordosyalarındakimetadataya dagenerateMetadata- aynı dosyada her iki dışa aktarımın bulunması
async generateMetadata: eşzamanlı çağrıldığı için bir Promise başlığı hiçbir zaman ayarlamaz- dize veya
{ default, template?, absolute? }olmayan birtitleya da yanlış yazılmış bir anahtar %siçermeyen veya hiçbir etkisinin olmadığı bir sayfada bulunantitle.template- dize olmayan bir
description
route-handler-exports
route.ts dosyaları yalnızca dışa aktardıkları HTTP yöntemlerine yanıt verir: GET, HEAD, POST, PUT, PATCH, DELETE ve OPTIONS. Küçük harfle başlayan get veya Post hiçbir zaman çağrılmaz (istekler 405 alır); öneri bunları yeniden adlandırır. dynamic ve revalidate rota segmenti yapılandırması dışında varsayılan dışa aktarımlar, işlev olmayan işleyiciler ve diğer dışa aktarımlar bildirilir. Paylaşılan kodu lib/ dizinine taşıyın veya ek adları { allow: [...] } ile listeleyin.
export function get() { … } // ✗ never calledexport function GET() { … } // ✓