Nub؛ نصب پکیج با سیملینک بهجای کپی
خلاصهٔ کاملتر
تو این مقاله اومده که همهٔ مدیرهای پکیج مدرن — از Cargo و uv تا npm و pnpm و Bun — به یه الگوی مشترک رسیدن: یه استور سراسری روی هر ماشین که هر نسخه از هر پکیج فقط یه بار دانلود میشه. تفاوت اصلی سر اینه که فایلها از اون کش چطور به node_modules پروژه میرسن، یعنی همون دایرکتوریای که Node واقعاً کد رو ازش لود میکنه.
npm همهچیز رو فیزیکی کپی و hoist میکنه و نتیجهش یه چیدمان ناسالمه: کد تو میتونه هر پکیجی رو که بالای درخت افتاده import کنه، حتی اگه هیچوقت اعلامش نکرده باشی. pnpm این رو با یه virtual store داخل پروژه (node_modules/.pnpm) درست میکنه، ولی اون استور هم هنوز فایلبهفایل با هاردلینک ساخته میشه — برای یه اپ معمولی دهها هزار لینک.
سؤال طبیعی اینه که وقتی فایلها همین الان تو استور ماشین هستن، چرا برای هر پروژه دوباره هاردلینکشون کنیم؟ ایدهٔ آرمانی یه سیملینک به ازای هر پکیجه. Bun دقیقاً همینو بهعنوان پیشفرض عرضه کرد و سه هفته بعد اختیاریش کرد، و pnpm هم پشت فلگ enableGlobalVirtualStore نگهش داشته، چون تو پروژههای واقعی Angular و Nuxt خطای resolve میده.
مقصر، مشکل phantom dependency تو Node ئه: پکیجهای پرمصرفی مثل es-abstract یا @firebase/database چیزی رو import میکنن که تو package.json خودشون اعلام نکردن. تو چیدمان تخت npm این تصادفی جواب میده و pnpm هم با یه درخت fallback مخفی نگهش داشته، ولی استور سراسری یه دنیای مهرومومشدهست: Node مسیر واقعی سیملینک رو resolve میکنه، پس node_modules پروژه اصلاً سر راه جستوجو نیست.
راهحل Nub اینه که کد هر پکیج رو واقعاً بخونه. Nub پارسر Oxc رو داخل خودش داره، پس همون موقع که tarball دانلود میشه گراف ماژول رو از نقطههای ورود میگرده و هر import ثابت رو با dependency های اعلامشده میسنجه. چون نسخههای منتشرشده تغییرناپذیرن، هر نسخه فقط یه بار اسکن میشه و حکمش به شکل یه فایل کوچک کنار پکیج تو استور میمونه.
پکیجی که پرچم خورده «eject» میشه: بهجای سیملینک، به شکل فایل واقعی داخل پروژه هاردلینک میشه تا مسیر resolve دوباره از پروژه رد شه. همهٔ والدهاش هم باهاش میان تا دو نسخهٔ جدا از یه ماژول ساخته نشه — که در عمل فقط ۰.۳ تا ۲.۱ درصد درخته. هدف phantom هم مستقیم داخل node_modules همون پکیج لینک میشه تا اول از همه پیدا شه.
نویسنده میگه همهٔ فریمورکهای اصلی با چیدمان پیشفرض تست شدن. Vite تا نسخهٔ ۸.۱ فایل بیرون پروژه رو نمیخونه، پس Nub اون پچ رو موقع لینک تا Vite 5 عقب میبره. Next.js و React Native دو استثنان: Turbopack و Metro مسیرها رو به ریشهٔ پروژه محدود میکنن و راه فرار ندارن، پس Nub برای اون پروژهها به روش هاردلینک برمیگرده. تو CI هم استور سراسری خودکار خاموش میشه.
نکات کلیدی:
- استور سراسری تو همهٔ مدیرهای پکیج مدرن هست؛ فرقشون تو نحوهٔ رسوندن فایلها به node_modules ئه
- سیملینک به استور، هاردلینک فایلبهفایل رو حذف میکنه ولی import های اعلامنشده رو میشکنه
- Nub با Oxc کد هر نسخه رو یه بار اسکن میکنه و پکیجهای مشکلدار رو داخل پروژه هاردلینک میکنه
- Next.js و React Native فعلاً به روش کلاسیک هاردلینک برمیگردن
- نصب گرم ۱۱۶۸ پکیج: ۳۴۶ میلیثانیه برای Nub در برابر ۱۸۹۶ برای bun و ۱۲۹۴۵ برای npm ci




