แชร์ประสบการณ์ลอง migrate จาก pnpm ไป yarn berry กัน
Tags: thai, pnpm, yarn, typescript
Published: 2021-11-13 11:33:50

เนื่องจากได้เห็นโพสต์ที่เกี่ยวกับ yarn berry แล้วเลยอยากลองที่จะ migrate จากตัวที่ใช้ pnpm ไป berry ก็เลยเกิดเป็นโพสต์นี้ครับ

## ก่อนเริ่ม

จริงๆแล้วผมเคยได้ยินyarn (berry) มานานมาแล้วแต่ไม่เคยลองเพราะว่าช่วงนั้นผมเคยเห็น issue ที่ใช้แล้ว project ไม่สามารถรันได้เลยก็เลยคิดว่ารอสักพักค่อยมาลองสุดท้ายก็ลืม -.- 

## Timeline

ตั้งแต่เข้าสู่วงการ dev ผมก็เริ่มที่ npm -> yarn(classic) -> pnpm(ปัจจุบัน)

ขอย้อนไปตอนที่ใช้ yarn classic (หลังจากนี้จะขอย่อว่า classic) ผมใช้ feature นึงคือตัว [worksapces](https://classic.yarnpkg.com/en/docs/workspaces)ใช้มาประมาณ 3 ปีได้แล้วถ้านับไม่ผิด และก็เมื่อไม่นานมานี้ผมได้เห็นตัว pnpm เป็นที่พูดถึงกันก็เลยลองไปดูว่ามันเป็นยังไงมี feature ไหนบ้าง

หลังจากที่ไปดูว่าโดยรวมถือว่าโอเคเลยมีทุก feature ที่ผมใช้หลังจากนั้นโปรเจคช่วงๆหลังผมจะเป็น pnpm ทั้งหมด

## ปัจจุบัน

ก่อนที่จะเข้าหัวข้อหลักคือ yarn berry มาดูตัวอย่าง structure ที่ผมใช้ pnpm แบบอย่างง่ายตาม example เลยก็ว่าได้แล้วจะแปลงเป็นในรูปแบบของ yarn berry

```
├── packages 
│   └── moduleA
│         └── package.json ──
│   └── moduleB              │ 
│         └── package.json   │  moduleC เรียกใช้ moduleA
│   └── moduleC              │ 
│         └── package.json __│ 
├── package.json 
├── pnpm-workspace.yaml // <- pnpm เอาไว้ใช้ประกาศ workspace
```
- packages รวม modules ต่างๆไว้ในนี้, submodule จะเป็น typescript project ทั้งหมด

ตัวอย่างโค้ดของแบบ pnpm อยู่ repo นี้ -> [example pnpm code](https://github.com/jungai/migrate-pnpm-to-berry/tree/pnpm)

> required nodejs >= 14.7

อธิบายเพิ่มเติมจาก code 

- module{A,B,C} จะเป็น typescript ทั้งหมดโดยจะให้มัน compile ออกมาเป็น esm format รวมถึง *.d.ts เผื่อให้แต่ละ module มี type ของตัวมันเอง
- moduleC จะเรียกใช้ moduleA สังเกตได้จากตัว [package.json](https://github.com/jungai/migrate-pnpm-to-berry/blob/pnpm/packages/moduleC/package.json) และจะเรียกใช้ใน
[index.ts](https://github.com/jungai/migrate-pnpm-to-berry/blob/pnpm/packages/moduleC/src/index.ts)

ทั้งหมดนี้เมื่อ compile และ run จะทำงานได้ถูกต้อง
> `getTheBestSong` จาก moduleA จะเกิด Bug ไม่ว่าเราจะใส่ parameter ให้หรือไม่ใส่มันก็จะออกแต่ [strawberry moon](https://www.youtube.com/watch?v=sqgxcCjD04s) ❤️

## Yarn (Berry)

หลังจากที่ไม่ได้พูดถึงเลยก็จะขอกลับมาพูดแบบย่อสักนิด

**ข้อดีคร่าวๆของตัว berry**

- เปลี่ยนการ resolve dependency ใหม่
- เพิ่ม ecosystem ของตัวเอง (plugin) e.g. `plugin-typescript`
- constraints e.g. ในแต่ละ workspaces ต้องมี dependencies, library, devDependencies ที่เวอร์ชั่นเท่ากัน
- offline Cache

และแน่นอน workspaces

**ข้อเสีย**
- ส่วนตัวว่า doc ยังทำความเข้าใจได้อยากอยู่และตัวอย่างน้อย

อ่านเพิ่มเติมได้ที่ [doc](https://yarnpkg.com/features/pnp)

## ปรับ pnpm ให้อยู่ในรูปแบบ berry

**tools**
- set yarn version berry ด้วย command `yarn set version berry`
- ลง [vscode extension](https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs)
- run command `yarn dlx @yarnpkg/sdks vscode` (เพื่อให้ vscode รู้การ resolve ที่ไม่ใช่จากตัว node_module)

จากโค้ด pnpm ข้างบน
- ลบ folder node_modules/ (แน่นอนจะไม่มี node_module แล้ว)
- ลบ pnpm-workspace.yaml, pnpm-lock.yaml
- เพิ่ม workspaces ใน [package.json](https://github.com/jungai/migrate-pnpm-to-berry/blob/master/package.json) ใน root project
- ใน moduleC เรียกใช้ workspace moduleA ใน [package.json](https://github.com/jungai/migrate-pnpm-to-berry/blob/master/packages/moduleC/package.json)

หลังจากทำตามข้างบนเรียบร้อยแล้วไปที่ root directory run command `yarn` เพื่อ install dependency

หลังจากที่ install แล้วจะเห็นว่ามันจะไม่มี node_module อีกต่อไปแล้วแต่จะมาในรูปแบบของ javascript file แทน ในที่นี้คือ .pnp.cjs เปรียบเสมือน node_module นั่นเองแล้วที่นี้มันจะไปดู dependencies ที่ไหนละคำตอบก็คือในโฟลเดอร์ .yarn/cache สังเกตว่า berry จะโหลด deps มาในรูปแบบ zip

เรียบแล้วแล้วตอนนี้ project ของเราก็จะอยู่ในรูปแบบของ workspace(berry)
ทีนี้ก็ลอง build ทั้งโปรเจคโดย run command `yarn workspaces foreach run build`

มันจะแจ้งเตือนให้ลง plugin เพิ่ม (ecosystem อันใหม่) run command `yarn plugin import workspace-tools`

เมื่อลงเสร็จลอง build ใหม่ตาม command ข้างบนทีนี้ก็เป็นการเสร็จสิ้น

แต่เดี๋ยวก่อนนน...

ถ้าเกิดลองไปดูที่ moduleC จะเห็นว่ามันจะแจ้งเตือนหา moduleA ไม่เจอ ซึ่งผมก็ติดอยู่ตรงนี้แหละไม่ได้ไปต่อ ซึ่งลองไปดู issue ก็จะมีคนติดปัญหาที่คล้ายๆแบบนี้ก็เลยไม่ได้ไปลอง feature อื่นๆต่อ

แต่ถ้า project เป็น javascript ไม่ใช่ typescript สามารถทำงานได้ปกติ

โค้ดของ berry -> [example berry code](https://github.com/jungai/migrate-pnpm-to-berry)

## สรุป

ปัญหาข้างบนเป็นในกรณีที่เราใช้ตัว workspaces แต่ถ้าไม่ได้ใช้ workspaces ตัว berry ทำงานได้ปกติ (resolve ได้ปกติ)

ทั้งหมดนี้ก็เป็นประสบการณ์ที่ผมได้เจอในการลองที่จะย้ายค่ายกลับมาใช้ yarn, ปัญหาที่เกิดขึ้นอาจจะเกิดที่ผมเซ็ตไม่ถูกต้องก็ได้ครับและถ้ามีโอกาสได้กลับมาแก้หรือมันแก้ไข้อะไรยังไงเรียบร้อยแล้วจะมาแชร์อีกทีครับ

ทั้งหมดนี้ถ้าผมสื่อสารผิดพลาดหรือทำให้งงยังไงก็ขอโทษด้วยนะครับจะพยายามปรับปรุงเรื่อยๆครับ ขอบคุณครับ 🙏


## Refs
- [https://yarnpkg.com/features/pnp](https://yarnpkg.com/features/pnp)
- [https://yarnpkg.com/getting-started/editor-sdks](https://yarnpkg.com/getting-started/editor-sdks)
- [issue](https://github.com/yarnpkg/berry/issues/3399)