برنامه‌نویسی

آموزش ساخت پکیج NPM


۲۵ شهریور ۱۳۹۹
چگونگی ساخت یک پکیج npm

با وجود جامعه‌های متن‌باز، توسعه‌دهندگان به ابزارها، کتابخانه‌ها و بسیاری از موارد مختلف دیگر به‌صورت رایگان دسترسی دارند. با تشکر از ابزارهای مدیریت پکیج مانند NPM (Node Package Manager)، سرعت و راحتی استفاده از این پکیج‌ها که توسط توسعه‌دهندگان مختلف انتشار پیدا کرده و نگهداری می‌شوند، چند برابر شده است. این مقاله مناسب افرادی است که می‌خواهند پکیج‌های خود را منتشر کنند ولی از چگونگی انجام این کار آگاهی ندارند. با ما همراه باشید تا در ادامه مقاله، نحوه ایجاد یک پکیج را بررسی کنیم.

قدم اول: تصمیم گیری برای انتشار یا عدم انتشار پکیج

بیایید فرض‌کنیم که شما پروژه‌های مفید و موفقی را توسعه داده‌اید (مانند redux middlewares که برای اتصال به API، سیستم Auth یا تازه‌سازی توکن از آن استفاده می‌شود). از آنجا که بلندپرواز هستید و copy/paste کردن کدها شما را راضی نمی‌کند، بدین ترتیب هر بار که پروژه جدیدی را شروع می‌کنید، این بخش مشترک را بهبود می‌بخشید. در برهه‌ای از زمان متوجه می‌شوید که به لطف تجربه به دست آمده از پروژه‌های مختلف، یک راه‌حل کارآمد و قابل اعتماد برای رفع یک مشکل خاص ایجاد کرده‌اید. باید تصمیم بگیرید که می‌خواهید با این راه‌حل چکار کنید. آیا تا‌به‌حال فکر کرده‌اید که اشتراک‌گذاری این راه‌ حل چقدر ارزشمند است؟

همه توسعه‌دهندگان حق دارند که پکیج npm خود را به‌صورت رایگان منتشر کنند اما ایده بهتر این است که شما اول از همه به ارزش این کار فکر کنید. بر اساس سایت modulecounts.com، رجیستری اصلی npm شامل بیش از ۷۰۰ هزار ماژول مختلف می‌شود. این تعداد زیاد از پکیج را می‌توان هم به‌عنوان مزیت دانست و هم می‌توان آن را یک عیب تلقی کرد. مزیت این تعداد بالا از ماژول‌ها را می‌توان این دانست که هرچه نیاز داشته باشید، احتمالا توسط یک شخص دیگر منتشر شده است، اما ضعف اصلی در نحوه پیدا کردن ماژول مورد نظرمان است، فرض کنید باید در انبار کاه دنبال سوزن بگردید. علاوه‌براینها اگر شما چندین پکیج بسته به نیازتان پیدا کنید، ممکن است در انتخاب مناسب‌ترین پکیج با مشکل روبرو شوید.

متاسفانه یکی از موارد معمول این است که تعداد زیادی از ماژول‌ها یک عملکرد مشابه را پیاده‌سازی می‌کنند، اما تعداد معدودی از آنها ارزش استفاده دارند. به‌عنوان یک توسعه‌دهنده قبل از انتشار پکیج‌تان باید از خود سوالاتی بپرسید:

  1. آیا راه‌ حل به‌دست‎ آمده توسط شما به اندازه کافی خوب است که به سایر توسعه‌دهندگان کمک کند؟
  2. آیا درحال حاضر پکیجی وجود دارد که دقیقا همان کار را انجام دهد؟ درصورت وجود، بهتر نیست که به توسعه‌دهندگان آن پکیج کمک کنیم تا اینکه در مقابل آنها قرار بگیریم؟
  3. آیا پکیج شما قابلیتی را ارائه می‌دهد که آن را از سایر پکیج‌ها متمایز می‌کند؟
  4. آیا درصورت محبوب شدن پکیج، شما توانایی توسعه و نگهداری از آن را خواهید داشت؟

