توسعه فرانت‌اند

NPM Scripts: اتوماسیون بدون ابزار اضافه

lifecycle، آرگومان، محیط و تفاوت سیستم‌عامل‌ها؛ package.json و دو فایل Node کامل برای ساخت گزارش با exit code روشن.

1404/11/17 2 دقیقه مطالعه 2,462 بازدید

در یک پروژه، هر نفر فرمان ساخت را به شکل متفاوت اجرا می‌کند: یکی متغیر محیطی اضافه می‌کند، دیگری پوشه‌ای را پاک می‌کند و نفر سوم مرحله آماده‌سازی را فراموش می‌کند. npm scripts می‌تواند این قرارداد را در package.json ثبت کند تا نام یک کار به مسیر مشخصی وصل شود. با این حال، npm خودش جای تمام ابزارهای ساخت را نمی‌گیرد؛ فرمانی که معرفی می‌کنید همچنان باید فایل، وابستگی و رفتار واقعی داشته باشد.

عنوان «بدون ابزار اضافه» را باید به معنی پرهیز از لایه هماهنگ‌کننده بی‌نیاز خواند، نه اینکه npm به‌تنهایی lint، تبدیل کد یا استقرار را انجام می‌دهد. اگر script نام یک ابزار خارجی را صدا می‌زند، آن ابزار باید نصب و نسخه‌اش مشخص باشد. برای کارهای ساده، فایل Node.js می‌تواند میان سیستم‌عامل‌ها قرارداد روشن‌تری از یک رشته طولانی shell ایجاد کند.

Script یک نام برای فرمان است

مستندات Scripts در npm درباره اجرا، مسیر binaryها و lifecycle توضیح می‌دهد. فرمان‌های معرفی‌شده در scripts با npm run و نام کار اجرا می‌شوند. ابزار نصب‌شده محلی می‌تواند از مسیر فراهم‌شده در همان محیط پیدا شود. این رفتار دلیل مناسبی برای نگهداری وابستگی در پروژه است؛ تکیه بر نصب سراسری ناشناخته می‌تواند نتیجه اعضای تیم را متفاوت کند.

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

نمونه کامل بدون وابستگی خارجی

سه فایل بعدی را به نام package.json، describe.cjs و build.cjs در یک پوشه تازه ذخیره کنید. Node.js سازگار مانند نسخه 24 و npm لازم‌اند؛ بسته خارجی نصب نمی‌شود. نمونه یک گزارش ثابت آموزشی می‌سازد، نه bundler یا خروجی برنامه وب. پوشه dist در همان پروژه ایجاد و report.json در آن نوشته می‌شود. از پوشه مستقل استفاده کنید تا خروجی نمونه با فایل واقعی پروژه شما هم‌نام نباشد.

{"name":"npm-report-demo","version":"1.0.0","private":true,"scripts":{"describe":"node describe.cjs","prebuild":"node describe.cjs","build":"node build.cjs","postbuild":"node describe.cjs --built"}}

با npm run describe پیام شروع را می‌بینید. با npm run build، prebuild ابتدا اجرا می‌شود، سپس build گزارش را می‌نویسد و postbuild نتیجه را توصیف می‌کند. نام‌های pre و post دقیقاً به build مرتبط‌اند. اگر مرحله ساخت شکست بخورد، مسیر موفق بعدی نباید به عنوان تکمیل کار گزارش شود. این چرخه برای کارهای مرتبط مفید است، ولی وابستگی مهم را نباید در hookهای نامشخص پنهان کرد.

const fs = require('node:fs');
const path = require('node:path');
const built = process.argv.includes('--built');
const output = path.join(__dirname, 'dist', 'report.json');
if (built && !fs.existsSync(output)) {
  console.error('Report is missing. Run npm run build first.');
  process.exitCode = 1;
} else {
  console.log(built ? 'Report exists: dist/report.json' : 'Preparing the local educational report.');
}

فایل describe.cjs بر اساس وجود آرگومان --built رفتار متفاوت دارد. در حالت دوم، وجود فایل گزارش را بررسی و مسیر نسبی آن را می‌گوید. اگر فایل نیست، exit code غیرصفر می‌دهد. این رفتار به مصرف‌کننده خودکار اجازه می‌دهد نبود خروجی را تشخیص دهد. چاپ یک جمله موفقیت بدون بررسی شرط مربوط، قرارداد قابل اعتمادی برای اتوماسیون ایجاد نمی‌کند.

const fs = require('node:fs/promises');
const path = require('node:path');
async function build() {
  const directory = path.join(__dirname, 'dist');
  await fs.mkdir(directory, { recursive: true });
  const report = { kind: 'educational-demo', topics: ['scripts', 'lifecycle', 'exit-code'] };
  await fs.writeFile(path.join(directory, 'report.json'), JSON.stringify(report, null, 2) + '\n', 'utf8');
  console.log('Report written: dist/report.json');
}
build().catch(error => { console.error('Report generation failed:', error.message); process.exitCode = 1; });

