Category: reference
npm Workspaces & Monorepo Basics
จัดการ multiple packages ใน repo เดียวด้วย npm workspaces — shared dependencies, cross-package scripts, และ monorepo patterns
สารบัญ
npm Workspaces คืออะไร
npm Workspaces ให้จัดการหลาย packages ใน repository เดียวได้ — แชร์ node_modules ร่วมกัน, import กันได้โดยไม่ต้อง publish, และ run scripts ใน workspace ทั้งหมดด้วยคำสั่งเดียว
my-monorepo/
├── package.json ← root (workspace root)
├── node_modules/ ← shared (symlinks จาก packages/)
├── packages/
│ ├── core/ ← @myapp/core
│ │ ├── package.json
│ │ └── src/
│ ├── ui/ ← @myapp/ui
│ │ ├── package.json
│ │ └── src/
│ └── cli/ ← @myapp/cli
│ ├── package.json
│ └── src/
└── apps/
└── web/ ← main app (ใช้ packages ข้างบน)
├── package.json
└── src/
Setup
// package.json (root)
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"packages/*",
"apps/*"
],
"scripts": {
"build": "npm run build --workspaces --if-present",
"test": "npm test --workspaces --if-present",
"lint": "npm run lint --workspaces --if-present"
}
}
# ติดตั้ง dependencies ทั้งหมดจาก root
npm install
# npm จะสร้าง symlinks ใน node_modules/ สำหรับแต่ละ workspace
# packages/core → node_modules/@myapp/core
Package Names ใน Workspace
// packages/core/package.json
{
"name": "@myapp/core",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
}
}
// apps/web/package.json
{
"name": "@myapp/web",
"dependencies": {
"@myapp/core": "*", // ✓ ใช้ * เพื่อ reference workspace package
"@myapp/ui": "*"
}
}
Install Dependencies
# ติดตั้ง dependency ใน workspace เฉพาะ
npm install lodash --workspace=packages/core
# ติดตั้ง dev dependency ที่ root (shared across all)
npm install --save-dev typescript --workspace=.
# ติดตั้ง package อื่นใน workspace เป็น dependency
npm install @myapp/core --workspace=apps/web
Run Scripts
# รัน script ใน workspace เดียว
npm run build --workspace=packages/core
# รันใน workspaces หลายตัว
npm run build --workspace=packages/core --workspace=packages/ui
# รันใน workspace ที่มี script นั้น (ข้ามถ้าไม่มี)
npm run build --workspaces --if-present
# รันตามลำดับ (default: parallel)
npm run build --workspaces --if-present --foreground-scripts
Shared TypeScript Config
// tsconfig.base.json (root)
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}
// packages/core/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"]
}
TypeScript Project References
Project references ทำให้ TypeScript รู้ว่า packages ไหน depend on กัน → incremental build เร็วขึ้น
// tsconfig.json (root)
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/ui" },
{ "path": "./apps/web" }
]
}
// apps/web/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"composite": true
},
"references": [
{ "path": "../../packages/core" },
{ "path": "../../packages/ui" }
]
}
# Build แบบ incremental (เฉพาะส่วนที่เปลี่ยน)
tsc --build
# Build ทั้งหมด
tsc --build --force
# Watch mode แบบ project references
tsc --build --watch
Shared ESLint Config ใน Monorepo
// eslint.config.js (root)
export default [
{ ignores: ['**/dist/**', '**/node_modules/**'] },
// shared rules สำหรับ TS files
];
// packages/core/package.json
{
"scripts": {
"lint": "eslint --config ../../eslint.config.js src"
}
}
เปรียบเทียบกับ Tools อื่น
| npm workspaces | pnpm workspaces | Turborepo | |
|---|---|---|---|
| Built-in | ✓ ไม่ต้องติดตั้งเพิ่ม | ✗ ติดตั้ง pnpm | ✗ ติดตั้ง turbo |
| Speed | ดี | เร็วกว่า (hard links) | ดีที่สุด (caching) |
| Task running | พื้นฐาน | พื้นฐาน | ซับซ้อน แต่ parallel + cache |
| เหมาะกับ | project ขนาดกลาง | monorepo จริงจัง | large-scale |
สำหรับโปรเจกต์ส่วนตัว npm workspaces เพียงพอ — ไม่ต้องเพิ่ม complexity ของ pnpm หรือ Turborepo