مورد چهارم هنوز هم جای بحث دارد. بیایید فرض کنیم که اولین نسخه از پکیج شما انتشار پیدا کرد و به محبوبیتی نسبی رسید اما پس از چند به‌روزرسانی شما تصمیم گرفتید که روند توسعه را متوقف کنید. به این صورت سایر توسعه‌دهندگان و افرادی که از پکیج شما استفاده می‌کرده‌اند با مشکل روبرو خواهند شد و باید به‌سرعت دنبال جایگزینی دیگر باشند، زیرا ویژگی‌های مورد نیازشان در آخرین نسخه از پکیج شما در دسترس نیست. البته این بدان معنی نیست که ما باید حتما نگهداری از پکیج‌‌مان را به‌تنهایی برعهده بگیریم و ویژگی‌های جدید را خودمان اضاف کنیم. دیگر توسعه‌دهندگان نیز می‌توانند به پکیج ما pull request بفرستند و تمام چیزی که ما نیاز داریم، یک نویسنده فنی برای دنبال کردن تغییرات و شخصی دیگر برای تایید آنها است.

یکی از جنبه‌های دیگری که ممکن است عوارض‌های بعدی را در پی داشته باشد، نگهداری پکیج به روشی سهل انگارانه و غیرمسئولانه است. اگر شما کدهایی به پکیچ اضاف کنید که با نسخه قبلی سازگار نیستند و این مورد را به توسعه‌دهندگان اطلاع ندهید، آنها بسیار از این اتفاق ناراحت خواهد شد زیرا این مورد ممکن است مشکل‌ساز شود و آنها باید تلاش کنند تا علت این اشکال غیر منتظره در برنامه‌شان، که تا چندی قبل به‌خوبی کار می‌کرده است را پیدا کنند.

قدم دوم: ساخت package.json

مانند هر پروژه جاوااسکریپت اولین قدم برای شروع کار با یک ماژول جدید، اجرای دستور npm init (اگر از yarn استفاده می‌کنید، دستور yarn init) است. پس از آن چندین سوال از شما پرسیده می‌شود که به‌صورت پیش‌فرض ارائه می‌شوند، بنابراین اگر می‌خواهید از آنها بگذرید فقط در پاسخ به سوالات، دکمه Enter را فشار دهید یا می‌توانید دستور اولیه خود را به‌صورت npm init -y یا اگر از yarn استفاده می‌کنید، دستور yarn init -y را اجرا کنید. اگر می‌خواهید بدانید که با اجرای این دستور چه اتفاقی می‌افتد باید بگویم پس از اجرای دستورهایی که در بالا به آنها اشاره کردیم، فایل package.json ایجاد می‌شود و با توجه به سوال‌هایی که از ما پرسیده می‌شوند، جزئیاتی مربوط به ماژول در آن قرار می‌گیرد.

فایل package.json
بک فایل package.json، حاوی مقادیر پیش‌فرض

فایل package.json اهداف مختلفی را دنبال می‌کند، اما مهم‌ترین هدف این فایل، یک سری منابع و اطلاعات اساسی از ماژول و نحوه پیکربندی آن است که در اختیار کاربران قرار داده می‌شود. براساس دستور‌العمل‌های رسمی برای ساخت یک ماژول نیاز است که یک نام برای پکیج انتخاب کرده و نسخه آن را بر اساس نسخه‌گذاری معنادار (semantic versioning) تعیین کنیم. البته بعضی افراد معتقدند که این موارد کافی نیستند و حتما باید توضیحات، نوع لایسنس و کلمه‌های کلیدی برای ساخت پکیج اجباری باشند. با این موارد یافتن پکیج و آگاهی از شرایط استفاده آن برای همه آسان‌تر خواهد شد.

همچنین ارائه اطلاعات توسعه‌دهنده پکیج، راه ارتباطی سریع‌تری را در اختیار سایر توسعه‌دهندگان قرار می‌دهد و ایده خوبی است که به این مورد هم توجه داشته باشید. علاوه‌براینها اگر می‌خواهید توسعه‌دهندگان دیگر را برای کمک به خودتان در توسعه بیشتر پروژه تشویق کنید، باید آدرس ریپازیتوری و صفحه اصلی پکیج‌تان را نیز اضاف کنید.

جستجوی ماژول در npm
اطلاعات اضافی وارد شده در package.json باعث تسهیل جستجوی پکیج npm شده و در نهایت باعث می‌شود که پکیج‌تان حرفه‌ای‌تر به‌نظر برسد.

