विषयसूची
- एक टेम्पलेट का उपयोग कर रेपो प्रारंभ करें
- एक टेम्पलेट की प्रारंभिक संरचना
- टेम्पलेट से स्क्रिप्ट का उपयोग करके नियम जोड़ना
- एस्लिंट प्लगइन के लिए परीक्षण लिखें
- एस्लिंट नियम लिखिए
- लघु एएसटी स्पष्टीकरण
- अंतिम संस्करण
- स्क्रिप्ट का उपयोग करके दस्तावेज़ों को अपडेट करना
- प्लगइन प्रकाशन
- इसे अपने एप्लिकेशन से कनेक्ट करें
पृष्ठभूमि
मैं चरण-दर-चरण स्पष्टीकरण के साथ रीटॉम रिपॉजिटरी में अपने पीआर के आधार पर एक ट्यूटोरियल लिखने की कोशिश करूंगा: https://github.com/artalar/Reatom/pull/488
यदि आप अधिक जानना चाहते हैं, तो आप इस मुद्दे को https://github.com/artalar/reatom/issues/487 पढ़ सकते हैं।
थोड़ा संदर्भ जोड़ने के लिए, रीटॉम एक राज्य प्रबंधन पुस्तकालय है। रिएक्ट के लिए एक राज्य प्रबंधन पुस्तकालय, रीटॉम में परमाणु एक अवधारणा है।
ESLint प्लगइन्स और नियम क्या हैं?
ESLint प्लगइन्स एक्सटेंशन हैं जो विशिष्ट कोडिंग मानकों को लागू करने के लिए कोर ESLint पैकेज के साथ काम करते हैं। प्लगइन्स में एक rules
फ़ोल्डर होता है, जो इन मानकों को लागू करने के लिए अलग-अलग नियमों को परिभाषित करता है।
प्रत्येक rule
मॉड्यूल में एक meta
गुण होता है जो नियम का वर्णन करता है और एक create
गुण होता है जो नियम के व्यवहार को परिभाषित करता है।
create
फ़ंक्शन एक context
तर्क लेता है, जिसका उपयोग चेक किए जा रहे कोड के साथ इंटरैक्ट करने के लिए किया जाता है, और आप इसका उपयोग अपने नियम के तर्क को परिभाषित करने के लिए कर सकते हैं, जैसे आपकी लाइब्रेरी के लिए सख्त नामकरण सम्मेलनों की आवश्यकता होती है।
आइए कोड में गोता लगाएँ
रेपो को इनिशियलाइज़ करें
एक नया टाइपस्क्रिप्ट एस्लिंट प्रोजेक्ट बनाना
npx degit https://github.com/pivaszbs/typescript-template-eslint-plugin reatom-eslint-plugin
फिर, नई परियोजना निर्देशिका पर जाएँ, और इसके साथ निर्भरताएँ स्थापित करें:
cd reatom-eslint-plugin && npm i
मैं एक अच्छा लड़का बनना चाहता हूं, इसलिए मैं गिट में प्रवेश करता हूं।
git init && git add . && git commit -m "init"
अगला, package.json
फ़ाइल खोलें, और name
फ़ील्ड खोजें। यह फ़ील्ड आवश्यक है क्योंकि जब इसका उपयोग किया जाएगा तो यह आपके प्लगइन के लिए मुख्य प्रवेश बिंदु होगा। आप इसे निम्न में बदल सकते हैं:
"name": "eslint-plugin-reatom"
वैकल्पिक रूप से, आप स्कोप्ड पैकेज नामकरण सम्मेलन का उपयोग कर सकते हैं:
"name": "@reatom/eslint-plugin"
प्रारंभिक संरचना
- scripts // some automation to concentrate on writing rules - docs - rules // here will be generated by npm run add-rule files - src - configs recommended.ts // generated config - rules // all your rules index.ts // Connection point to your plugin, autogenerated by scripts/lib/update-lib-index.ts
सामान्य अनुक्रमणिका में, फ़ाइलें स्क्रिप्ट द्वारा उत्पन्न की जाएंगी, इसलिए आपको इसके बारे में चिंता करने की आवश्यकता नहीं है 😀
/* DON'T EDIT THIS FILE. This is generated by 'scripts/lib/update-lib-index.ts' */ import { recommended } from './configs/recommended'; import exampleRule from './rules/example-rule' export const configs = { recommended }; export const rules = { 'example-rule': exampleRule };
नियम जोड़ना और डॉक्स अपडेट करना
इस रिपॉजिटरी में, आपको नियम जोड़ने और दस्तावेजों को अपडेट करने के लिए कुछ सुविधाजनक स्क्रिप्ट्स मिलेंगी। नया नियम जोड़ने के लिए, आप निम्न आदेश का उपयोग कर सकते हैं:
npm run add-rule atom-rule suggestion
यह नए नियम के लिए तीन खंड उत्पन्न करेगा: प्रलेखन, परीक्षण और वास्तविक कोड। हम अभी के लिए प्रलेखन अनुभाग को छोड़ सकते हैं और अंतिम दो पर ध्यान केंद्रित कर सकते हैं।
टेस्ट लिखें
TDD (परीक्षण-संचालित विकास) उत्साही के रूप में, हम tests/atom-rule.ts
फ़ाइल में कुछ सरल परीक्षण बनाकर शुरू करेंगे:
// tests/atom-rule.ts tester.run('atom-rule', atomRule, { valid: [ { code: 'const countAtom = atom(0, "countAtom");' }, ], invalid: [ { code: `const countAtom = atom(0);`, errors: [{ message: 'atom name is not defined' }] }, { code: 'const countAtom = atom(0, "count");', errors: [{ message: 'atom name is defined bad'}] }, ] });
यदि आप अभी परीक्षण चलाते हैं, तो वे विफल हो जाएँगे क्योंकि हमने अभी तक atomRule
लागू नहीं किया है।
नियम लिखना
atomRule
वह जगह है जहाँ हम नियम के व्यवहार को परिभाषित करते हैं। यहाँ एक सरल कार्यान्वयन है:
import { Rule } from "eslint"; const rule: Rule.RuleModule = { meta: { docs: { description: "Add name for every atom call", // simply describe your rule recommended: true, // if it's recommended, then npm run update will add it to recommmended config }, type: "suggestion" }, create: function (context: Rule.RuleContext): Rule.RuleListener { return { VariableDeclaration: node => { // listener for declaration, here we can specifiy more specific selector node.declarations.forEach(d => { if (d.init?.type !== 'CallExpression') return; if (d.init.callee.type !== 'Identifier') return; if (d.init.callee.name !== 'atom') return; if (d.id.type !== 'Identifier') return; // just guard everything that we don't need if (d.init.arguments.length <= 1) { // show error in user code context.report({ message: `atom name is not defined`, // here we can pass what will be underlined by red/yellow line node, }) } if (d.init.arguments[1]?.type !== 'Literal') return; // just another guard if (d.init.arguments[1].value !== d.id.name) { context.report({ message: `atom name is defined bad`, node }) } }) } }; }, }; export default rule;
यह एक साधारण प्रकार है, लेकिन यहाँ, हम आसानी से समझ सकते हैं कि क्या हो रहा है।
अपने कोड की एएसटी संरचना की बेहतर समझ के लिए, आप https://astexplorer.net/ या केवल कंसोल.लॉग पार्स किए गए नोड्स का उपयोग कर सकते हैं।
एएसटी टाइपिंग की बेहतर समझ के लिए एक छोटा सा स्पष्टीकरण
यहाँ एक छोटे से उदाहरण में प्रत्येक पहचानकर्ता का एक छोटा सा विवरण दिया गया है:
const kek = atom('kek')
Identifier
: एक टाइपस्क्रिप्ट इंटरफ़ेस जो एएसटी में एक पहचानकर्ता नोड का प्रतिनिधित्व करता है।const kek = atom ('kek'), केक, और परमाणु पहचानकर्ता नोड हैं।
Literal
: एक टाइपस्क्रिप्ट इंटरफ़ेस जो एएसटी में एक शाब्दिक मान (स्ट्रिंग, संख्या, बूलियन, आदि) नोड का प्रतिनिधित्व करता है। const kek = atom(' kek '), 'kek' एक शाब्दिक है।
CallExpression
: एक टाइपस्क्रिप्ट इंटरफ़ेस जो एक सार सिंटैक्स ट्री (एएसटी) में फ़ंक्शन कॉल एक्सप्रेशन नोड का प्रतिनिधित्व करता है।
हमारे उदाहरण में, atom('kek') एक कॉलएक्सप्रेशन है, जिसमें परमाणु-पहचानकर्ता और केक-शाब्दिक होते हैं।
VariableDeclarator
: एक टाइपस्क्रिप्ट इंटरफ़ेस जो एएसटी में एक वेरिएबल डिक्लेरेटर नोड का प्रतिनिधित्व करता है
हमारे उदाहरण में, const को छोड़कर संपूर्ण व्यंजक VariableDeclarator kek = atom('kek') है
Node
: एक टाइपस्क्रिप्ट इंटरफ़ेस जो एक सामान्य एएसटी नोड का प्रतिनिधित्व करता है।
या बस एस्टएक्सप्लोरर का उपयोग करना
अंतिम संस्करण
अंतिम परीक्षण
tester.run('atom-rule', rule, { valid: [ { code: ` import { atom } from '@reatom/framework' const countAtom = atom(0, "countAtom"); ` }, { code: `const countAtom = atom(0);`, }, { code: 'const countAtom = atom(0, "count");', }, ], invalid: [ { code: ` import { atom } from '@reatom/framework' const countAtom = atom(0); `, errors: [{ message: 'atom "countAtom" should has a name inside atom() call', }], output: ` import { atom } from '@reatom/framework' const countAtom = atom(0, "countAtom"); `, }, { code: ` import { atom } from '@reatom/framework' const countAtom = atom(0, "count"); `, errors: [{ message: `atom "countAtom" should be named as it's variable name, rename it to "countAtom"` }], output: ` import { atom } from '@reatom/framework' const countAtom = atom(0, "countAtom"); `, }, ] });
परीक्षणों से, हम समझते हैं कि हमें अपने नियम का उपयोग करके स्रोत कोड को किसी तरह बदलने की आवश्यकता है।
अपने नियम को कैसे ठीक करें?
संदर्भ रिपोर्ट में एक सरल रेखा जोड़ें।
fix: fixer => fixer.replaceText(node, replaceString)
नोड - एक वास्तविक नोड या प्रतीकों की श्रेणी हो सकती है जिसे आप बदलना चाहते हैं।
replaceString - आप किस कोड को देखने की उम्मीद करते हैं।
अपने नियम मेटा टैग के लिए फिक्सेबल : 'कोड' या फिक्सेबल : 'व्हाट्सएप' जोड़ना न भूलें।
यदि आप इसे एस्लिंट से ठीक करने के तरीके से परिचित नहीं हैं, तो बस अपने मौजूदा प्रोजेक्ट पर प्रयास करें।
eslint --fix ./src
कोड स्वयं
import { Rule } from "eslint"; import { CallExpression, Identifier, Literal, VariableDeclarator, Node } from 'estree'; import { isIdentifier, isLiteral } from "../lib"; type AtomCallExpression = CallExpression & { callee: Identifier, arguments: [Literal] | [Literal, Literal] } type AtomVariableDeclarator = VariableDeclarator & { id: Identifier, init: AtomCallExpression } const noname = (atomName: string) => `atom "${atomName}" should has a name inside atom() call`; const invalidName = (atomName: string) => `atom "${atomName}" should be named as it's variable name, rename it to "${atomName}"`; export const atomRule: Rule.RuleModule = { meta: { type: 'suggestion', docs: { recommended: true, description: "Add name for every atom call" }, fixable: 'code' }, create: function (context: Rule.RuleContext): Rule.RuleListener { let importedFromReatom = false; return { ImportSpecifier(node) { const imported = node.imported.name; // @ts-ignore const from = node.parent.source.value; if (from.startsWith('@reatom') && imported === 'atom') { importedFromReatom = true; } }, VariableDeclarator: d => { if (!isAtomVariableDeclarator(d) || !importedFromReatom) return; if (d.init.arguments.length === 1) { reportUndefinedAtomName(context, d); } else if (isLiteral(d.init.arguments[1]) && d.init.arguments[1].value !== d.id.name) { reportBadAtomName(context, d); } } }; } } function isAtomCallExpression(node?: Node | null): node is AtomCallExpression { return node?.type === 'CallExpression' && node.callee?.type === 'Identifier' && node.callee.name === 'atom'; } function isAtomVariableDeclarator(node: VariableDeclarator): node is AtomVariableDeclarator { return isAtomCallExpression(node.init) && isIdentifier(node.id); } function reportUndefinedAtomName(context: Rule.RuleContext, d: AtomVariableDeclarator) { context.report({ message: noname(d.id.name), node: d, fix: fixer => fixer.insertTextAfter(d.init.arguments[0], `, "${d.id.name}"`) }); } function reportBadAtomName(context: Rule.RuleContext, d: AtomVariableDeclarator) { context.report({ message: invalidName(d.id.name), node: d, fix: fixer => fixer.replaceText(d.init.arguments[1], `"${d.id.name}"`) }); }
जैसा कि आप देख सकते हैं, इसमें केवल बेहतर त्रुटियाँ हैं, गार्ड टाइप करें, और आयात जाँच शामिल है। और, ज़ाहिर है, मैं नियम को ठीक करने योग्य बनाता हूँ।
डॉक्स को अपडेट कर रहा है
दस्तावेज़ों को अद्यतन करने के लिए, आप निम्न आदेश का उपयोग कर सकते हैं:
npm run update
यह आदेश प्रत्येक नियम के लिए README.md और अद्यतन दस्तावेज़ अपडेट करेगा (लेकिन आपको दस्तावेज़/{नियम} फ़ाइल में प्रत्येक नियम के बारे में कुछ लिखने की आवश्यकता है)।
साथ ही, जैसा कि मैंने कहा, आपको अनुक्रमणिका फ़ाइल के बारे में चिंता करने की आवश्यकता नहीं है।
चरण प्रकाशित करें
सुनिश्चित करें कि संस्करण आपके package.json में है।
"version": "1.0.0"
अगर यह 1.0.0 नहीं है तो टर्म में लिखें।
npm version 1.0.0
तो बस जड़ में लिखो।
npm publish
सब कुछ आपके परिभाषित पैकेज नाम के साथ बनाया और प्रकाशित किया जाएगा।
इसे अपने एप्लिकेशन से कनेक्ट करें
मैं अपने पैकेज का नाम देता हूं।
@reatom/eslint-plugin
तो, मुझे इसे स्थापित करने की ज़रूरत है।
npm i @reatom/eslint-plugin
और मेरे .eslintrc कॉन्फ़िग में जोड़ें।
module.exports = { plugins: [ "@reatom" ], // use all rules extends: [ "plugin:@reatom/recommended" ], // or pick some rules: { '@reatom/atom-rule': 'error', // aditional rules, you can see it in PR '@reatom/action-rule': 'error', '@reatom/reatom-prefix-rule': 'error' } }
और सब कुछ बस काम करता है (सिर्फ reatom-eslint-plugin
के लिए आपको हर जगह “@reatom"
के बजाय " “reatom”
लिखना चाहिए)।
निष्कर्ष
इस ट्यूटोरियल में, हमने रीटॉम स्टेट मैनेजमेंट लाइब्रेरी के लिए ESLint प्लगइन बनाने की प्रक्रिया के बारे में जाना। हम कवर करते हैं:
- टाइपस्क्रिप्ट में एस्लिंट प्लगइन कैसे लिखें।
- इसे टेस्ट से कैसे कवर करें।
- इसे --fix विकल्प के साथ कैसे काम करें।
- मेरे टेम्पलेट का उपयोग कैसे करें।
- एस्लिंट प्लगइन को कैसे प्रकाशित करें।
- एस्लिंट के साथ इसे अपने मौजूदा रिपॉजिटरी में कैसे जोड़ें
आगे सीखने और अन्वेषण के लिए संसाधन
- https://github.com/pivaszbs/typescript-template-eslint-plugin
- https://astexplorer.net/
- https://github.com/artalar/reatom/pull/488/files
- https://eslint.org/docs/latest/extend/plugins
- https://www.reatom.dev/
- https://github.com/artalar/reatom
- https://docs.npmjs.com/about-semantic-versioning
मस्ती करो :)