আমি ধাপে ধাপে ব্যাখ্যা সহ Reatom সংগ্রহস্থলে আমার পিআর-এর উপর ভিত্তি করে একটি টিউটোরিয়াল লেখার চেষ্টা করব: https://github.com/artalar/Reatom/pull/488
আপনি যদি আরও জানতে চান, আপনি https://github.com/artalar/reatom/issues/487 সমস্যাটি পড়তে পারেন।
একটু প্রসঙ্গ যোগ করার জন্য, Reatom হল একটি রাষ্ট্রীয় ব্যবস্থাপনা লাইব্রেরি। পরমাণু হল Reatom-এর একটি ধারণা, প্রতিক্রিয়ার জন্য একটি রাষ্ট্রীয় ব্যবস্থাপনা লাইব্রেরি।
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 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;
এটি একটি সাধারণ বৈকল্পিক, কিন্তু এখানে, আমরা সহজেই বুঝতে পারি কি ঘটছে।
আপনার কোডের AST গঠন সম্পর্কে আরও ভালোভাবে বোঝার জন্য, আপনি https://astexplorer.net/ ব্যবহার করতে পারেন অথবা কেবল console.log পার্স করা নোড ব্যবহার করতে পারেন।
এখানে একটি ছোট উদাহরণে প্রতিটি শনাক্তকারীর একটি ছোট বিবরণ রয়েছে:
const kek = atom('kek')
Identifier
: একটি টাইপস্ক্রিপ্ট ইন্টারফেস যা একটি AST-তে একটি শনাক্তকারী নোডকে প্রতিনিধিত্ব করে।
const kek = পরমাণু ('kek'), kek এবং পরমাণু হল শনাক্তকারী নোড।
Literal
: একটি টাইপস্ক্রিপ্ট ইন্টারফেস যা একটি AST-তে একটি আক্ষরিক মান (স্ট্রিং, সংখ্যা, বুলিয়ান, ইত্যাদি) নোডকে প্রতিনিধিত্ব করে। const kek = atom(' kek '), 'kek' একটি আক্ষরিক।
CallExpression
: একটি টাইপস্ক্রিপ্ট ইন্টারফেস যা একটি বিমূর্ত সিনট্যাক্স ট্রি (AST) এ একটি ফাংশন কল এক্সপ্রেশন নোডকে উপস্থাপন করে।
আমাদের উদাহরণে, atom('kek') হল একটি CallExpression, যার মধ্যে রয়েছে পরমাণু - শনাক্তকারী এবং কেক - আক্ষরিক।
VariableDeclarator
: একটি টাইপস্ক্রিপ্ট ইন্টারফেস যা একটি AST-তে একটি পরিবর্তনশীল ঘোষণাকারী নোডকে প্রতিনিধিত্ব করে
আমাদের উদাহরণে, const ব্যতীত সম্পূর্ণ অভিব্যক্তি হল VariableDeclarator kek = atom('kek')
Node
: একটি টাইপস্ক্রিপ্ট ইন্টারফেস যা একটি জেনেরিক AST নোডকে উপস্থাপন করে।
অথবা শুধুমাত্র astexplorer ব্যবহার করে
চূড়ান্ত পরীক্ষা
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)
নোড - একটি প্রকৃত নোড বা চিহ্নের পরিসর হতে পারে যা আপনি প্রতিস্থাপন করতে চান।
রিপ্লেস স্ট্রিং - আপনি কোন কোডটি দেখতে চান।
আপনার নিয়ম মেটা ট্যাগের জন্য fixable : 'code' বা fixable : 'whitespace' যোগ করতে ভুলবেন না।
আপনি যদি eslint এর সাথে কীভাবে এটি ঠিক করবেন তার সাথে পরিচিত না হন তবে আপনার বিদ্যমান প্রকল্পে চেষ্টা করুন।
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 আপডেট করবে এবং প্রতিটি নিয়মের জন্য ডক্স আপডেট করবে (তবে আপনাকে ডক্স/{rule} ফাইলে প্রতিটি নিয়ম সম্পর্কে কিছুটা লিখতে হবে)।
এছাড়াও, যেমন আমি বলেছি, আপনাকে সূচী ফাইল সম্পর্কে চিন্তা করতে হবে না।
সংস্করণটি আপনার 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”
লিখতে হবে)।
এই টিউটোরিয়ালে, আমরা Reatom স্টেট ম্যানেজমেন্ট লাইব্রেরির জন্য একটি ESLint প্লাগইন তৈরি করার প্রক্রিয়ার মধ্য দিয়ে হেঁটেছি। আমরা কভার করি:
আরও শেখার এবং অন্বেষণের জন্য সম্পদ
আনন্দ কর :)