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-eslint
eslint.config.jsjs
import { 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 चेतावनी के रूप में

इसके बजाय नियम अलग-अलग चुनें:

eslint.config.jsjs
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" }],    },  },];

कंसोल फ़ाइलें

ज़्यादातर नियम सिर्फ़ कंसोल में रेंडर करने वाली फ़ाइलों पर लागू होते हैं: वे फ़ाइलें जो @textjs/app या @textjs/react से कोई कंपोनेंट या हुक (Box, Text, useInput, …) इंपोर्ट करती हैं। react-dom या @textjs/react/dom इंपोर्ट करने वाली फ़ाइलें पेज कोड हैं और उन्हें नहीं छुआ जाता, इसलिए एक वेबसाइट एक ही प्रोजेक्ट में DOM पेज और कंसोल ऐप मिला सकती है। tokenize या parseMarkdown जैसी यूटिलिटी इंपोर्ट करने से फ़ाइल कंसोल फ़ाइल नहीं बनती।

eslint.config.jsjs
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 के नियम src/app/ सहित किसी भी app/ डायरेक्टरी में फ़ाइल के नाम (page.tsx, layout.tsx, route.ts, …) के आधार पर लागू होते हैं। अगर आपकी डायरेक्टरी का नाम कुछ और है, तो valid-route-module, valid-metadata और route-handler-exports को { appDir: "routes" } दें।

नियम

नियमअनुशंसितसुधार
no-dom-elementsएररसुझाव
no-box-in-textएरर
no-unsupported-glyphsएररऑटोफ़िक्स, सुझाव
no-console-in-componentsचेतावनीसुझाव
prefer-use-intervalचेतावनीसुझाव
no-sub-frame-timersचेतावनी
prefer-use-keymapसिर्फ़ strict में
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 components

no-unsupported-glyphs

Text.js हर कैरेक्टर को एक तय सेल ग्रिड में रखता है। macOS पर DevTools कंसोल के फ़ॉन्ट Menlo में जो ग्लिफ़ नहीं हैं, वे सेल से चौड़े फ़ॉलबैक फ़ॉन्ट में रेंडर होते हैं, इसलिए उनके बाद का हर सेल खिसक जाता है।

  • ब्रेल (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" }]

UI में useToast() या लॉग <ScrollBox> से संदेश दिखाएं, या [▼] Dev Overlay (Ctrl+`) में स्टेट की जांच करें। logger के साथ, सुझाव console.warn(…) को log.warn(…) में बदल देता है। टेस्ट फ़ाइलों और @textjs/app/testing रेंडर को छोड़ दिया जाता है।

prefer-use-interval

कंपोनेंट में सीधे setInterval इस्तेमाल करने पर पुराने स्टेट से बचने के लिए इफ़ेक्ट, क्लीनअप और रेफ़ की ज़रूरत होती है। 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 खर्च करते हैं। यह minInterval (डिफ़ॉल्ट 66) से कम setInterval/useInterval डिले और 15 से ज़्यादा fps वाले useAnimationFrame की रिपोर्ट करता है।

prefer-use-keymap

e.key की तुलना खुद करना काम करता है, लेकिन शॉर्टकट दिखते नहीं हैं। useKeymap लेबल वाली बाइंडिंग को <KeyHelp>, <KeyHints> (hint: true वाली बाइंडिंग) और कमांड पैलेट में रखता है, g g जैसे क्रम संभालता है और मैच हुई कुंजियों के लिए प्रोपेगेशन रोकता है। यह दो या उससे ज़्यादा की लिटरल (minKeys) की तुलना करने वाले useInput हैंडलर की रिपोर्ट करता है और टाइपिंग के लिए 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, Next.js के "error" को सपोर्ट नहीं करता। इसे पेज, लेआउट और रूट हैंडलर से पढ़ा जाता है। पेज और लेआउट में इसे साधारण स्ट्रिंग लिटरल के रूप में लिखें: text build मॉड्यूल चलाए बिना इसे सोर्स से पढ़ता है।

valid-metadata

Metadata पहले लेआउट से, फिर पेज (या not-found) से मर्ज होता है। यह नियम उन चीज़ों की रिपोर्ट करता है जिन्हें राउटर बिना बताए अनदेखा कर देता:

  • loading या error फ़ाइलों में metadata या generateMetadata
  • एक फ़ाइल में दोनों एक्सपोर्ट
  • async generateMetadata: इसे सिंक्रोनस रूप से कॉल किया जाता है, इसलिए Promise कभी टाइटल सेट नहीं करता
  • ऐसा title जो स्ट्रिंग या { default, template?, absolute? } नहीं है, या गलत वर्तनी वाली की
  • %s के बिना title.template, या पेज में title.template, जहां इसका कोई असर नहीं होता
  • गैर-स्ट्रिंग description

route-handler-exports

route.ts फ़ाइलें सिर्फ़ उन HTTP मेथड का जवाब देती हैं जिन्हें वे एक्सपोर्ट करती हैं: GET, HEAD, POST, PUT, PATCH, DELETE और OPTIONS। छोटे अक्षरों वाला get या Post कभी कॉल नहीं होता (रिक्वेस्ट को 405 मिलता है); सुझाव इसका नाम बदल देता है। dynamic और revalidate रूट सेगमेंट कॉन्फ़िग को छोड़कर, डिफ़ॉल्ट एक्सपोर्ट, गैर-फ़ंक्शन हैंडलर और अन्य एक्सपोर्ट की रिपोर्ट की जाती है। साझा कोड को lib/ में ले जाएं, या { allow: [...] } के साथ अतिरिक्त नाम सूचीबद्ध करें।

app/api/health/route.tsts
export function get() { … }   // ✗ never calledexport function GET() { … }   // ✓
GitHub पर यह पेज एडिट करें