MDX

ماركداون (Markdown) هي لغة ترميز خفيفة الوزن تستخدم لتنسيق النصوص. تتيح لك الكتابة باستخدام صيغة نص عادي وتحويله إلى HTML صالح هيكليًا. تُستخدم عادةً لكتابة المحتوى على المواقع والمدونات.

تكتب...

أنا **أحب** استخدام [Next.js](https://nextjs.org/)

المخرجات:

<p>أنا <strong>أحب</strong> استخدام <a href="https://nextjs.org/">Next.js</a></p>

MDX هي مجموعة شاملة من ماركداون تتيح لك كتابة JSX مباشرة في ملفات ماركداون الخاصة بك. إنها طريقة قوية لإضافة تفاعل ديناميكي وتضمين مكونات React داخل محتواك.

يدعم Next.js كلًا من محتوى MDX المحلي داخل تطبيقك، وكذلك ملفات MDX البعيدة التي يتم جلبها ديناميكيًا من الخادم. يقوم ملحق Next.js بتحويل ماركداون ومكونات React إلى HTML، بما في ذلك الدعم للاستخدام في مكونات الخادم (الافتراضي في App Router).

@next/mdx

يُستخدم حزمة @next/mdx لتكوين Next.js حتى تتمكن من معالجة ماركداون وMDX. إنها تستورد البيانات من الملفات المحلية، مما يسمح لك بإنشاء صفحات بامتداد .mdx، مباشرة في مجلد /pages أو /app.

دعنا نتعرف على كيفية تكوين واستخدام MDX مع Next.js.

البدء

قم بتثبيت الحزم المطلوبة لعرض MDX:

Terminal
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx

قم بتحديث ملف next.config.js في جذر مشروعك لتكوينه لاستخدام MDX:

next.config.js
const withMDX = require('@next/mdx')()

/** @type {import('next').NextConfig} */
const nextConfig = {
  // قم بتكوين `pageExtensions` لتشمل ملفات MDX
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // اختياريًا، أضف أي تكوين آخر لـ Next.js أدناه
}

module.exports = withMDX(nextConfig)

بعد ذلك، قم بإنشاء صفحة MDX جديدة داخل مجلد /pages:

  your-project
  ├── pages
  │   └── my-mdx-page.mdx
  └── package.json

الآن يمكنك استخدام ماركداون واستيراد مكونات React مباشرة داخل صفحة MDX الخاصة بك:

import { MyComponent } from 'my-components'

# مرحبًا بكم في صفحة MDX الخاصة بي!

هذا بعض النصوص **غامق** و _مائل_.

هذه قائمة في ماركداون:

- واحد
- اثنان
- ثلاثة

تحقق من مكون React الخاص بي:

<MyComponent />

التصفح إلى مسار /my-mdx-page يجب أن يعرض MDX المقدم الخاص بك.

MDX البعيدة

إذا كانت ملفات ماركداون أو MDX أو المحتوى موجودة في مكان آخر، يمكنك جلبها ديناميكيًا من الخادم. هذا مفيد للمحتوى المخزن في مجلد محلي منفصل، نظام إدارة المحتوى (CMS)، قاعدة بيانات، أو أي مكان آخر.

هناك حزمتين شائعتين من المجتمع لجلب محتوى MDX:

معلومة مفيدة: يرجى المتابعة بحذر. يترجم MDX إلى JavaScript ويتم تنفيذه على الخادم. يجب عليك فقط جلب محتوى MDX من مصدر موثوق، وإلا يمكن أن يؤدي ذلك إلى تنفيذ كود بعيد (RCE).

المثال التالي يستخدم next-mdx-remote:

import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'

interface Props {
  mdxSource: MDXRemoteSerializeResult
}

export default function RemoteMdxPage({ mdxSource }: Props) {
  return <MDXRemote {...mdxSource} />
}

export async function getStaticProps() {
  // نص MDX - يمكن أن يكون من ملف محلي، قاعدة بيانات، CMS، جلب، أي مكان...
  const res = await fetch('https:...')
  const mdxText = await res.text()
  const mdxSource = await serialize(mdxText)
  return { props: { source: mdxSource } }
}
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

export default function RemoteMdxPage({ mdxSource }) {
  return <MDXRemote {...mdxSource} />
}

export async function getStaticProps() {
  // نص MDX - يمكن أن يكون من ملف محلي، قاعدة بيانات، CMS، جلب، أي مكان...
  const res = await fetch('https:...')
  const mdxText = await res.text()
  const mdxSource = await serialize(mdxText)
  return { props: { source: mdxSource } }
}

التصفح إلى مسار /my-mdx-page-remote يجب أن يعرض MDX المقدم الخاص بك.

التخطيطات

لمشاركة تخطيط حول صفحات MDX، قم بإنشاء مكون تخطيط:

export default function MdxLayout({ children }: { children: React.ReactNode }) {
  // يمكنك إنشاء أي تخطيط أو أنماط مشتركة هنا
  return <div style={{ color: 'blue' }}>{children}</div>
}
export default function MdxLayout({ children }) {
  // يمكنك إنشاء أي تخطيط أو أنماط مشتركة هنا
  return <div style={{ color: 'blue' }}>{children}</div>
}

ثم، قم باستيراد مكون التخطيط إلى صفحة MDX، وقم بتغليف محتوى MDX في التخطيط، وقم بتصديره:

import MdxLayout from '../components/mdx-layout'

# مرحبًا بكم في صفحة MDX الخاصة بي!

export default function MDXPage({ children }) {
  return <MdxLayout>{children}</MdxLayout>;

}

ملحقات Remark و Rehype

يمكنك اختياريًا توفير ملحقات remark و rehype لتحويل محتوى MDX.

على سبيل المثال، يمكنك استخدام remark-gfm لدعم ماركداون بنكهة جيت هاب (GitHub Flavored Markdown).

نظرًا لأن نظام remark و rehype يعمل فقط مع ESM، ستحتاج إلى استخدام next.config.mjs كملف تكوين.

next.config.mjs
import remarkGfm from 'remark-gfm'
import createMDX from '@next/mdx'

/** @type {import('next').NextConfig} */
const nextConfig = {
  // قم بتكوين `pageExtensions` لتشمل ملفات MDX
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
  // اختياريًا، أضف أي تكوين آخر لـ Next.js أدناه
}

const withMDX = createMDX({
  // أضف ملحقات ماركداون هنا، كما تريد
  options: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [],
  },
})

