npm workspace monorepo workflow for Starwards - build order, module dependencies, watch mode, testing across modules, and avoiding common monorepo pitfalls; core builds first always
Starwards uses npm workspaces with 4 interdependent modules. Understanding build order, dependencies, and workflow prevents wasted time.
Core principle: Core builds first. Others depend on it.
starwards/
āāā package.json # Root workspace config
āāā modules/
ā āāā core/ # Game logic, shared types
ā ā āāā src/ # TypeScript source
ā ā āāā cjs/ # Built CommonJS (gitignored)
ā ā āāā package.json # @starwards/core
ā āāā server/ # Colyseus rooms, game server
ā ā āāā src/
ā ā āāā cjs/ # Built server code
ā ā āāā package.json # @starwards/server
ā āāā browser/ # React UI, PixiJS rendering
ā ā āāā src/
ā ā āāā dist/ # Webpack bundle
ā ā āāā package.json # @starwards/browser
ā āāā node-red/ # Node-RED integration
ā ā āāā src/
ā ā āāā dist/
ā ā āāā package.json # @starwards/node-red
ā āāā mcp/ # MCP server: LLM station client
ā ā āāā src/
ā ā āāā dist/
ā ā āāā package.json # @starwards/mcp
ā āāā e2e/ # Playwright tests
ā āāā test/
āāā node_modules/ # Shared dependencies (hoisted)
core (no deps)
āāā server (depends on core)
āāā browser (depends on core)
āāā node-red (depends on core)
āāā mcp (depends on core)
āāā e2e (depends on all)
Critical: Core must build before others.
# Build all modules (correct order)
npm run build
# Runs: core ā (server, browser, node-red, mcp in parallel)
# Build specific module
npm run build:core
npm run build:browser
npm run build:server
npm run build:node-red
npm run build:mcp
# Clean all build artifacts
npm run clean
# Removes: cjs/, dist/ from all modules
# Fresh build
npm run clean && npm run build
# Build just this module
cd modules/core && npm run build
cd modules/browser && npm run build
# Watch mode (rebuild on file change)
cd modules/core && npm run build:watch
When to use:
cd modules/core && npm run build:watch
Purpose: Automatically rebuild core when files change
When running: Any time you're editing core/ files
Status: Keep running in background
Troubleshooting:
cd modules/browser && npm start
Purpose: Hot-reload browser UI
Serves: http://localhost:3000
When running: When developing browser/ UI
Troubleshooting:
lsof -ti:3000 | xargs kill -9node -r ts-node/register/transpile-only modules/server/src/dev.ts
Purpose: Run game server with Colyseus
Serves: http://localhost:8080
When running: Always (browser needs API)
Troubleshooting:
# From root: Run all module tests
npm test
# Runs: core tests, server tests, node-red tests
# Specific module
npm test -- --projects=core
npm test -- --projects=server
npm test -- --projects=node-red
# Specific file
npm test -- modules/core/test/shield.spec.ts
# E2E tests require ALL modules built
npm run build # Build everything first
npm run test:e2e # Then run E2E
# Update snapshots
npm run test:e2e -- --update-snapshots
Why build first: E2E tests start real server, which needs built modules.
# Watch all tests
npm test -- --watch
# Watch specific module
cd modules/core && npm test -- --watch
Tip: Run in 4th terminal while developing.
# 1. Ensure core watch running
cd modules/core && npm run build:watch # Terminal 1
# 2. Write test (TDD)
# modules/core/test/shield.spec.ts
# 3. Run test (should fail RED)
npm test -- modules/core/test/shield.spec.ts
# 4. Implement feature
# modules/core/src/ship/shield.ts
# (watch rebuilds automatically)
# 5. Test passes GREEN
npm test -- modules/core/test/shield.spec.ts
# 6. Verify dependents still work
npm test # All tests
npm run test:e2e # E2E tests
# 1. Ensure everything running
# Terminal 1: core watch
# Terminal 2: webpack dev server
# Terminal 3: API server
# 2. Write E2E test (TDD)
# modules/e2e/test/shield-widget.spec.ts
npm run test:e2e -- shield-widget.spec.ts # Fails RED
# 3. Create widget
# modules/browser/src/widgets/shield.ts
# (webpack hot-reloads automatically)
# 4. Test passes GREEN
npm run test:e2e -- shield-widget.spec.ts
# 5. Verify
npm run test:e2e # All E2E
# 1. Core watch running (Terminal 1)
# 2. Write test
# modules/server/test/shield-command.spec.ts
npm test -- modules/server/test/shield-command.spec.ts # RED
# 3. Implement
# modules/server/src/ship/room.ts
npm run build:server
# 4. Restart server (Terminal 3)
# Ctrl+C, then re-run command
# 5. Test passes GREEN
npm test -- modules/server/test/shield-command.spec.ts
# 6. Verify E2E
npm run test:e2e
Automatic (from root):
npm run build
# 1. Build core first (required)
# 2. Build server, browser, node-red in parallel
Manual (module by module):
# CORRECT order
npm run build:core # 1st
npm run build:server # 2nd (can run parallel with browser)
npm run build:browser # 2nd (can run parallel with server)
npm run build:node-red # 2nd (can run parallel with others)
# WRONG order
npm run build:browser # FAILS - core not built yet
npm run build:core # Too late
All modules can import core using @starwards/core:
// In server, browser, or node-red:
import { ShipState } from '@starwards/core';
Configured in:
tsconfig.json (TypeScript)jest.config.js (Jest tests)webpack.common.js (Browser webpack)Maps to:
modules/core/srcmodules/core/cjsSymptom:
Error: Cannot find module '@starwards/core'
Solution:
npm run build:core
# Or start watch mode: cd modules/core && npm run build:watch
Symptom: Server code changes not appearing
Solution: Server doesn't auto-reload. Restart Terminal 3:
Ctrl+C
node -r ts-node/register/transpile-only modules/server/src/dev.ts
Symptom: Browser changes not appearing
Solution: Start Terminal 2:
cd modules/browser && npm start
Symptom: Weird errors, imports failing, tests failing randomly
Solution: Fresh build:
npm run clean
npm ci # Fresh dependencies
npm run build
Symptom: Tests not found
Solution: Run from root, not module:
# CORRECT
npm test
# WRONG
cd modules/core && npm test # Works but limits to core only
Symptom: E2E tests fail, server doesn't start
Solution:
npm run build # Build all first
npm run test:e2e # Then E2E
Symptom: "Multiple versions of package X"
Solution:
npm ci # Use exact versions from package-lock.json
npm dedupe # Deduplicate dependencies
GitHub Actions workflow:
- run: npm ci # Install all workspaces
- run: npm run test:types # Type check all modules
- run: npm run test:format # Format check all modules
- run: npm run build # Build all modules (correct order)
- run: npm test # Test all modules
- run: npm run test:e2e # E2E tests (after build)
Order matters: Build must complete before E2E.
# Verify all modules built
ls modules/core/cjs # Should exist
ls modules/browser/dist # Should exist
ls modules/server/cjs # Should exist
# List workspace dependencies
npm ls @starwards/core # Who depends on core?
# Check if hoisted correctly
ls node_modules/@starwards # Should see symlinks
# Nuclear option - full reset
npm run clean # Remove build artifacts
rm -rf node_modules # Remove dependencies
rm package-lock.json # Remove lockfile
npm install # Fresh install
npm run build # Fresh build
Use when: Everything is broken, nothing makes sense.
| Action | Command |
|---|---|
| Build all | npm run build |
| Build core only | npm run build:core |
| Core watch | cd modules/core && npm run build:watch |
| Test all | npm test |
| Test core only | npm test -- --projects=core |
| Clean all | npm run clean |
| Fresh build | npm run clean && npm ci && npm run build |
| Dev workflow | 3 terminals: core watch, webpack, server |
Remember: