الفكرة
الـ namespace هو اسم مختصر بيبدأ بـ @ بيشاور على registry. وبيه تقدر تسطّب في مشروع واحد من registries كتير: public وthird-party وprivate بتاعة الشركة.
terminal
npx shadcn@latest add @shadcn/button # الـ registry الرسميnpx shadcn@latest add @nile/money-input # registry بتاعكnpx shadcn@latest add @nile/header @lib/auth-utils @ai/chatbot-rules # كذا item مرة واحدةمفيش جهة مركزية
النظام decentralized بالقصد:
- فيه Registry Directory مركزي للـ namespaces الـ open source، بس مش إجباري.
- تقدر تسمّي namespace بأي اسم، ومفيش حد هيمنعك.
- مفيش تعارض أسماء: الاسم معرّف في
components.jsonبتاعك إنت بس.
وده بيخليك تنظّم بأي شكل:
components.json
{ "registries": { "@nile-ui": "https://registry.nile.dev/ui/{name}.json", "@nile-docs": "https://registry.nile.dev/docs/{name}.json", "@nile-ai": "https://registry.nile.dev/ai/{name}.json", "@stable": "https://registry.nile.dev/stable/{name}.json", "@experimental": "https://registry.nile.dev/experimental/{name}.json", "@nile-internal": { "url": "https://internal.nile.dev/registry/{name}.json", "headers": { "Authorization": "Bearer ${INTERNAL_TOKEN}" } } }}- حسب النوع: ui وdocs وai وthemes.
- حسب الفريق: design وengineering وmarketing، كل فريق يمسك registry.
- حسب الـ visibility: public وprivate.
- حسب الثبات: stable وbeta وexperimental.
قواعد الاسم
- أوله
@دايمًا. - الحروف المسموحة: حروف وأرقام و
-و_بس. - أمثلة صح:
@v0و@nile-uiو@my_company. - شكل الاستخدام:
@namespace/item-name.
والـ parser بيستخدم regex بالشكل ده:
namespace regex
/^(@[a-zA-Z0-9](?:[a-zA-Z0-9-_]*[a-zA-Z0-9])?)\/(.+)$/يعني كمان الاسم لازم يبدأ وينتهي بحرف أو رقم، مش بـ - أو _.
GitHub ولا namespace؟
عنوان GitHub
nile-team/ui/button. مناسب لـ repo public، والمستخدم مش محتاج يعرّف حاجة في components.json.
namespace
@nile/button. مناسب لما تحتاج اسم ثابت، أو hosting خاص، أو auth، أو headers، أو query params، أو registry private.
التعريف في components.json
الشكل البسيط: string
components.json
{ "registries": { "@v0": "https://v0.dev/chat/b/{name}", "@nile": "https://registry.nile.dev/r/{name}.json", "@lib": "https://lib.nile.dev/utilities/{name}" }}الشكل المتقدم: object
components.json
{ "registries": { "@private": { "url": "https://api.nile.dev/registry/{name}.json", "headers": { "Authorization": "Bearer ${REGISTRY_TOKEN}", "X-API-Key": "${API_KEY}" }, "params": { "version": "latest", "format": "json" } } }}${VAR}بيتقري منprocess.env، وبيشتغل في الـurlوالـheadersوالـparams.paramsبتتحط كـ query string، يعني?version=latest&format=json.
الـ placeholders في الـ URL
{name}(إجباري): بيتبدل باسم الـ item، فـ@nile/money-inputيبقى.../money-input.json.{style}(اختياري): بيتبدل بالـ style بتاع المشروع. مثلًاhttps://registry.nile.dev/{style}/{name}.jsonمع stylenew-yorkيبقى.../new-york/card.json. وده مفيد لو عندك نسخة مختلفة من الـ component لكل style.
الـ CLI بيحل @ns/item إزاي؟
- Parse: بيفصل الـ namespace عن اسم الـ item.
- Lookup: بيدوّر على الـ namespace في
components.json. - Build URL: بيبدّل
{name}و{style}و${VAR}. - Headers: بيحط الـ auth headers لو موجودة.
- Fetch: بيجيب الـ JSON.
- Validate: بيتأكد إنه ماشي على registry-item schema.
- Dependencies: بيكرر نفس الخطوات لكل
registryDependenciesبشكل recursive.
ترتيب الـ dependencies — وده مهم جدًا
لما تكتب npx shadcn@latest add @nile/x، الـ CLI بيعمل:
- بيمسح الـ context ويبدأ على نضيف.
- بيجيب الـ item الرئيسي.
- بيجيب الـ dependencies من الـ registry بتاعة كل واحدة، بشكل recursive.
- بيرتّبهم topological sort، فالـ dependency تتسطب قبل اللي محتاجها.
- بيشيل الملفات المكررة حسب الـ target، والأخير بيكسب.
- بيعمل deep merge للـ
tailwindوcssVarsوcssوenvVars.
وفيه 3 حاجات بيتعامل معاها لوحده:
- كل item عارف هو جاي منين، فمفيش لخبطة لو اسمين متشابهين في registries مختلفة.
- الـ circular dependencies بيكتشفها ويمنعها.
- كل registry ليها auth context لوحدها، فالـ token بتاع registry مش بيروح لـ registry تانية.
terminal
npx shadcn@latest add @vendor/auth @nile/login-form# لو الاتنين فيهم login-form.tsx بنفس الـ target، بتاع @nile هو اللي هيفضلتعدّل item من registry تانية
غيّر قيم بس
nile-button.json
{ "name": "nile-button", "type": "registry:ui", "registryDependencies": ["@vendor/button"], "cssVars": { "light": { "button-bg": "oklch(0.55 0.17 160)" } }}الـ button الأصلي بيتسطب، وبعدين الـ cssVars بتاعتك بتتعمل merge فوق قيمته.
زوّد من غير ما تبدّل
nile-table.json
{ "name": "nile-table", "registryDependencies": ["@vendor/table"], "files": [ { "path": "components/ui/table-extended.tsx", "content": "import { Table } from \"@/components/ui/table\"\n// زيادات بتاعتنا\n" } ]}بدّل ملف واحد بس من item كبير
nile-auth.json
{ "name": "nile-auth", "registryDependencies": ["@vendor/auth"], "files": [ { "path": "lib/auth-server.ts", "type": "registry:lib", "content": "// auth server بتاعنا" } ]}مثال على الترتيب
dashboard.json
{ "name": "dashboard", "registryDependencies": ["@shadcn/card", "@vendor/chart", "@nile/card"]}@shadcn/cardبيتكتب فيcomponents/ui/card.tsx.@vendor/chartبيتكتب فيcomponents/ui/chart.tsx.@nile/cardبيكتب فوقcard.tsx، لو الـ target هو نفسه.
Versioning بالـ params
مفيش versioning جاهز، لكن تقدر تعمله بالـ query params والسيرفر بتاعك يفهمها:
components.json
{ "registries": { "@pinned": { "url": "https://registry.nile.dev/{name}", "params": { "version": "v2" } }, "@env": { "url": "https://registry.nile.dev/{name}", "params": { "version": "${REGISTRY_VERSION}" } }, "@range": { "url": "https://registry.nile.dev/{name}", "params": { "semver": "^2.0.0", "prerelease": "${ALLOW_PRERELEASE}" } } }}@pinned/buttonبيروح لـhttps://registry.nile.dev/button?version=v2.${REGISTRY_VERSION}: تحطv1.2.3في production وقيمة تانية في staging أو dev.- default value: استخدم
${VAR:-default}. - وثّق طريقة الـ versions، وادعم الـ pinning عشان الـ builds تبقى reproducible.
- اعمل endpoint يعرض الـ versions المتاحة، زي
/versions/{name}. - حط cache headers مناسبة للنسخ الـ versioned.
الأوامر
terminal
# add: من namespace أو URL أو ملف localnpx shadcn@latest add @nile/button @lib/utilsnpx shadcn@latest add https://registry.nile.dev/button.jsonnpx shadcn@latest add ./local-registry/button.json# view: تشوف الـ item قبل ما تسطّبnpx shadcn@latest view @nile/button @shadcn/card# search و list (list اسم تاني لـ search)npx shadcn@latest search @nile --query "auth"npx shadcn@latest search @v0 @nile @libnpx shadcn@latest search @v0 --limit 10 --offset 20npx shadcn@latest list @nileviewبيعرض: الاسم والنوع والوصف، والـ dependencies والـ registry dependencies، ومحتوى الملفات، والـ CSS vars والـ Tailwind config، والـ env vars المطلوبة.searchبيعرض: الاسم والنوع والوصف والـ registry اللي جاي منها.search -lو-o: الـ default بتاع limit هو 100 لكل registry، والـ offset بيبدأ من 0.
flags مهمة لـ add
--dry-run: يعرض اللي هيحصل من غير ما يكتب.--diff [path]: يعرض الفرق لملف.--view [path]: يعرض محتوى ملف.-o, --overwrite: يكتب فوق الملفات الموجودة.-y, --yes: من غير سؤال تأكيد.-p, --path: يحط الـ component في مسار معين.-a, --all: يسطّب كل الـ items.-s, --silent: من غير output.-c, --cwd: يشتغل في فولدر تاني.
الـ errors ومعناها
Unknown registry "@x": الـ namespace مش متعرّف فيcomponents.json، والرسالة بتوريك الشكل اللي تضيفه.Registry "@private" requires the following environment variables: متغير زيREGISTRY_TOKENناقص، فحطه في.envأو.env.local.- 404
was not found: الاسم غلط، أو الـ item مش موجود، أو شكل الـ URL غلط. - 401
not authorized: الـ token غلط أو مش موجود. - 403
Access forbidden: الـ token شغال بس معندوش صلاحية للـ item ده.
لو هتعمل registry يشتغل بالـ namespaces
- رجّع JSON ماشي على registry-item schema.
- ادعم
{name}في شكل الـ URL. - استخدم types مناسبة زي
registry:uiوregistry:libوregistry:theme. - اقبل الـ auth في الـ headers أو الـ query params لو محتاج.
- وثّق للمستخدم السطر اللي يحطه في
components.json.
Best practices
- حط الأسرار في env variables، مش في
components.json. - اختار اسم unique وواضح للـ namespace.
- وثّق متطلبات الـ auth.
- رجّع errors مفهومة.
- اعمل cache للـ responses.
- ادعم أكتر من style لو الـ components بتاعتك ليها themes.
Troubleshooting
- الـ item مش لاقيه: اتأكد من الـ URL ومن وجود
{name}فيه، وإن الـ item موجود، وإن الـ type مناسب. - مشكلة auth: اتأكد إن الـ env variables موجودة، والـ token مش expired، والـ headers مكتوبة صح.
- تعارض dependencies: اكتب الأسماء كاملة
@namespace/item، ودوّر على circular dependencies، وتأكد إن الأنواع متوافقة لما تخلط registries.
خلّصت الفصل لما
🧠 اختبر نفسك
١) اسم @nile- ينفع؟
لأ. لازم يبدأ وينتهي بحرف أو رقم.
٢) @shadcn/card و@nile/card الاتنين بيكتبوا card.tsx. مين بيفضل؟
اللي اتحل في الآخر، وفي registryDependencies ده بيتحدد بالترتيب.
٣) عايز تغيّر لون button من vendor من غير ما تلمس كوده. تعمل إيه؟
item بتاعك فيه registryDependencies: @vendor/button وcssVars بالقيمة الجديدة.
٤) الـ CLI رجّع 403 مش 401. الفرق إيه؟
401 يعني الـ token غلط أو مش موجود. 403 يعني الـ token شغال بس معندوش صلاحية للـ item.