سایر گزینه‌هایی که در فرایند انتشار و پیکربندی پکیج مهم هستند، عبارتند از:

  1. main: در این بخش، فایل اصلی ماژول را تعریف می‌کنیم. توسط این قابلیت قادر خواهید بود که بدون مشخص کردن مسیر کامل ماژول بتوانید از آن استفاده کنید (برای مثال: const myModule = require('my-module')). با این تکه کد، فایلی که در بخش main قرار داده شده فراخوانی می‌شود. درصورتی که بخواهید ماژول خود را به‌صورت پیش‌فرض فراخوانی کنید باید به‌این صورت عمل کنید: const myModule = require('my-module/index.js')
  2. scripts: این مورد توسعه‌دهندگان را قادر می‌سازد تا انجام وظایف خاصی را خودکار کنند برای مثال شما می‌توانید کلید‌هایی تعریف کنید که هر کدام از آنها اسکریپت‌های مورد نیاز شما را اجرا می‌کنند به طور مثال می‌توانید کلید prepublish، قبل از انتشار، پروژه‌تان را بیلد کنید. همچنین برای اطلاعات بیشتر می‌توانید به مستندات Scripts مراجعه کنید.
  3. dependencies: می‌توان در این بخش وابستگی‌های ماژول را تعیین کرد. در ادامه انواع وابستگی‌ها را توضیح خواهیم داد.

انواع وابستگی‌ها

انواع مختلفی از وابستگی‌ها وجود دارد پس بسیار مهم است که تفاوت بین آنها را بدانید و از نوع مناسبی بر اساس نیاز خود استفاده کنید:

dependencies: این وابستگی‌ها برای عملکرد ماژول، ضروری هستند بنابراین به‌صورت خودکار هنگام نصب ماژول توسط npm نصب می‌شوند و هر زمان که ماژولی را توسط ابزار npm نصب کنید، به‌صورت خودکار در این بخش اضاف می‌شود یا خودتان می‌توانید به‌صورت دستی این قسمت را ویرایش کرده و وابستگی دیگری را اضاف کنید.

devDependencies: این وابستگی‌ها مربوط به زمانی هستند که پروژه در حال توسعه است و بیشتر شامل مواردی مانند transpilation، static type checking، unit testing، linters، code beautifiers و … است. این وابستگی‌ها به همراه ماژول نصب نمی‌شوند.

peerDependencies: این نوع وابستگی‌ها شبیه به dependencies، برای اجرای ماژول ضروری هستند. اولین تفاوت در این است که آنها به‌صورت خودکار نصب نمی‌شوند، بنابراین توسعه‌دهنده برنامه باید آنها را به‌صورت دستی همراه با ماژول اضافه کند. البته باید افزود که در بعضی موارد استفاده از وابستگی‌های این بخش برای عملکرد صحیح برنامه اجباری است.

optionalDependencies: همان‌طور که از نام این نوع وابستگی‌ها پیداست، برای اجرای ماژول اجباری نیستند اما می‌توانند اجرای ماژول را از جهاتی بهبود ببخشند. درصورت امکان، این موارد به‌صورت خودکار همراه ماژول نصب می‌شوند. استفاده از وابستگی‌های اختیاری در زمانی که از پلتفرم‌های خاص (مانند Linux، Windows، MacOS) استفاده می‌کنید، تفاوت چشم‌گیری را در عملکرد ایجاد می‌کنند و در مواردی اگر وجود نداشته باشند باید خودمان مدیریت کدها را به‌دست بگیریم.

به‌طور خلاصه، با اختصاص دادن نوع مناسب به هر ماژول در بخش وابستگی‌ها می‌توانیم در زمان نصب تعیین کنیم که کدام ماژول‌ها برای اجرا الزامی، اختیاری یا فقط در توسعه آن ماژول مورد نیاز هستند و گاهی اوقات این موارد در اجرای ماژول اهمیت زیادی پیدا می‌کنند.

درصورت لزوم می‌توانید سایر گزینه‌هایی مانند configuration و metadata را به فایل package.json اضاف کنید که مربوط به خود ماژول می‌شوند و همچنین نحوه کار با آن را دربر می‌گیرند. برای کسب اطلاعات بیشتر می‌توانید از مستندات رسمی package.json استفاده کنید.

از آنجا که فایل package.json شما را قادر می‌سازد کلید‌های سفارشی تعریف کنید که در بعضی موارد به عملکرد npm هم مربوط نمی‌شوند، بنابراین می‌توانید از آن به‌عنوان یک فایل پیکربندی برای ابزارهای اضافی در طول توسعه استفاده کنید. کلیدهایی که معمولا در این بخش یافت می‌شوند عبارتند از:

  1. jest: شامل پیکربندی فریم‌ورک jest می‌شود که به جهت انجام unit test مورد استفاده قرار می‌گیرد.
  2. eslintConfig: این مورد برای پیکربندی ابزار ESlint است که برای کنترل کیفیت کد استفاده می‌شود.
  3. prettier: تعریف سبک کدهایی که در Prettier استفاده شده‌اند.
  4. husky: می‌توان یک سری وظیفه اضافی تعریف کرد که در هنگام کار با مخزن کدهایتان، توسط husky به‌طور خودکار اجرا شوند.

