ماركداون و MDX

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

عند الكتابة...

I **love** using [Next.js](https://nextjs.org/)

المخرجات:

<p>I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a></p>

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

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

@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)، قاعدة بيانات، أو أي مكان آخر. حزمة مجتمعية شائعة لهذا الاستخدام هي next-mdx-remote.

معلومة مفيدة: يرجى المتابعة بحذر. يترجم 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 - يمكن أن يكون من ملف محلي، قاعدة بيانات، نظام إدارة محتوى، جلب، أي مكان...
  const res = await fetch('https:...')
  const mdxText = await res.text()
  const mdxSource = await serialize(mdxText)
  return { props: { 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 - يمكن أن يكون من ملف محلي، قاعدة بيانات، نظام إدارة محتوى، جلب، أي مكان...
  const res = await fetch('https:...')
  const mdxText = await res.text()
  const mdxSource = await serialize(mdxText)
  return { props: { 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.

نظرًا لأن نظام 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، يمكنك تصدير كائن بيانات وصفية من داخل ملف .mdx:

export const metadata = {
  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, { ImageProps } 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 as ImageProps)}
      />
    ),
    ...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('Hello, Next.js!')

  console.log(String(file)) // <p>Hello, 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,
  },
})

روابط مفيدة