# Kiti publish etmədən başqa proyektdə işlətmək

`pnpm kit:local` kiti build edir və **`npm publish`-in göndərəcəyi eyni fayl dəstini** başqa bir proyektin `node_modules/` qovluğuna qoyur. Registry lazım deyil, paket adı, lisenziya sahibi və ya Nexus hesabı hələ qərarlaşmamış ola bilər.

English: [`kit-local.md`](./kit-local.md) · Qısa versiya: [README §8b](../../README.md#8b-using-the-kit-without-publishing-it)

---

## 1. Beş addımda

Fərz edək kit `.../create-reactivite/template5`, tətbiqin isə `.../my-app`.

```bash
# 1) Target proyekt qurulmuş olmalıdır (node_modules mövcud olmalıdır)
cd ../my-app && npm install

# 2) Kitdən sync
cd ../create-reactivite/template5
npm run kit:local -- --to ../../my-app

# 3) Skript nə etməli olduğunu çap edir — onu tətbiq et (aşağıda tam kod var)

# 4) TypeScript proyektidirsə
cd ../../my-app && npm install --save-dev radix-ui

# 5) İşə sal
npm run dev
```

Üç çağırış forması da eynidir — birini seç:

```bash
pnpm kit:local -- --to ../my-app          # pnpm varsa
npm  run kit:local -- --to ../my-app      # npm ilə
node scripts/link-local.mjs --to ../my-app # heç bir paket meneceri lazım deyil
```

> `--` işarəsi vacibdir: onsuz `npm run` / `pnpm` bayraqları skriptə deyil, özünə aid edir.

---

## 2. Target proyektdə nə dəyişir

Skript bunları özü çap edir, amma tam şəkli buradadır.

### 2.1 Stylesheet — bir dəfə, app entry-də

```tsx
// Vite: src/main.tsx   ·   Next: app/layout.tsx
import '@cbar/uikit/styles.css';
```

Bu fayl **öz-özünə yetərlidir** (~112 kB, kompilyasiya olunmuş Tailwind + hər iki token qatı). Tətbiqində Tailwind **olmasına ehtiyac yoxdur**.

Tətbiqində artıq Tailwind v4 varsa, əvəzinə tokenləri götür:

```css
@import 'tailwindcss';
@import '@cbar/uikit/tokens.css';
@import '@cbar/uikit/theme.css';
@source '../node_modules/@cbar/uikit/dist';
```

### 2.2 İki kök provider

```tsx
import { TooltipProvider } from '@cbar/uikit/tooltip';
import { Toaster } from '@cbar/uikit/toast';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <TooltipProvider>
      <App />
      <Toaster position="bottom-right" />
    </TooltipProvider>
  </StrictMode>,
);
```

`TooltipProvider` hər `Tooltip`-dən yuxarıda olmalıdır (açılma gecikməsi paylaşılır), `Toaster` isə `toast()` çağırışlarının render olunduğu portaldır.

### 2.3 Komponentlər — subpath import

```tsx
import { Button } from '@cbar/uikit/button';
import { HomeIcon } from '@cbar/uikit/icons';

<Button colorPalette="primary" size="md">
  <HomeIcon />
  Salam
</Button>
```

Barrel (`from '@cbar/uikit'`) da işləyir, amma subpath soyuq build və type-check-i nəzərəçarpacaq dərəcədə sürətli saxlayır. `form` və `icons` barrel-də **yoxdur** — hərəsinin öz subpath-i var.

### 2.4 Dark mode

```html
<html class="dark">
```

### 2.5 Vite konfiqurasiyası

```ts
export default defineConfig({
  plugins: [react()],
  optimizeDeps: { exclude: ['@cbar/uikit'] },
});
```

Bu olmadan Vite kiti bir dəfə əvvəlcədən bundle edir və hər resync-dən sonra `vite --force` lazım gəlir.

**Next.js**: `next.config`-ə **heç nə əlavə etmə**. Xüsusilə `transpilePackages` yazma — kit kompilyasiya olunmuş ESM + CJS göndərir və `'use client'` direktivləri `dist`-də qalır, yəni Next onu normal paket kimi qəbul edir.

### 2.6 TypeScript — `radix-ui` dev dependency

```bash
npm install --save-dev radix-ui      # pnpm add -D radix-ui
```

Kit **runtime** dependency göndərmir, amma göndərilən `.d.ts` faylları prop tiplərini təsvir etmək üçün hələ də `import * as DialogPrimitive from 'radix-ui/dialog'` yazır. Paket diskdə olmasa həmin tiplər boşluğa resolve olunur və Dialog, Tooltip, Select və daha 15 komponent **heç bir prop qəbul etmirmiş kimi** type-check olunur:

```
TS2559: Type '{ children: Element[]; }' has no properties in common with
        type 'IntrinsicAttributes & DialogContentProps'.
```

Runtime-a təsiri **sıfırdır** — JavaScript-in içində Radix onsuz da var, və tətbiqin build-i bu paketlə də, onsuz da bayt-bayt eynidir (ölçülüb: eyni chunk hash).

---

## 3. Bütün bayraqlar

| Bayraq | Nə edir |
|---|---|
| `--to <yol>` | Target proyekt. **Təkrarlana bilər.** Yol cari qovluğa görə həll olunur. |
| `--watch` | `src/` dəyişəndə avtomatik yenidən build + sync. Yalnız copy rejimi. |
| `--no-build` | Build-i keç, mövcud `dist/`-i olduğu kimi işlət. |
| `--mode copy` | Default. `node_modules`-a birbaşa kopyalayır. |
| `--mode pack` | Əsl tarball qurur və install edir (bax §5). |
| `--pm npm\|pnpm\|yarn` | Kitin build-ini hansı menecer işlətsin. Default `npm`. |
| `--target-pm npm\|pnpm\|yarn` | `--mode pack`-da install əmri. Default: target-in lock faylından oxunur. |
| `--force` | Bu skriptin yazmadığı qovluğun üstünə yaz (bax §6). |
| `-h`, `--help` | Kömək mətni. |

---

## 4. Bir neçə proyekt üçün `.kit-local.json`

Kit kökündə (`template5/.kit-local.json`):

```json
{ "targets": ["../my-app", "../another-app", "../../work/dashboard"] }
```

Sonra sadəcə:

```bash
npm run kit:local
```

Bu fayl **gitignore olunub** — maşına aiddir, hər developer öz siyahısını saxlayır. `--to` verildikdə bu fayl tamamilə nəzərə alınmır.

> Fərq: `--to` yolları **cari qovluğa** görə, `.kit-local.json`-dakı yollar isə **kit kökünə** görə həll olunur (fayl orada yaşadığı üçün).

---

## 5. `copy` yoxsa `pack`

| | `--mode copy` (default) | `--mode pack` |
|---|---|---|
| Nə edir | faylları birbaşa `node_modules`-a kopyalayır | `npm pack` → tarball → target-də install |
| Sürət | saniyələr | install gözləmək lazımdır |
| Target `package.json` | **dəyişmir** | dependency yazılır |
| `install`-dan sonra | **silinə bilər** | sağ qalır |
| Yarn PnP | işləmir | işləyir |
| Nə vaxt | gündəlik iş, `--watch` | komanda yoldaşına vermək, PnP, sabit qurulum |

`pack` rejimində tarball **kitin öz kökünə** yazılır (temp qovluğa yox) — çünki npm target-in `package.json`-una tarball-ın yolunu qeyd edir, temp qovluq isə növbəti təmizləmədə itir və həmin dependency sınır. `*.tgz` kitin `_gitignore`-undadır.

---

## 5b. Niyə `node_modules`, tətbiqin başqa bir qovluğu yox

Haqlı sualdır — `vendor/ui-kit/` kimi bir qovluq repo-da görünən olardı və `install`-dan sağ çıxardı. Buna baxmayaraq, üç səbəb `node_modules`-u doğru hədəf edir.

**`exports` xəritəsi yalnız orada işləyir.** Kitin bütün istifadə modeli 48 subpath-dir — `import { Button } from '@cbar/uikit/button'` — və subpath həlli `node_modules`-da axtarılan *paket adı* üzərindən gedir. `./button` `package.json#exports`-da `./dist/components/button/index.js` kimi elan olunub. Adı bir qovluğa alias etsən, `<pkg>/button` hərfi olaraq `vendor/ui-kit/button` yoluna çevrilir — belə fayl yoxdur. Ya subpath-lar itir, ya da 48-in hər biri üçün alias sətri yazılmalıdır — həm də dörd ayrı yerdə: `tsconfig#paths`, Vite, Next-in webpack konfiqi və Vitest.

**Fidelity skriptin bütün mənasıdır.** O, `package.json#files`-in göstərdiyi dəqiq fayl dəstini kopyalayır (`link-local.mjs`, `shippedEntries()`) ki, lokalda çıxan bug publish-də də çıxacaq bug olsun. `node_modules`-dan kənarda isə heç bir consumer-in işlətmədiyi resolution yolunu işə salırsan: conditional exports (`import` / `require` / `types`), `typesVersions`, Next-in dependency içindəki `'use client'` direktivinə münasibəti, Vite-ın `optimizeDeps`-i. Belə olanda lokal uğur publish olunmuş paket haqqında heç nə demir.

**Tətbiqin import sətirləri heç vaxt dəyişmir.** Bu gün yazılan sətir əsl `npm install <pkg>`-dən sonra yazılacaq sətirlə hərf-hərf eynidir. Lokal nüsxədən publish olunmuş paketə keçmək sadəcə nüsxəni silməkdir — bir sətir kod redaktəsi yoxdur. Vendor qovluğu və nisbi yollar seçilsəydi, publish günü tətbiqdəki hər import yenidən yazılmalı olardı.

### Nəzərdən keçirilmiş və rədd edilmiş yanaşmalar

| Yanaşma | Niyə yox |
|---|---|
| `npm link` / `pnpm link` / `file:` — kit repo-suna yönəlmiş | Hər üçü kitin öz qovluğuna symlink yaradır, orada isə öz `node_modules` var — Node `react`-i tətbiqinkindən əvvəl oradan həll edir və hər hook `Invalid hook call` atır. |
| `vendor/ui-kit/` + bundler alias | `exports` artıq tətbiq olunmur, yəni 48 subpath sınır; sinxron saxlanmalı dörd alias səthi; publish günü tətbiqdəki hər import yenidən yazılır. |
| **yalc** | Bu problemin de-fakto aləti, və bilmək faydalıdır ki, o, məhz "proyektin içində qovluq" ideyasını reallaşdırır — paketlənmiş faylları `<app>/.yalc/<pkg>`-ə qoyur — **və yenə də onlara işarə edən `node_modules/<pkg>` girişi yaradır.** Staging qovluğu import yolu deyil; onun işi `package.json`-a qeyd olunub nüsxəni `install`-dan sağ çıxarmaqdır. Yəni yalc bu qərarı təkzib etmir, təsdiqləyir. |
| Verdaccio və ya başqa lokal registry | Ən yüksək fidelity, amma işləyən servis və hər maşında konfiqurasiya olunmuş registry tələb edir. `--mode pack` eyni nəticəyə heç biri olmadan çatır. |
| Dependency kimi install olunan tarball | Rədd edilmir — bu, elə `--mode pack`-dır, §5. |

Bir dəqiqləşdirmə, çünki yalc sətri başqa cür ziddiyyət kimi oxunur: "kopyala, link etmə" qaydası symlink anlayışının özünə yox, **link-in nəyə işarə etdiyinə** aiddir. Kit repo-suna verilən link React-i ikiləşdirir, çünki kit repo-sunun öz `node_modules`-u var. Tətbiqin **öz içindəki** qovluğa verilən link isə ikiləşdirmir — həll olunan real yol yenə tətbiqin altında qalır, deməli `react` tətbiqin öz ağacından tapılır. yalc-ın işləməsinin bütün səbəbi budur.

`node_modules`-a kopyalamağın yeganə əsl qiyməti odur ki, nüsxə heç bir lock faylında deyil və target-də sonrakı `install` onu silə bilər. Bu, xırda detal deyil, real məhdudiyyətdir — cavabı isə §5-dəki `--mode pack`-dır: o, sağ qalan dependency yazır.

---

## 6. Təhlükəsizlik qoruyucuları

Skript heç nə yazmazdan **əvvəl** yoxlayır:

| Yoxlama | Nəticə |
|---|---|
| Target-də `package.json` yoxdur | dayanır |
| Target-də `node_modules` yoxdur | dayanır — "əvvəlcə install et" |
| Target Yarn PnP-dir (`.pnp.cjs`) | dayanır, `--mode pack` təklif edir |
| Target-in React major-u peer aralığına düşmür | **xəbərdarlıq**, dayanmır |
| Hədəf qovluq var, amma `.kit-local` markeri yoxdur | dayanır — bu, eyni adlı əsl install ola bilər. `--force` ilə keçilir |
| `dist/index.js` və ya `dist/styles.css` yoxdur | dayanır — yarımçıq `dist/` heç nədən pisdir |

Hər uğurlu sync hədəf qovluğa `.kit-local` markeri yazır: mənbə yolu, versiya və tarix. Bunun sayəsində qovluğun lokal drop olduğu bəllidir.

Sync hər dəfə hədəf qovluğu **əvvəlcə tam silir**. Buna görə adı dəyişmiş və ya silinmiş komponentin köhnə `dist/components/<köhnə>/index.js`-i geridə qalıb hələ də resolve olunmur.

---

## 7. Gündəlik iş axını

### Komponenti dəyişdim, tətbiqdə dərhal görmək istəyirəm

```bash
npm run kit:local -- --to ../my-app --watch
```

`src/` altındakı `.ts`, `.tsx`, `.css` dəyişiklikləri izlənir, 300 ms debounce ilə. Beş faylı ard-arda yadda saxlasan **bir** sync olur, beş yox. Build gedərkən gələn yeni dəyişiklik növbəyə düşür — paralel build başlamır. Build sınsa watcher **ölmür**, xətanı çap edir və növbəti yadda saxlamanı gözləyir.

Tətbiqin dev server-i ayrıca terminalda işləyir; sync bitəndən sonra HMR dəyişikliyi götürür.

### Yalnız kopyalamaq istəyirəm, build artıq hazırdır

```bash
npm run kit:local -- --to ../my-app --no-build
```

### Yeni komponent əlavə etdim

```bash
npm run exports:gen        # yeni subpath package.json#exports-a düşsün
npm run kit:local -- --to ../my-app
```

(`kit:local` onsuz da `build` çağırır, o da `exports:gen` ilə başlayır — yəni bu addım avtomatikdir. Ayrıca yazmaq yalnız `--no-build` işlədəndə lazımdır.)

### Tətbiqdə `npm install` etdim və kit yoxa çıxdı

Gözlənilən haldır: copy heç bir lock faylında deyil, ona görə prune edən menecer onu silir. İki yol:

```bash
npm run kit:local -- --to ../my-app --no-build   # yenidən sync
# və ya birdəfəlik həll:
npm run kit:local -- --to ../my-app --mode pack
```

### Komanda yoldaşıma vermək istəyirəm

```bash
npm run kit:local -- --to ../my-app --mode pack
```

Sonra yaranan `.tgz` faylını target repo-nun içinə köçür və oradan install et — belə olanda başqasının klonu da qura bilər.

---

## 8. Nəsə sınanda

| Simptom | Səbəb və həll |
|---|---|
| `TS2559: … has no properties in common with … DialogContentProps` | Tətbiqdə `radix-ui` yoxdur. `npm i -D radix-ui`. Bax §2.6. |
| Dəyişiklik etdim, tətbiqdə görünmür | Vite öz pre-bundle keşini verir. `optimizeDeps.exclude` əlavə et (§2.5); skript `node_modules/.vite`-ı onsuz da silir, amma dev server yenidən başladılmalı ola bilər. |
| `Invalid hook call` | İki React nüsxəsi. Bu skript copy işlətdiyi üçün bu baş verməməlidir — `npm link` və ya `file:` symlink işlətmədiyinə əmin ol. |
| `… has no node_modules` | Target-də əvvəlcə `npm install` işlət. |
| `dist/ is incomplete — missing dist\styles.css` | Yarımçıq build (məsələn tək başına `run-tsup.mjs` işlədilib, onun `clean: true`-su `dist`-i silir). `--no-build`-siz işlət. |
| `… already exists and was not written by this script` | Həmin adda əsl install var. Sil, ya da `--force` ver. |
| `uses Yarn PnP` | `--mode pack` işlət. |
| Stil ümumiyyətlə gəlmir | `styles.css` import olunmayıb, ya da `.dark` sinfi gözlədiyindən fərqlidir. §2.1. |
| Tooltip açılmır / `toast()` heç nə etmir | Kök provider-lər qoyulmayıb. §2.2. |

---

## 9. Nə vaxt bunu buraxıb əsl publish-ə keçmək lazımdır

`kit:local` inkişaf və sınaq üçündür. Bunlardan biri doğrudursa, publish vaxtıdır:

- Kiti işlədən bir neçə nəfər var və hamısı öz maşınında sync etməli olur.
- CI target proyekti qurmalıdır (CI-da bu skript işləmir — kit repo-su orada yoxdur).
- Versiya nömrəsinə görə geri qayıtmaq lazım gəlir.

Publish yolu: [`instructions.az.md` §6](../instructions.az.md#6-kiti-publish-etmək) və `/release-kit` skill-i.