فایل build.cjs از APIهای داخلی Node برای ساخت پوشه و نوشتن JSON استفاده می‌کند. مقدارهای گزارش ثابت و آموزشی‌اند و آمار عملکرد واقعی نیستند. اگر نوشتن شکست بخورد، پیام مشخص و exit code ناموفق داده می‌شود. نمونه عملیات پاک کردن پوشه یا deploy ندارد. افزودن آن‌ها نیاز به قرارداد جدا درباره مسیر، اختیار و نتیجه دارد و نباید از نام build حدس زده شود.

وابستگی مرحله‌ها را آشکار نگه دارید

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

در اجرای چند کار مستقل به صورت موازی، وضعیت پایان همه فرایندها و رفتار شکست باید مدیریت شود. npm run به‌تنهایی یک scheduler عمومی برای آن‌ها نیست. ابزارهایی مانند concurrently می‌توانند نقش هماهنگ‌کننده داشته باشند، اما وجود آن‌ها با ادعای «هیچ ابزار دیگری لازم نیست» سازگار نیست. اگر استفاده می‌کنید، وابستگی و علت انتخاب را ثبت کنید و نام فرایندهای خروجی را قابل تشخیص نگه دارید.

آرگومان و متغیر محیطی

برای فرستادن آرگومان به script، قرارداد npm و جداکننده مناسب را بخوانید. در نمونه، می‌توان describe را با آرگومان --built اجرا کرد، ولی این کار قبل از ساخت باید شکست معنادار بدهد. آرگومان ورودی نیز داده‌ای بی‌اعتماد است؛ اگر مسیر یا نام فایل می‌گیرد، آن را بدون بررسی به عملیات فایل منتقل نکنید. ساخت فرمان shell از رشته ورودی می‌تواند نتیجه‌ای خارج از انتظار ایجاد کند.

تعریف متغیر محیطی در shellهای مختلف syntax یکسانی ندارد. cross-env یک ابزار ممکن برای بخشی از این تفاوت است، نه ویژگی خود npm. برای انتخاب ساده، فایل Node می‌تواند مقدار پیکربندی را بخواند و اعتبارسنجی کند. اطلاعات محرمانه را در package.json ثبت نکنید یا در log چاپ نکنید. نام متغیر باید واضح باشد و مقدار ناموجود رفتار مشخصی داشته باشد.

قابلیت اجرا در سیستم‌عامل‌های متفاوت

فرمان‌هایی مانند کپی، حذف و تنظیم محیط در Windows و محیط‌های Unix می‌توانند متفاوت باشند. یک script که روی دستگاه نویسنده کار می‌کند، بدون شناخت shell مقصد قابل انتقال نیست. در مثال، ساخت پوشه با fs انجام می‌شود تا وابستگی به فرمان shell کاهش یابد. این انتخاب هم محدودیت دارد و باید مسیرها با قرارداد پروژه سازگار باشند. رشته‌های مسیر را بی‌دلیل با separator ثابت ترکیب نکنید.

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

Lifecycle نصب را با کار معمول مخلوط نکنید

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

prebuild و postbuild برای نمونه کوچک قابل فهم‌اند چون همان فایل‌های visible را صدا می‌زنند. در پروژه بزرگ، تعداد زیادی hook می‌تواند جریان واقعی را پنهان کند. گاهی یک فرمان صریح prepare و یک مسیر build روشن‌تر است. قرارداد انتخاب را در مستندات نگه دارید. راهنمای Git درباره مرز تغییرها کمک می‌کند script و پیکربندی همراه کد مرتبط قابل مرور بمانند.

CI باید همان قرارداد را اجرا کند

اگر توسعه‌دهنده npm run build اجرا می‌کند ولی CI مجموعه فرمان دیگری دارد، دو تعریف از ساخت ایجاد می‌شود. نسخه Node، lockfile و نصب وابستگی باید معلوم باشند. cache وابستگی هم به کلید و قرارداد نسخه نیاز دارد و حذف بی‌دلیل آن مشکل اصلی را همیشه حل نمی‌کند. خروجی ناموفق باید مرحله را متوقف کند و گزارش دلیل آن قابل پیدا کردن باشد.

نام test در scripts فقط مسیر اجرای ابزار را معرفی می‌کند و کیفیت آزمون‌ها را تعیین نمی‌کند. درباره سؤال آزمون و رفتار قابل مشاهده، راهنمای Testing Library مکمل است. برای سرور هم نمونه Express پیش‌نیاز و فرمان مستقل دارد. هر کار باید بتواند با نام و خروجی‌اش دامنه واقعی خود را توضیح دهد.

تحویل اتوماسیون قابل نگهداری

برای هر script، ورودی، خروجی، پیش‌نیاز و اثر جانبی را بنویسید. مشخص کنید کدام کار محلی، کدام طولانی‌مدت و کدام نیازمند اختیار انتشار است. فایل‌های لازم باید همراه مثال باشند؛ نام یک build.js که وجود ندارد راهنمای عملی نیست. پیام‌ها باید مرحله و شکست را روشن کنند. پاک کردن داده، مهاجرت و انتشار را با قرارداد کار معمول build مخلوط نکنید.

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

واکنش به این مقاله

اشتراک‌گذاری این مقاله:
دیدگاه‌ها 0
۰ / ۲۰۰۰

هنوز دیدگاهی ثبت نشده

اولین نفری باشید که نظر می‌دهد!