قدم سوم: سایر فایل‌های مهم

هر پروژه متن‌باز قابل قبول باید دارای مستندات باشد، پکیج‌های npm از این قاعده مستثنی نیستند. npm با پیروی از قرارداد GitHub که بسیار محبوب است از فایل README.md ‌به‌عنوان توضیحات پکیج استفاده می‌کند. به کارهای اضافی دیگری نیاز نیست، فقط اگر همین مورد را انجام داده باشید، بسیار خوب است.

همچنین فایل‌های .gitignore و .npmignore کاربردهایی نیز دارند. فایل .gitignore مستقیما به سیستم کنترل نسخه git مربوط می‌شود و می‌توان در این فایل تعیین کرد که کدام فایل‌ها باید نادیده گرفته شوند، همچنین برای npm هم می‌توان از آن استفاده کرد. فایل .npmignore هم به همین صورت است ولی فقط به npm مربوط می‌شود. هردوی این فایل‌ها تعیین می‌کنند که کدام بخش از کدهای پکیج در مخزن npm قرار بگیرند. اگر شما فایل .npmignore را در پروژه‌تان نساخته باشید ولی فایل .gitignore در دسترس باشد، از قوانین موجود در .gitignore استفاده می‌شود. اما اگر می‌خواهید قوانین جداگانه‌ای برای هرکدام قراردهید باید هر دو فایل را ایجاد کنید. در این شرایط اگر شما یک فایل خالی با نام .npmignore در پروژه‌تان داشته باشید، تمام قوانین موجود در فایل .gitignore نادیده گرفته می‌شوند و تمام فایل‌های پروژه در مخزن npm قرار می‌گیرند (توجه داشته باشید که node_modules همیشه مستثنی است). استفاده از این فایل‌ها ضروری نیست اما می‌تواند اندازه پکیج را بهبود ببخشد، به‌عنوان مثال می‌توان از فایل‌هایی که استفاده نشده‌اند صرف نظر کرد.

از آنجا که در حذف پکیج‌های منتشر شده مشکل‌هایی وجود دارد، ابزار npm این امکان را در اختیار شما قرار می‌دهد تا با دستور npm pack یک پکیج آزمایشی ایجاد کنید. این دستور یک آرشیو ایجاد می‌کند که شامل تمام فایل‌های پروژه است، بنابراین شما می‌توانید ببینید که دقیقا چه فایل‌هایی در مخزن npm بارگذاری می‌شوند. برای اطمینان بیشتر می‌توانید پکیج آرشیو شده خود را بدون نیاز به انتشار فقط با قرار دادن مسیر ماژول به‌جای نام ماژول نصب کنید.

npm install ../my-module/my-module-1.3.0.tgz

البته باید توجه داشته باشید که اگر فایل آرشیو شده را در هنگام انتشار پکیج حذف نکرده باشید، همراه با فایل‌های اصلی پکیج وارد مخزن npm می‌شود. بنابراین بهتر است که در فایل .npmignore قوانینی را اضاف کنید.

نادیده گرفتن فایل‌های tgz توسط فایل .npmignore

قدم چهارم: استقرار و نگهداری از پکیج

برای اینکه بتوانید پکیج خود را منتشر کنید نیاز است که یک حساب در سایت npm داشته باشید و توسط آن به npm وارد شوید. بقیه مراحل بسیار ساده هستند (به استثنای موارد پیشرفته)، فقط لازم است که دستور npm publish را اجرا کنید.

این یک روش معمول برای انتشار پکیج در npm است. اگر پوشه‌ای که این دستور در آن اجرا شده، دارای یک فایل معتبر package.json باشد، فایل‌های پکیج برای انتشار آماده و توسط قوانین موجود در فایل‌‌های .gitignore و .npmignore فیلتر می‌شوند. پس از آن یک آرشیو از فایل‌ها ایجاد شده و در مخزن npm بارگذاری می‌شود. از این مرحله به بعد، همگان می‌توانند این پکیج را نصب و استفاده کنند. اگرچه ممکن است کمی زمان نیاز داشته باشد تا در نتایج جستجو ظاهر شود. یکی از موارد مهمی که قبل از انتشار پکیج باید به آن دقت داشته باشید این است که داده‌های حساس را حذف کنید (رمزهای عبور، API key). شما می‌توانید این موارد را به‌طور موقت توسط قوانین موجود در .gitignore و .npmignore فیلتر کنید که این راه حل بهتری است یا می‌توانید موارد حساس را از پروژه حذف یا به مسیری دیگر انتقال دهید. فقط با ایجاد یک پکیج آزمایشی از کارکرد صحیح پکیج اطمینان حاصل کنید.

