بناء مولّد المواقع الثابتة الخاص بي: رحلة 9 سنوات نحو البيت

بعد 9 سنوات من التنقّل بين أُطر العمل — من Jekyll إلى Hugo إلى Nuxt إلى Next.js — بنيتُ أخيرًا ما أحتاجه تمامًا. هذه حكاية مطوّر Kotlin يعرف من JavaScript قدرًا يكفيه بالكاد، بنى مولّد مواقع ثابتة بمساعدة Claude.

سنوات الترحال

قبل تسع سنوات، اتخذتُ قرارًا ظلّ يطاردني قرابة عقدٍ كامل: تركتُ Jekyll.

لا تظنّ بي الظنون — كان Jekyll وفيًّا معي. كان بسيطًا، وكان يعمل، وجعل GitHub Pages النشر أمرًا هيّنًا. لكنني أردت المزيد. أردت السرعة. أردت الأدوات الحديثة. أردت ذلك الشيء اللامع الجديد الذي يتحدث عنه الجميع.

فانتقلت إلى Hugo. ثم Nuxt. ثم Next.js. وفي كل مرة قلت لنفسي إنّ هذا هو الإطار المناسب. وفي كل مرة كنت أصارع الإطار أكثر مما أكتب محتوى. تضخّمت ملفات الإعدادات. طالت أوقات البناء. وتراكمت التبعيات. وفي زحمة الطريق غاب عني ما كنت أحتاجه فعلًا: طريقة بسيطة أنشر بها أفكاري.

الشرارة

ذات يوم، وبينما كنت أتسوّف في عملٍ حقيقي، عثرت على jakewharton.com. موقع Jake Wharton — أسطورة تطوير أندرويد — كان… بسيطًا. بسيطًا بقسوةٍ وجمال. لا حركاتٍ مبهرة، ولا أُطر JavaScript. محتوى فحسب، في إخراجٍ نظيف.

أحببته.

وتعمّقت، فوجدت أن ملف CSS الخاص بتلوين الصياغة يعود في أصله إلى mojombo/tpw — الموقع الشخصي لـ Tom Preston-Werner. أحد مؤسسي GitHub. ومبنيٌّ بـ Jekyll.

لم تفُتني المفارقة: بعد سنوات من القفز من إطارٍ إلى آخر، كانت الإجابة حاضرةً طوال الوقت — البساطة.

لكنني لم أُرِد العودة إلى Jekyll. أردت شيئًا يكون لي. شيئًا أفهمه فهمًا كاملًا. شيئًا أصوغه على مقاس احتياجاتي بعينها.

التحدّي

المسألة أنني مطوّر Kotlin. أندرويد عالمي، وJVM منطقة راحتي. وأما JavaScript؟ أعرف منها قدرًا يكفي بالكاد لأكون خطرًا — وحين أقول «خطر» فأنا أعني خطرًا على نفسي.

لكنني كنت أعرف تمامًا ما أريده:

  • Markdown مع Frontmatter — لأن الكتابة ينبغي أن تشعرَك أنك تكتب فعلًا
  • تلوين الصياغة — بثيم Android Studio الذي أعشقه
  • دعم السلاسل — للشروحات متعددة الأجزاء
  • توليد صور OG — لأن لمعاينات وسائل التواصل شأنها
  • PWA بدعم العمل دون اتصال — ولماذا لا؟
  • تجربة تطوير جميلة — ليست وظيفية فحسب، بل ممتعة

هل أستطيع بناء هذا؟ أنا ومعرفتي المحدودة بـ JavaScript؟

قررت أن أجرّب، وكان في جعبتي سلاحٌ سرّي: Claude.

الرحلة

بدأ الأمر سكربتَ بناءٍ بسيطًا، ثم صار شيئًا أفخر به حقًا. دعني أصحبك عبر محطاته الأساسية.

الأساس

