در Node.js، CommonJS معمولاً از require و module.exports استفاده میکند؛ ESM از import و export. اما انتخاب با جایگزینکردن یک کلمه تمام نمیشود: پسوند فایل، نزدیکترین package.json و نسخهٔ Node تعیینکنندهاند. برای رفع ابهام، قالب ماژول را صریح مشخص کنید.
۱. پسوند و type را کنار هم بخوانید
| فایل یا قرارداد | قالب مشخص |
|---|---|
.mjs |
ESM، مستقل از type نزدیک |
.cjs |
CommonJS، مستقل از type نزدیک |
.js با type: module |
ESM در محدودهٔ همان package |
.js با type: commonjs |
CommonJS در محدودهٔ همان package |
در نسخههای جدید، برای ورودیهای بدون نشانهٔ صریح امکان تشخیص بر اساس syntax هم مطرح است. بنابراین فرض همیشگی «هر .js بدون type حتماً در همهٔ شرایط CommonJS است» برای عیبیابی نسخههای مختلف کافی نیست. قرارداد صریح، نمونه را قابلفهمتر و نگهداری را سادهتر میکند.
۲. نمونهٔ کوچک با مرز روشن
در یک پوشهٔ آزمایشی، فایل hello.cjs این خروجی را صادر میکند:
module.exports = { greeting: 'hello' };
فایل esm-demo.mjs آن را بهصورت default import میخواند:
import greeting from './hello.cjs';
console.log(greeting.greeting);
اجرای node esm-demo.mjs در Node ۲۴.۱۹.۰ خروجی hello داد. این مثال استفادهٔ محدود ESM از خروجی CommonJS را نشان میدهد؛ تضمین نمیکند هر named import از هر بستهٔ CommonJS کار کند.
۳. پیام require is not defined چه میگوید؟
اگر در یک فایل .mjs از require معمول CommonJS استفاده کنید، آن نام بهطور عادی در محدودهٔ ESM تعریف نشده است. در آزمون ما، همین کاربرد در wrong-esm.mjs خطا داد. این نتیجه به معنی حذفشدن یا نصبنشدن فایل مقصد نیست.
- ابتدا پسوند فایلِ خطادار را بخوانید.
- نزدیکترین package.json و type را بررسی کنید.
- شکل export مقصد و شکل import مبدأ را تطبیق دهید.
- به جای تغییر سراسری type، یک نمونهٔ مستقل و کوچک بسازید.
اگر به سازگاری ویژه نیاز دارید، ابزارهایی مانند createRequire قرارداد مشخص دارند؛ استفاده از آنها باید با مستند نسخه و نیاز واقعی برنامه سنجیده شود، نه نسخهپیچی برای همهٔ خطاها.
۴. قواعدی که با نسخه تغییر کردهاند
در Nodeهای جدید، require میتواند بعضی ماژولهای ESM همگام را در شرایط مشخص بارگذاری کند. بنابراین توصیهٔ قدیمی «require برای هر ESM همیشه ERR_REQUIRE_ESM میدهد» را قانون همیشگی ندانید. وجود top-level await، خروجی بسته و محدودیت نسخه همچنان مهم است.
برای import نسبی ESM، پسوند فایل را صریح بنویسید و قواعد resolution همان شاخهٔ Node را بخوانید. رفتار CommonJS در جستوجوی پسوند یا پوشه را خودکار به ESM تعمیم ندهید. این مقاله مهاجرت سراسری پروژه یا دستکاری build ابزارها را اجرا نمیکند.
۵. خطای syntax با خطای پیدا نشدن متفاوت است
| نشانه | سؤال اصلی |
|---|---|
require is not defined |
آیا فایل در ESM اجرا میشود؟ |
Cannot use import statement outside a module |
قالب واقعی ورودی با syntax همخوان است؟ |
ERR_MODULE_NOT_FOUND |
مسیر، پسوند و قرارداد exports درست است؟ |
Named export not found |
مقصد واقعاً همان نام را صادر میکند؟ |
نکته: تغییر type در package.json میتواند چندین فایل .js را همزمان تحت تأثیر بگذارد. قبل از اعمال روی پروژه، فهرست ورودیها، ابزار build و آزمونها را در محیط جدا بررسی کنید.
۶. تصمیم قابل نگهداری
برای پروژهٔ تازه، یک قرارداد روشن انتخاب و همان را در فایل نمونه، اسکریپت اجرا و مستند پروژه ثبت کنید. در پروژهٔ موجود، ناسازگاری را در کوچکترین مرز ممکن تشخیص دهید؛ rename تصادفی فایل و نصب دوبارهٔ همهٔ بستهها دلیل قالب ماژول را روشن نمیکند. راهنمای استقرار موجود مرحلهٔ هاست را پوشش میدهد، اینجا تمرکز روی رفتار کد است.
۷. مسیر مطالعهٔ مرتبط
۸. منابع و روش بررسی
منابع رسمی در ۲۴ اکتبر ۲۰۲۶ بررسی شدند. مثالهای کدِ این متن در Node.js نسخهٔ ۲۴.۱۹.۰ روی محیط محلی ایزوله بررسی شدهاند؛ نصب روی هاست، اتصال دیتابیس یا کارایی تولید با این آزمایش اثبات نشده است.