🌐 Detecting your location…

كيفية إصلاح خطأ MODULE_NOT_FOUND بعد نشر Node.js في الإنتاج

⏱️3 min read  ·  481 words

Error: Cannot find module './Utils/logger'يعمل بشكل مثالي على جهازك ويفشل لحظة نشره. هذه الفئة من الأخطاء لها عدد قليل من الأسباب، وكلها تقريبًا ترجع إلى اختلاف بين بيئة التطوير لديك والخادم.

السبب 1: حساسية الحالة (الأكثر شيوعًا)

يستخدم نظاما التشغيل macOS وWindows أنظمة ملفات غير حساسة لحالة الأحرف بشكل افتراضي. لينكس لا. إذنrequire('./Utils/logger') يحل ملف اسمهutils/logger.js على الكمبيوتر المحمول الخاص بك وفشل على الخادم.

// File on disk: src/utils/logger.js

const logger = require('./Utils/logger');   // works on macOS, fails on Linux
const logger = require('./utils/logger');   // ✅ correct everywhere

ابحث عن هذه العناصر قبل النشر عن طريق التحقق مما سجله Git بالفعل، وهو ما يعتبر موثوقًا بغض النظر عن نظام الملفات المحلي لديك.

# List tracked paths and eyeball the casing
git ls-files | grep -i utils

# Catch a rename that Git ignored because only the case changed
git config core.ignorecase false
git status

إذا سجل Git حالة خاطئة، فافرض إعادة التسمية من خلال اسم وسيط.

git mv src/Utils src/utils-tmp
git mv src/utils-tmp src/utils
git commit -m "fix: correct directory casing for case-sensitive filesystems"

الوقاية الموثوقة هي وظيفة CI التي تعمل على Linux. يتم التقاط ذلك عند كل طلب سحب بدلاً من النشر.

السبب 2: الحزمة موجودة في تبعيات التطوير

تتخطى عمليات التثبيت الإنتاجية تبعيات التطوير، لذا فإن أي شيء يتم استيراده بواسطة كود وقت التشغيل يجب أن يكون تبعية عادية.

npm ci --omit=dev        # devDependencies are not installed
{
  "dependencies": {
    "express": "^5.0.0"
  },
  "devDependencies": {
    "dotenv": "^17.0.0"     // ❌ but required at runtime in server.js
  }
}
# Move it
npm uninstall dotenv
npm install dotenv

للعثور على كل حالة مرة واحدة، قم بتثبيت تبعيات الإنتاج في دليل نظيف وابدأ التطبيق.

rm -rf node_modules
npm ci --omit=dev
node dist/server.js

السبب 3: نسخ العقدة النمطية إلى Docker

نسخ بنيت محلياnode_modules في صورة ما، يتم تقسيم الوحدات الأصلية، لأن الثنائيات التي تم تجميعها لنظام التشغيل macOS أو لبنيتك لن يتم تحميلها على النظام الأساسي للحاوية.

# .dockerignore — essential
node_modules
npm-debug.log
.git
dist
.env
# Dockerfile — install inside the image
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]

نسخpackage*.json قبل بقية المصدر، يعد هذا خيارًا متعمدًا للتخزين المؤقت للطبقة: تتم إعادة تثبيت التبعيات فقط عندما يتغير البيان، وليس في كل تحرير للمصدر.

السبب 4: الوحدات الأصلية المصممة للنظام الأساسي الخاطئ

حزم ذات مكونات مجمعة —bcrypt, sharp, canvas، برامج تشغيل قاعدة البيانات – إنتاج ثنائيات خاصة بالنظام الأساسي.

Error: Cannot find module '.../node_modules/bcrypt/lib/binding/napi-v3/bcrypt_lib.node'

أعد بنائها على النظام الأساسي المستهدف، أو قم بتثبيتها داخل الحاوية كما هو مذكور أعلاه.

npm rebuild bcrypt --build-from-source

# Alpine images need build tools for native compilation
RUN apk add --no-cache python3 make g++

حيث يوجد بديل جافا سكريبت خالص —bcryptjs بدلا منbcryptعلى سبيل المثال – يؤدي استخدامه إلى إزالة هذه الفئة بأكملها من مشكلة النشر.

السبب 5: ملحقات الملفات المفقودة في ESM

تتطلب وحدات ES الامتداد في الواردات النسبية. CommonJS لا. الهجرة بينهما تظهر هذا على الفور.

// package.json has "type": "module"
import { logger } from './utils/logger';        // ❌ ERR_MODULE_NOT_FOUND
import { logger } from './utils/logger.js';     // ✅

في ترجمة TypeScript إلى ESM، يجب أن يشير محدد الاستيراد إلىالإخراج الملف فتكتب.js بالرغم من أن المصدر هو.ts.

import { logger } from './utils/logger.js';   // correct — refers to compiled output

السبب 6: لم يتم حل الأسماء المستعارة لمسار TypeScript في وقت التشغيل