كانت الخطوة الأولى متواضعة: مولّد مواقع ثابتة أساسي يحلّل الـ Markdown ويولّد HTML. لا بهرجة فيه. لكنه كان يعمل.

وخلال التكرارات الأولى صار لدي:

  • نظام بناء وحداتي (Modular)
  • تصغير (Minification) لمخرجات الإنتاج (خفض الحجم بنسبة 14%!)
  • أداة سطر أوامر لإدارة المحتوى مبنية على Ink
  • إصلاحات تجاوب الشاشات المحمولة، ومعها اختبارات مؤتمتة

الهجرة الكبرى

في النشر صارت الأمور مثيرة. بدأت مع GitHub Pages، وانتقلت إلى Netlify، ثم استقر بي المقام أخيرًا على Cloudflare Pages.

علّمني كل انتقالٍ شيئًا. كان Netlify سهلًا لكنه محدود. أما Cloudflare فمنحني أداء الحواف (Edge) والإعدادات التي احتجتها.

ثورة في نظام البناء

كان سكربت البناء يخرج عن السيطرة؛ بلغ 1,704 سطرًا — كان وحشًا. ففعلت ما يفعله أي مطوّر عاقل: قسّمته.

أما الآن فأكبر ملف لا يتجاوز 328 سطرًا، ولكل وحدةٍ مسؤولية واحدة:

  • build/config.js — إعداد Marked مع تلوين الصياغة
  • build/content.js — تحميل المحتوى وتحليل الـ Frontmatter
  • build/series.js — جمع السلاسل وبناء صفحاتها
  • build/feeds.js — خلاصة Atom و sitemap و robots.txt

مغامرة الـ TUI

هنا تحديدًا صارت الأمور ممتعة. لم أُرِد مخرجات طرفية مملة وأنا أطوّر؛ أردت شيئًا جميلًا.

فدخل Ink على الخط — الـ React الخاص بسطر الأوامر. وتحوّل بناء واجهة مستخدم نصية (TUI) لخادم التطوير إلى هوسٍ استولى عليّ.

والنتيجة؟ تجربة تطوير تضم:

  • إعادة تحميل فورية عبر اتصالات WebSocket
  • رموز QR للاختبار على الهاتف
  • نفق Cloudflared للوصول عبر رابطٍ عام
  • لوحة تنقيح تعرض السجلات لحظيًا
  • اختصارات لوحة المفاتيح لكل شيء

لكن الطريق لم يكن مفروشًا بالورود. Ink v6 كسر كل شيء: الحزم المساعدة القديمة كانت تعتمد require()، وهو ما لا يعمل مع الـ top-level await في Ink v6. فاضطررت إلى بناء مكوّنات خاصة بي — مكوّن روابط يستخدم رموز الهروب ANSI OSC 8، و Hook مخصص للأبعاد، وكل ما بينهما.

هل كان ضروريًا؟ على الأرجح لا. هل كان يستحق العناء؟ قطعًا.

رحلة الـ PWA

أردت أن يعمل موقعي دون اتصال. ليس بمعنى «نعرض عليك صفحةً مخزنة» حين تنقطع الشبكة، بل عملٌ دون اتصال كما ينبغي، مع استراتيجيات تخزينٍ مؤقت ذكية.

يستخدم الـ Service Worker:

  • التخزين المؤقت أولًا (Cache-first) للأصول الثابتة (CSS و JS والصور والخطوط)
  • الشبكة أولًا (Network-first) لصفحات HTML، مع بديلٍ عند انقطاع الاتصال
  • Stale-while-revalidate لموارد الـ CDN

وحين يصدر تحديث؟ إشعارٌ يمكن إغلاقه، مع فترة سكونٍ مدتها 24 ساعة، وفق أنماط Google في تطبيقات PWA.

مرحلة الصقل

كان الشوط الأخير صقلًا خالصًا. إصلاحات في إمكانية الوصول (Accessibility). تحسينات في التنضيد. توليد صور OG مع شارات CTA.