باید افزود که شما می‌توانید پکیج‌هایتان را به صورت خصوصی انتشار دهید و همان‌طور که از نام آن پیداست، برای سایر کاربران در دسترس نیست. البته استفاده از این قابلیت نیازمند پرداخت هزینه است. برای انتشار یک پکیج خصوصی باید از یک scope استفاده کنید که پیشوندی منحصربه‌فرد (نام‌کاربری) به نام پکیج اضاف می‌شود. استفاده از scope به‌خودی‌خود رایگان است اما برای پکیج‌های خصوصی اجباری است. همچنین اگر بعدا نظرتان تغییر کرد می‌توانید پکیج خصوصی خود را به‌صورت عمومی منتشر کنید.

شما با استفاده از سوئیچ -tag می‌توانید نسخه‌های جایگزین برای پکیج‌تان منتشر کنید. به‌طور پیش‌فرض هر نسخه‌ای که منتشر می‌شود با تگ latest در مخزن npm قرار می‌گیرد (اگر درهنگام نصب پکیج شماره نسخه یا تگ خاصی استفاده نشود، تگ latest به‌صورت پیش‌فرض نصب می‌شود). شما می‌توانید از تگ‌های اختصاصی مانند beta نیز استفاده کنید. این کاربران را قادر می‌سازد تا به‌راحتی به یک نسخه خاص از پکیج دسترسی داشته باشند و بتوانند آن را نصب کنند.

npm install my-module@beta

گزینه‌هایی برای تجارت

اگرچه در این مقاله سعی داریم که npm را از دیدگاه پروژه‌های کوچک و متوسط متن‌باز توصیف کنیم اما شاید بهتر باشد به برخی گزینه‌های موجود برای شرکت‌ها نیز اشاره‌ای داشته باشیم. npm نه‌تنها اجازه انتشار پکیج‌های خصوصی را در اختیار شما قرار می‌دهد بلکه اجازه استفاده از مخزن خصوصی را به شما می‌دهد.

مخزن خصوصی تضمین می‌کند که پکیج‌ها فقط توسط کاربران مجاز قابل دسترسی باشند و اگر شما از میزبانی خودتان استفاده کنید پکیج‌تان به‌صورت مستقل از زیرساخت‌های npm درمی‌آید.

مطلبی کوتاه درباره نگهداری از پکیج

از آنجا که هر دفعه شما بخواهید پکیج‌تان را انتشار دهید، نیاز است یک نسخه جدید برای آن در نظر بگیرید، مهم است که به‌درستی آن را نسخه‌گذاری کنید. ما قبلا درباره نسخه‌گذاری معنادار (semantic versioning) مقاله‌ای را در اختیار شما قرار داده‌ایم که می‌توانید با مطالعه آن مناسب‌ترین نسخه را برای پکیج خود انتخاب کنید. اگرچه ممکن است ترجیح دهید که از دستور npm version major/minor/path استفاده کنید که به‌طور خودکار نسخه را در فایل package.json با توجه به تغییرات کد، افزایش می‌دهد. علاوه‌براینها اگر از git استفاده می‌کنید می‌توانید با دستور npm version patch -m "Upgrade to %s for reasons" کامیت مناسبی با توجه به تغییرات ایجاد کنید که قابلیت شخصی‌سازی پیام را نیز دارد.

حتی اگر npm نیاز نداشته باشد که از سیستم‌های کنترل نسخه‌ای مانند git یا push کردن کدها به GitHub یا BitBucket استفاده کنید، توصیه می‌شود که این کارها را خودتان انجام دهید. این موارد به شما در توسعه بیشتر پکیج توسط سایر توسعه‌دهندگان کمک می‌کند. حتی اگر پروژه شما فقط توسط خودتان توسعه یافته و نگهداری می‌شود، نگه داشتن سورس کدتان در یک مخزن مطمئن و remote، به شما احساس امنیت و سهولت دسترسی را می‌دهد.

منبع: https://tsh.io/blog/step-by-step-guide-creating-npm-package-for-node-js-and-frontend-developers