// دمج تكوين MDX مع تكوين Next.js
export default withMDX(nextConfig)

Frontmatter

Frontmatter هو زوج مفتاح/قيمة يشبه YAML يمكن استخدامه لتخزين البيانات حول الصفحة. لا يدعم @next/mdx Frontmatter افتراضيًا، على الرغم من وجود العديد من الحلول لإضافته إلى محتوى MDX الخاص بك، مثل:

للوصول إلى بيانات وصفية للصفحة مع @next/mdx، يمكنك تصدير كائن meta من داخل ملف .mdx:

export const meta = {
  author: 'John Doe',
}

# صفحة MDX الخاصة بي

العناصر المخصصة

أحد الجوانب الممتعة لاستخدام ماركداون، هو أنها تعين إلى عناصر HTML الأصلية، مما يجعل الكتابة سريعة وبديهية:

هذه قائمة في ماركداون:

- واحد
- اثنان
- ثلاثة

يولد ما سبق HTML التالية:

<p>هذه قائمة في ماركداون:</p>

<ul>
  <li>واحد</li>
  <li>اثنان</li>
  <li>ثلاثة</li>
</ul>

عندما تريد تخصيص العناصر الخاصة بك لمظهر مخصص لموقعك أو تطبيقك، يمكنك تمرير رموز قصيرة. هذه هي المكونات المخصصة الخاصة بك التي تعين إلى عناصر HTML.

للقيام بذلك، قم بإنشاء ملف mdx-components.tsx في جذر تطبيقك (المجلد الأصلي لمجلد pages/ أو src/) وأضف عناصر مخصصة:

import type { MDXComponents } from 'mdx/types'
import Image from 'next/image'

// يسمح لك هذا الملف بتوفير مكونات React مخصصة
// لاستخدامها في ملفات MDX. يمكنك استيراد واستخدام أي
// مكون React تريده، بما في ذلك الأنماط المضمنة،
// مكونات من مكتبات أخرى، والمزيد.

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    // يسمح بتخصيص المكونات المدمجة، على سبيل المثال لإضافة أنماط.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...props}
      />
    ),
    ...components,
  }
}
import Image from 'next/image'

// يسمح لك هذا الملف بتوفير مكونات React مخصصة
// لاستخدامها في ملفات MDX. يمكنك استيراد واستخدام أي
// مكون React تريده، بما في ذلك الأنماط المضمنة،
// مكونات من مكتبات أخرى، والمزيد.

export function useMDXComponents(components) {
  return {
    // يسمح بتخصيص المكونات المدمجة، على سبيل المثال لإضافة أنماط.
    h1: ({ children }) => <h1 style={{ fontSize: '100px' }}>{children}</h1>,
    img: (props) => (
      <Image
        sizes="100vw"
        style={{ width: '100%', height: 'auto' }}
        {...props}
      />
    ),
    ...components,
  }
}

تعمق: كيف يتم تحويل ماركداون إلى HTML؟

لا يفهم React ماركداون بشكل أصلي. يجب تحويل نص ماركداون العادي أولاً إلى HTML. يمكن تحقيق ذلك باستخدام remark و rehype.

remark هو نظام من الأدوات حول ماركداون. rehype هو نفسه، ولكن لـ HTML. على سبيل المثال، يقوم مقتطف الكود التالي بتحويل ماركداون إلى HTML:

import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'

main()

async function main() {
  const file = await unified()
    .use(remarkParse) // تحويل إلى شجرة ماركداون المجردة (AST)
    .use(remarkRehype) // تحويل إلى شجرة HTML المجردة (AST)
    .use(rehypeSanitize) // تعقيم إدخال HTML
    .use(rehypeStringify) // تحويل AST إلى HTML متسلسل
    .process('مرحبًا، Next.js!')

  console.log(String(file)) // <p>مرحبًا، Next.js!</p>
}

يحتوي نظام remark و rehype على ملحقات لـ تلوين الصيغة (syntax highlighting)، ربط العناوين، إنشاء جدول محتويات، والمزيد.

عند استخدام @next/mdx كما هو موضح أعلاه، لا تحتاج إلى استخدام remark أو rehype مباشرة، حيث يتم التعامل معها لك. نحن نصفها هنا لفهم أعمق لما تقوم به حزمة @next/mdx تحتها.

استخدام مترجم MDX المبني على Rust (تجريبي)

يدعم Next.js مترجم MDX جديد مكتوب بلغة Rust. هذا المترجم لا يزال تجريبيًا ولا يُنصح باستخدامه في الإنتاج. لاستخدام المترجم الجديد، تحتاج إلى تكوين next.config.js عند تمريره إلى withMDX:

next.config.js
module.exports = withMDX({
  experimental: {
    mdxRs: true,
  },
})

روابط مفيدة