بل بنيت مخطط مساهمات GitHub كأداةٍ في التذييل. ثم أزلته. بعض التجارب لا يُكتب لها النجاح، ولا بأس في ذلك.

ما بنيتُه

إليك ما لديّ اليوم:

الميزات الأساسية:

  • محتوى Markdown مع Frontmatter
  • مقالات مدونة مع دعم الوسوم والسلاسل
  • تلوين الصياغة (بثيم Android Studio)
  • مفتاح للتبديل بين الوضعين الداكن والفاتح
  • أحجام صور قابلة للضبط في الـ Markdown
  • توليد صور Open Graph باستخدام Satori
  • خلاصات RSS/Atom

تجربة المطوّر:

  • خادم تطوير بواجهة نصية تفاعلية باستخدام Ink
  • إعادة تحميل فورية عبر WebSocket
  • رموز QR للاختبار على الهاتف
  • نفق Cloudflared للوصول العام
  • لوحة تنقيح للمراقبة
  • أداة سطر أوامر لإدارة المحتوى

الإنتاج:

  • تعمية أسماء أصناف CSS
  • تصغير HTML/CSS/JS
  • Service Worker بتخزينٍ مؤقت ذكي
  • إشعارات تحديث الـ PWA
  • اختبارات شاملة (إمكانية الوصول، التجاوب، اختبارات الوحدة، النشر)
  • نشر على Cloudflare Pages

ما تعلمتُه

1. لست بحاجة إلى إطار عمل

في مدونةٍ شخصية، مولّد المواقع الثابتة الذي تبنيه بنفسك ليس خيارًا ممكنًا فحسب — إنه تحرر كامل. لا صراع مع الإعدادات. لا أخطاء بناءٍ غامضة. كودٌ كتبتَه وتفهمه، لا أكثر.

2. Claude يغيّر قواعد اللعبة

أنا مطوّر Kotlin، وخبرتي في JavaScript محدودة، وكان Claude هنا عونًا لا يقدَّر بثمن. لم تكن قيمته في أن يكتب لي الكود وأنا مغمض العينين، بل في فهم الأنماط، وتنقيح المشكلات، وتعلّم الممارسات الفضلى. كل ميزة خرجت من تعاون بيننا.

3. تجربة المطوّر مهمة

صرفتُ على الـ TUI وقتًا أطول بكثير مما ينبغي. أصلحت وميض مؤشر التحميل، وبنيت مكوّنات Ink خاصة، وأضفت اختصارات لكل شيء. لكنّي كلما شغّلتُ npm run dev ورأيت تلك الواجهة الجميلة في الطرفية، ابتسمت. وهذا يكفي.

4. البساطة صعبة

لم يكن الأصعب إضافة الميزات؛ الأصعب أن أعرف متى أتوقف. كان مخطط مساهمات GitHub رائعًا، لكنه لم يكن يخدم غرض الموقع، وكان حذفه هو القرار الصحيح.

5. الرحلة هي الغاية

هذا المشروع ليس «مكتملًا»، ولن يكون أبدًا — وهذا بيت القصيد. إنه كائنٌ حيّ ينمو معي، أستطيع تشكيله وتحسينه ما حييت.

النهاية (إلى حين)

تسع سنوات. من Jekyll إلى Hugo إلى Nuxt إلى Next.js إلى… هذا. مولّد مواقع ثابتة بنيتُه بنفسي، بمساعدة ذكاءٍ اصطناعي، وبلغةٍ بالكاد أعرفها.

أحيانًا تعيدك أطول الرحلات إلى البيت نفسه. غير أنك لست الشخص نفسه الذي غادر.


بنيتُ هذا الموقع بالحب والإحباط والعزيمة. وإن أحببت أن تعرف كيف يعمل جزءٌ بعينه، فتواصل معي دون تردد.