الأسماء المستعارة فيtsconfig.json هي راحة وقت الترجمة. لا يقوم المترجم بإعادة كتابتها، لذا فإن JavaScript المنبعثة لا تزال تحتوي على@/utils/logger، والتي لا تستطيع العقدة حلها.

{
  "compilerOptions": {
    "paths": { "@/*": ["./src/*"] }
  }
}
// Compiles fine, fails at runtime:
// Error: Cannot find module '@/utils/logger'
import { logger } from '@/utils/logger';

قم إما بإعادة كتابة المسارات بعد التجميع، أو تسجيل محلل وقت التشغيل.

npm install -D tsc-alias

# package.json
"build": "tsc && tsc-alias"

تعمل الحزم مع esbuild أو tsup أو ما شابه ذلك أيضًا على حل الأسماء المستعارة أثناء الإنشاء، ولهذا السبب نادرًا ما تصل عمليات النشر المجمعة إلى هذا الحد.

السبب 7: لم يتم نشر إخراج البناء

A .gitignore or .dockerignore دخول لـdist صحيح – ولكن بعد ذلك يجب تشغيل الإنشاء على الخادم أو في CI. تأكيد ما تم شحنه بالفعل.

# Inspect the running container
docker exec -it <container> ls -la /app/dist
docker exec -it <container> ls -la /app/node_modules | head

التشخيص بشكل منهجي

# 1. Which exact path is Node looking for?
node dist/server.js
# Read the full error — it prints the resolved path it tried.

# 2. Does that path exist on the server?
ls -la /app/dist/utils/

# 3. Is the package installed?
ls /app/node_modules | grep package-name
npm ls package-name

# 4. Trace resolution in detail
NODE_DEBUG=module node dist/server.js 2>&1 | head -50

# 5. Confirm the Node version matches your local one
node --version

NODE_DEBUG=module يطبع كل فحص لعقدة الدليل، مما يجعل المشكلة واضحة في غضون بضعة أسطر عادةً.

الوقاية

  • قم بتشغيل CI على Linux حتى تفشل مشكلات حساسية حالة الأحرف قبل الدمج
  • اختبار معnpm ci --omit=dev في CI للقبض على التبعيات في غير محلها
  • دائما.dockerignore your node_modules
  • قم بتنفيذ ملف القفل واستخدمnpm ci, أبداnpm install، في البنيات
  • قم بتثبيت الإصدار الرئيسي للعقدة في ملف Dockerfile وفيengines
  • تفضل حزم JavaScript خالصة حيث لا تكون الوحدة الأصلية ضرورية

الأسئلة المتداولة

س: لماذا يعمل محليًا وليس في Docker؟
ج: حساسية مختلفة لحالة نظام الملفات، ونظام أساسي مختلف للثنائيات الأصلية، ومجموعة تبعيات مختلفة إذا قمت بالتثبيت باستخدام تبعيات التطوير محليًا. يتم التخلص من الثلاثة عن طريق التثبيت داخل الصورة.

س: هل يجب أن ألتزم بـNode_modules؟
ج: لا. قم بتنفيذ ملف القفل وتثبيته أثناء الإنشاء. تنقطع التبعيات الملتزم بها عند تغيير النظام الأساسي وتؤدي إلى تضخم المستودع بشكل سيئ.

س: تثبيت npm ci أو npm في الإنتاج؟
A: npm ci. يقوم بتثبيت ما يحدده ملف القفل بالضبط ويفشل إذا كان البيان وملف القفل مختلفين، وهو ما تريده في البناء.

س: كيف يمكنني العثور على حالات عدم تطابق الحالة عبر قاعدة تعليمات برمجية كبيرة؟
ج: البناء على Linux في CI. هذه هي الطريقة الوحيدة الموثوقة – لا يمكن للأدوات المحلية الموجودة على نظام ملفات غير حساس لحالة الأحرف رؤية المشكلة.

س: الوحدة موجودة في وحدات العقدة ولكن لم يتم العثور عليها بعد. لماذا؟
ج: عادةً ما يكون هناك تعارض في التبعية متداخل، أو ارتباط رمزي معطل من إعداد مساحة عمل، أو حزمةexports لا يعرض الحقل المسار الفرعي الذي تقوم باستيراده. تحقق من الحزمةexports الخريطة فيpackage.json.

الخلاصة

الإنتاج فقطMODULE_NOT_FOUNDالأخطاء تأتي من الاختلافات البيئية. التحقق منها بالترتيب:حساسية حالة الأحرف مقابل Linux، وواردات وقت التشغيل الموجودة في devDependeency، ووحدات العقدة المبنية محليًا المنسوخة في الصورة، والوحدات الأصلية التي تم تجميعها لنظام أساسي خاطئ، مفقودة.js الامتدادات ضمن ESM، والأسماء المستعارة لمسار TypeScript التي لم يتم حلها. الإصلاح الهيكلي لجميع هذه البرامج تقريبًا هو نفسه — البناء والتثبيت داخل البيئة المستهدفة، وتشغيل CI على Linux، واستخدامnpm ci مع ملف قفل ملتزم.

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা