ماژول در Node.js؛ فایل محلی، ESM و مرز محیط مرورگر
در این درس ۴ بخش
در مرورگر ممکن است چند فایل اسکریپت را کنار هم قرار داده باشید. در برنامهٔ سمت سرور هم تقسیم مسئولیت لازم است، اما نباید به متغیرهایی که تصادفی در صفحه ساخته شدهاند وابسته باشیم. ماژول، ورودی و خروجی هر فایل را مشخص میکند. در این درس، یک محاسبهٔ هزینه را از فایل اجرا جدا میکنیم؛ هدف یادگیری یک قرارداد کوچک و قابل آزمون است، نه حفظکردن دهها شکل واردکردن بسته.
یک پوشهٔ تازه به نام modules-lab را در VS Code باز کنید. مثالهای این فصل مستقلاند؛ فایلهای پوشهٔ پروژهٔ پایانی را با آنها جایگزین نکنید. مسیر اصلی دوره، ESM است و برای روشنبودن انتخاب از پسوند .mjs استفاده میکنیم. این پسوند به Node میگوید فایل، ماژول ECMAScript است. در پروژههایی با فایل .js، فیلد type در نزدیکترین package.json نیز در تعیین نوع ماژول نقش دارد. بهتر است انتخاب صریح باشد و به تشخیص خودکار نحو تکیه نکنیم.
دو فایل با یک مرز روشن
فایل pricing.mjs را بسازید. تابع فقط عدد دریافت میکند و عدد برمیگرداند؛ نه ورودی کنسول میگیرد، نه فایل مینویسد. این مرز بعداً اجازه میدهد همان محاسبه را در یک فرمان محلی یا پشت API استفاده کنیم. مبلغها در این مثال تومان و عدد صحیحاند؛ هزینهٔ ارسال ثابت، فرض همین تمرین است.
// pricing.mjs
export function total(price, shipping) {
if (![price, shipping].every(Number.isSafeInteger) || price < 0 || shipping < 0) {
throw new TypeError('Expected non-negative integer amounts');
}
const result = price + shipping;
if (!Number.isSafeInteger(result)) throw new RangeError('Unsafe total');
return result;
}اکنون فایل app.mjs را دقیقاً کنار آن قرار دهید. پیشوند ./ به فایل محلی اشاره میکند؛ حذف آن نام را شبیه نام یک بسته میکند. در import نسبی ESM، پسوند فایل را هم بنویسید. وجود فایل pricing.mjs بهتنهایی باعث نمیشود import از ./pricing درست باشد.
// app.mjs
import { total } from './pricing.mjs';
import { platform } from 'node:os';
console.log(total(80000, 20000));
console.log(typeof platform());
console.log(typeof document);
// 100000
// string
// undefinednode .\app.mjsپیشوند node: کتابخانهٔ داخلی Node را صریح میکند؛ node:os را با npm نصب نمیکنیم. خروجی platform روی ویندوز win32 است، اما در این مثال نوع خروجی را چاپ کردهایم تا نتیجه در محیطهای مختلف ثابت بماند. document متعلق به DOM مرورگر است و در یک برنامهٔ معمول Node وجود ندارد. typeof روی نام تعریفنشده، رشتهٔ undefined میدهد؛ خود document.querySelector(...) خطای ReferenceError ایجاد میکند. وجود fetch در Node هم به معنی وجود صفحه و دکمه و DOM نیست.
وقتی آموزش قدیمی از require استفاده میکند
CommonJS هم در پروژههای واقعی رایج است. در آن معمولاً require و module.exports را میبینید و پسوند .cjs انتخابی صریح برای این نوع فایل است. لازم نیست فایل سالم پروژهای دیگر را فوراً به ESM تبدیل کنید. ابتدا نوع همان پروژه را از پسوندها و package.json بفهمید. کنار هم گذاشتن require و export در یک فایل تازه، راه حل عمومی سازگاری نیست؛ قواعد تعامل دو سیستم جزئیات بیشتری دارند.
// legacy.cjs — یک مثال مستقل، نه بخشی از app.mjs
const { platform } = require('node:os');
console.log(typeof platform()); // stringاگر پیام require is not defined in ES module scope دیدید، ابتدا نوع فایل و شیوهٔ واردکردن را بررسی کنید. برای ERR_MODULE_NOT_FOUND، املای نام، پسوند، محل فایل و پیشوند ./ را کنترل کنید. در ویندوز ممکن است تفاوت حروف بزرگ و کوچک آشکار نشود، اما همان نام ناهماهنگ روی سرور لینوکسی شکست بخورد. نام فایل را یکسان نگه دارید و خطای واردکردن را با نصب تصادفی یک بسته پاسخ ندهید.
تمرین با پاسخ و معیار تحویل
یک export به نام deliveryLabel به pricing.mjs اضافه کنید که برای ارسال صفر، متن free و برای مبلغ مثبت، متن paid بدهد. ورودی منفی، اعشاری یا متن باید رد شود. سپس هر دو تابع را در app.mjs وارد کنید. پیش از اجرا، خروجی چهار ورودی 0، 20000، -1 و متن 20000 را بنویسید. تغییر فایل محاسبه نباید باعث نیاز به دسترسی به مرورگر شود.
پاسخ راهنما: ابتدا typeof کافی نیست، چون NaN نیز نوع number دارد؛ Number.isSafeInteger و بررسی منفینبودن را ترکیب کنید. سپس با شرط amount === 0 نتیجه را انتخاب کنید. دو حالت نخست free و paid میدهند و دو حالت بعدی باید خطا شوند. معیار تحویل، اجرای فایل از ترمینال، واردکردن با پسوند کامل و توضیح تفاوت فایل محلی، کتابخانهٔ داخلی و بستهٔ npm است. در بازبینی مشخص کنید کدام تصمیم مربوط به زبان جاوااسکریپت و کدام مربوط به محیط Node است.
منابع رسمی و ادامهٔ مطالعه
Node.js، ماژولهای ECMAScript: https://nodejs.org/api/esm.html
Node.js، تشخیص نوع بسته و فایل: https://nodejs.org/api/packages.html
Node.js، ماژولهای CommonJS: https://nodejs.org/api/modules.html
Node.js، تفاوت محیط مرورگر و Node: https://nodejs.org/en/learn/getting-started/differences-between-nodejs-and-the-browser
تألیف و مثالهای اختصاصی: تحریریه درسآوند. مثالهای این درس با Node.js 24.22.1 در محیط لینوکس اجرا شدهاند؛ نصب و پوستهٔ ویندوز در این محیط آزموده نشدهاند.
متن درسهای عمومی را بدون ورود بخوانید.