Skip to content

النشر ​

يوضح هذا المستند كيفية نشر @lh-ui/vue3 إلى مستودع npm العام، وكيفية استضافته على خادم خاص (بطريقتين: رابط tarball مباشر وبيانات registry الوصفية).

يدعم النشر قناتين: يدوية وتلقائية عبر CI؛ إذ تُنفَّذ أوامر changeset محليًا، ثم يُعتمد النموذج بعد أن يُؤكَّد رقم الإصدار ووصف التغييرات بمراجعة بشرية (انظر «سير النشر» أدناه)، وعند الدفع إلى main مع وجود تغييرات في .changeset/**، ينفّذ GitHub Actions تلقائيًا version + publish (انظر «النشر التلقائي عبر GitHub Actions»).

التحضيرات ​

1. حساب npm وملكية scope ​

  • سجّل حسابًا في npm وفعّل المصادقة الثنائية (2FA).

  • تأكد من أن حسابك يملك صلاحية النشر في scope الذي يحمل الاسم @lh-ui: فالـ scope يُمنح لمن يبادر أولًا، ويصبح الحساب الذي ينشر حزمة @lh-ui/* لأول مرة مالكًا لهذا الـ scope.

  • تسجيل الدخول محليًا:

    bash
    npm whoami          # التأكد من الحساب المسجَّل حاليًا
    npm login           # عند عدم تسجيل الدخول، اتبع التعليمات لإكمال تسجيل الدخول + 2FA

2. التأكد من توجيه registry إلى المستودع العام ​

يحتوي ملف .npmrc في جذر المستودع على shamefully-hoist = true فقط، دون تثبيت registry، فيُستخدم افتراضيًا https://registry.npmjs.org/. كما يثبّت publishConfig في package.json المستودع العام:

json
"publishConfig": {
  "access": "public",
  "registry": "https://registry.npmjs.org/"
}

لا تكتب عنوان registry خاصًا في ملف .npmrc في جذر المستودع — فهذا سيوجّه النشر إلى المصدر الخاص أيضًا. وللتبديل المؤقت إلى registry استخدم ملف المستخدم ~/.npmrc خارج المشروع أو وسائط سطر الأوامر.

3. ما الذي يتضمنه النشر ​

تتضمن القائمة البيضاء files نواتج البناء فقط: es/ (ESM + الأنواع + تنسيقات المكوّنات) وlib/ (CJS) وdist/ (UMD + التنسيقات الكاملة). ويُستبعد المصدر والتوثيق والاختبارات والنصوص البرمجية عبر .npmignore. وعند تنفيذ أمر النشر يُشغَّل prepublishOnly تلقائيًا pnpm build أولًا، فلا حاجة للبناء يدويًا.

سير النشر ​

الخطوة 1: كتابة changeset ​

ينبغي أن يقابل كل تغيير ظاهر للمستخدم changeset واحد. وإذا كُتب changeset للتغيير الحالي فانتقل إلى الخطوة 2.

bash
pnpm changeset

واتبع التعليمات التفاعلية لاختيار @lh-ui/vue3 ونوع bump (major / minor / patch) ووصف التغييرات.

ملاحظة: changesets في هذا المستودع تراكمية. تراكمت عشرات changesets غير المستهلكة في .changeset/ الحالي، وعند تنفيذ changeset version في الخطوة 2 تُستهلك جميعها دفعة واحدة وتُدمج في إصدار واحد. وإذا أردت النشر على دفعات، فاستخدم فروع git أو انقل بعض ملفات changeset يدويًا للتحكم في الإيقاع.

الخطوة 2: رفع رقم الإصدار وتوليد CHANGELOG ​

bash
pnpm changeset:version

يقوم هذا الأمر بـ:

  • حساب رقم الإصدار الجديد وفق أعلى مستوى bump في changesets وكتابته إلى package.json
  • تلخيص جميع changesets لتوليد CHANGELOG.md
  • حذف ملفات .changeset/*.md المستهلكة (قيمة commit: false في config.json، فلا تُسلَّم تلقائيًا)

وبعد التنفيذ يجب الفحص يدويًا:

bash
git status              # التأكد من استهلاك changesets وتوليد CHANGELOG
git diff package.json   # التأكد من أن رقم الإصدار كما هو متوقع

الخطوة 3: النشر ​

bash
pnpm changeset:publish

ينفّذ داخليًا pnpm publish، وذلك على النحو التالي:

  1. تشغيل prepublishOnly → pnpm build (بناء كامل: ESM / CJS / UMD / الأنواع / التنسيقات)
  2. تحزيم الحزمة وفق publishConfig ودفعها إلى مستودع npm العام
  3. توليد ودفع وسم git (بالصيغة @lh-ui/vue3@<version>)

ولأن publishConfig.access تساوي public، فإن scoped packages تكون علنية أيضًا دون الحاجة إلى إضافة --access public.

الخطوة 4: الدفع (ينفّذه المطور) ​

bash
git push --follow-tags

الفحص الذاتي قبل النشر ​

قبل النشر الفعلي، استخدم dry-run للاطلاع على محتوى الحزمة:

bash
npm pack --dry-run

وتأكد في المخرجات من:

  • أنها لا تتضمن سوى الأدلة الثلاثة es/ وlib/ وdist/
  • عدم احتوائها على src/ وpackages/ وdocs/ وtests/ وscripts/ وai/
  • أن الحجم معقول (حزمة UMD الكاملة نحو 600KB، والتنسيقات الكاملة نحو 700KB، ضمن النطاق المتوقع)

التحقق بعد النشر ​

bash
# 1. رؤية الإصدار الجديد على npm
npm view @lh-ui/vue3
npm view @lh-ui/vue3 versions

# 2. إنشاء مشروع تجريبي لاختبار صيغ الوحدات الثلاث فعليًا
pnpm create vite verify-pkg --template vue-ts
cd verify-pkg && pnpm add @lh-ui/vue3

وفي المشروع التجريبي تحقّق تباعًا من:

عنصر التحققالصيغة
الاستيراد الكامل (ESM)import LhUiPlus from '@lh-ui/vue3' + import '@lh-ui/vue3/dist/index.css'
استيراد المكوّنات عند الطلبimport { LhButton } from '@lh-ui/vue3'
استيراد التنسيقات عند الطلب (يتطلب sass)import '@lh-ui/vue3/style/button'
استيراد التنسيقات عند الطلب (دون sass)import '@lh-ui/vue3/style-css/button'
أدوات السمةimport { getThemeVars } from '@lh-ui/vue3/theme'
CDN (بعد التحديث خلال دقائق)https://unpkg.com/@lh-ui/vue3

الاستضافة على خادم خاص (tarball ثابت) ​

مستودع npm العام هو قناة النشر الرئيسية؛ وإذا احتجت إلى مرآة مستضافة لديك (لاستخدام داخلي، أو لكون npm محجوبًا، أو لتثبيت إصدار محدد)، فإن وضع tarball على خادمك الخاص هو الحل الأخف، دون الحاجة إلى بناء npm registry خاص.

1. تحزيم tarball ​

bash
pnpm build
npm pack

اسم الملف الناتج هو lh-ui-vue3-<version>.tgz — يحوّل npm الـ scope @lh-ui/ فيه إلى lh-ui-vue3- (تتحول الشرطة المائلة إلى واصلة). ويمكن التأكد مسبقًا باستخدام npm pack --dry-run.

2. الرفع إلى الخادم ​

وفق اصطلاح أدلة موقع nginx القائم (موقع التوثيق منشور في /home/webuser/nginx/lh-ui/vue3/)، أضِف دليل packages/ تحته، وخزّن الملفات في أدلة بحسب الإصدار:

/home/webuser/nginx/lh-ui/vue3/
├── index.html              # موقع التوثيق
└── packages/
    ├── 1.0.0/
    │   └── lh-ui-vue3-1.0.0.tgz
    ├── 1.1.0/
    │   └── lh-ui-vue3-1.1.0.tgz
    └── latest              # رابط رمزي يشير إلى الإصدار الموصى به حاليًا
        └── lh-ui-vue3-1.1.0.tgz

العنوان المقابل:

https://www.lhgb.top/lhui/vue3/packages/1.0.0/lh-ui-vue3-1.0.0.tgz

packages/ دليل ملفات ثابتة، ويعيد استخدام إعدادات nginx للموقع القائم دون الحاجة إلى location جديد:

nginx
# location الخاص بموقع التوثيق القائم يغطي البادئة /lhui/vue3/،
# ويزداد الدليل packages/ قابلية للوصول تلقائيًا كدليل فرعي منه،
# ويكفي التأكد من إمكانية تنزيل ملفات
# .tgz (نوع المحتوى الافتراضي octet-stream في nginx كافٍ)
location /lhui/vue3/ {
    alias /home/webuser/nginx/lh-ui/vue3/;
    autoindex off;
}

3. التثبيت من الخادم الخاص ​

ثلاث صيغ متكافئة، اختر إحداها:

bash
# pnpm
pnpm add https://www.lhgb.top/lhui/vue3/packages/1.0.0/lh-ui-vue3-1.0.0.tgz

# npm
npm install https://www.lhgb.top/lhui/vue3/packages/1.0.0/lh-ui-vue3-1.0.0.tgz

# yarn
yarn add https://www.lhgb.top/lhui/vue3/packages/1.0.0/lh-ui-vue3-1.0.0.tgz

أو اكتبه مباشرة في package.json:

json
{
  "dependencies": {
    "@lh-ui/vue3": "https://www.lhgb.top/lhui/vue3/packages/1.0.0/lh-ui-vue3-1.0.0.tgz"
  }
}

4. ملاحظات ​

  • تجاوز تحليل اعتمادات registry: لا يستعلم تثبيت tarball عن registry، بل تأتي الاعتمادات مباشرة من التعريفات داخل tarball. ولا يحتوي هذا الحزمة على أي dependencies تشغيلية، وإنما فقط peerDependencies.vue، فيكفي أن يكون Vue 3 مثبتًا في المشروع المستهلك لتُثبَّت الحزمة وتعمل.
  • لا توجد بيانات وصفية للإصدارات: لا يعمل npm update مع اعتماديات tarball، ويجب استبدال العنوان يدويًا عند الترقية (أو تغيير الرابط الرمزي latest ثم إعادة التثبيت). وهي مناسبة كـ مرآة لتثبيت إصدار محدد، لا للتحديثات المتكررة.
  • https ضروري: يرفض pnpm / npm / yarn افتراضيًا عناوين tarball من نوع http.
  • في pnpm-lock.yaml يُحفظ العنوان كاملاً مع integrity hash: ما يتيح للمجموعة إعادة إنتاج التثبيت بشكل صحيح من ملف القفل دون الحاجة إلى الوصول إلى npm.

الاستضافة على خادم خاص (بيانات registry) ​

رابط tarball المباشر السابق يعمل، لكن npm update غير مفعّل، ويحوي ملف القفل سلسلة عنوان. وإذا أردت للمشروع المستهلك أن يثبّت pnpm add @lhgb/vue3 كأي حزمة عادية، مع تحليل الإصدارات وdist-tags، فيمكنك جعل الدليل الثابت في Nginx يولّد إضافةً لذلك بيانات npm registry وصفية مزيّفة، فيؤدي دور registry للقراءة فقط. ويمكن للطريقتين التعايش معًا.

1. التحزيم ​

bash
pnpm build
node scripts/pack-private.mjs

ما يقوم به النص البرمجي: إعادة تسمية ناتج البناء نفسه إلى @lhgb/vue3 (يختلف فقط name / publishConfig)، وإنتاج tarball عبر npm pack، وحساب integrity (sha512) وshasum (sha1)، وتجميع JSON لبيانات registry الوصفية وإخراجها إلى private-registry/:

private-registry/
├── @lhgb/vue3              # JSON لبيانات registry الوصفية (بلا امتداد)
└── tarballs/@lhgb/vue3/
    └── lhgb-vue3-1.0.0.tgz

الوسائط الشائعة:

  • --dry-run: طباعة قائمة الملفات والبيانات الوصفية المُولَّدة فقط دون كتابتها على القرص، للمراجعة قبل النشر
  • --incremental: قراءة البيانات الوصفية الموجودة ودمج الإصدار الجديد، لتفادي الكتابة فوق الإصدارات السابقة

2. النشر إلى الخادم ​

ارفع محتويات private-registry/ إلى دليل على الخادم، وطبّق location الوارد في scripts/nginx-private-registry.conf.example (على خادم Nginx نفسه الخاص بموقع التوثيق /lhui/vue3/، لكن ضمن location مستقل):

/home/webuser/nginx/registry/
├── @lhgb/vue3
└── tarballs/@lhgb/vue3/
    └── lhgb-vue3-1.0.0.tgz

والأمر الأساسي في إعدادات Nginx هو default_type application/json — فملفات البيانات الوصفية بلا امتداد، وإن لم يُصرَّح بالنوع سيفشل العميل في تحليلها نصًا خامًا. والإعدادات الكاملة في scripts/nginx-private-registry.conf.example (يجب أن يوضع .tgz في location منفصل بتعبير نمطي في المستوى الأعلى، ولا يجوز تضمينه داخل ^~ /lhgb/، وإلا فإن المطابقة بالبادئة ستتجاوز التعبير النمطي):

nginx
location ^~ /lhgb/ {
  alias /home/webuser/nginx/registry/;
  default_type application/json;
  autoindex off;
  expires 5m;
}

location ~ ^/lhgb/(.+\.tgz)$ {
  alias /home/webuser/nginx/registry/$1;
  default_type application/octet-stream;
  expires 1y;
  add_header Cache-Control "public, immutable" always;
}

3. التثبيت من registry الخاص ​

أضف سطرًا إلى .npmrc على مستوى المشروع أو المستخدم:

ini
@lhgb:registry=https://www.lhgb.top/lhgb

وبذلك تتوحَّد مديرات الحزم الثلاث:

bash
pnpm add @lhgb/vue3
# أو
npm install @lhgb/vue3
# أو
yarn add @lhgb/vue3

4. العلاقة مع الحزمة العامة ​

@lhgb/vue3 و@lh-ui/vue3 من المصدر نفسه وبالإصدار نفسه، ويتطابقان في ناتج البناء تمامًا، ويختلفان فقط في name وregistry، وكلاهما قابل للاستبدال:

  • سيناريوهات الشبكات الداخلية / حجب npm / الحاجة إلى تثبيت مصدر النشر: استخدم @lhgb/vue3
  • سيناريوهات الانفتاح للعامة وإمكانية الوصول عبر unpkg / jsdelivr CDN: استخدم @lh-ui/vue3

5. ملاحظات ​

  • حدِّث البيانات الوصفية بشكل تزايدي: بعد كل نشر استخدم --incremental لدمج الإصدار الجديد، فالكتابة المباشرة فوق البيانات تفقد إصدارات التاريخ، ولن يمكن تثبيت الإصدارات القديمة.
  • لا يجوز نقل tarball: عنوان tarball في البيانات الوصفية مسار مطلق، ويجب أن تقابل الملفات عناوين URL واحدًا لواحد.
  • https ضروري أيضًا: يرفض pnpm / npm / yarn عناوين http افتراضيًا.
  • لا تكتب registry الخاص بـ @lhgb في .npmrc بجذر المستودع: يبقى ملف .npmrc الجذري محصورًا في shamefully-hoist = true، واكتب عنوان الخادم الخاص في ملف المستخدم ~/.npmrc، لتفادي تلويث سلسلة النشر.

النشر التلقائي عبر GitHub Actions ​

إضافةً إلى السير اليدوي، يوفر .github/workflows/release.yml قناة نشر تلقائية:

  • شروط التشغيل: الدفع إلى main مع وجود تغييرات في .changeset/** أو package.json؛ أو التشغيل اليدوي من صفحة Actions في المستودع (workflow_dispatch).
  • السلوك: تشغيل pnpm build + pnpm verify:dts، ثم تنفيذ version + publish من changesets/action؛ وعند عدم وجود changesets بانتظار النشر لا يفعل شيئًا، لذا فهو آمن على push.
  • secrets المطلوبة: في المستودع Settings → Secrets and variables → Actions، اضبط NPM_TOKEN — وهو Granular Access Token يُنشأ في npm مع اختيار صلاحية publish لمؤسسة @lh-ui.

يمكن للقناتين اليدوية والتلقائية التعايش: فالإصدار المحلي pnpm changeset:publish مناسب للحالات التي تتطلب تأكيدًا بشريًا لكل عملية نشر؛ بينما CI مناسب للنشر التراكمي تلقائيًا بعد تراكم changesets.

التراجع ​

bash
# وضع علامة الإهمال على إصدار (موصى به، لا يؤثر على المستخدمين المثبَّتين)
npm deprecate @lh-ui/vue3@1.0.0 "يحتوي هذا الإصدار على مشكلة كذا، يرجى الترقية إلى 1.0.1"

# الحذف نهائيًا: ممكن فقط خلال 72 ساعة من النشر، وبعدم وجود حزم أخرى تعتمد عليه
npm unpublish @lh-ui/vue3@1.0.0

تنتهي صلاحية ذاكرة CDN (unpkg / jsdelivr) خلال ساعات إلى يوم، وبعد التراجع يمكنك التحقق في مشروع تجريبي باستخدام عنوان يحمل رقم الإصدار من تأكيد إمكانية تثبيت الإصدار القديم.

جمال التقاليد، أساس الكود