Appearance
Monorepo 源代码导览
项目结构
monorepo-course/
├── 04-core-feature/ # 核心功能实现
│ ├── src/
│ │ ├── workspace.js # 工作空间管理
│ │ ├── graph.js # 依赖图
│ │ └── build.js # 构建系统
│ ├── test/
│ │ ├── workspace.test.js
│ │ ├── graph.test.js
│ │ └── build.test.js
│ ├── package.json
│ └── README.md
├── 05-lesson-plan.md # 课程计划
├── 01-intro.md # 背景研究
├── 02-arch.md # 架构分析
└── 03-code-walkthrough.md # 源代码导览核心文件解析
1. workspace.js - 工作空间管理
文件路径: src/workspace.js
核心功能:
- 管理工作空间
- 解析依赖关系
- 协调构建流程
关键代码:
javascript
// 工作空间
export class Workspace {
constructor(options = {}) {
this.name = options.name
this.path = options.path
this.version = options.version || '1.0.0'
this.dependencies = options.dependencies || {}
this.devDependencies = options.devDependencies || {}
this.peerDependencies = options.peerDependencies || {}
}
// 获取所有依赖
getAllDependencies() {
return {
...this.dependencies,
...this.devDependencies,
...this.peerDependencies
}
}
// 检查是否依赖某个包
hasDependency(name) {
return this.getAllDependencies().hasOwnProperty(name)
}
}
// 工作空间管理器
export class WorkspaceManager {
constructor(config = {}) {
this.config = config
this.workspaces = new Map()
}
// 添加工作空间
addWorkspace(workspace) {
this.workspaces.set(workspace.name, workspace)
}
// 获取工作空间
getWorkspace(name) {
return this.workspaces.get(name)
}
// 获取所有工作空间
getWorkspaces() {
return Array.from(this.workspaces.values())
}
// 解析依赖关系
resolveDependencies() {
const graph = new DependencyGraph()
for (const workspace of this.workspaces.values()) {
graph.addNode(workspace.name, workspace)
for (const dep of Object.keys(workspace.getAllDependencies())) {
if (this.workspaces.has(dep)) {
graph.addEdge(workspace.name, dep)
}
}
}
return graph
}
}设计要点:
- 使用 Map 存储工作空间
- 支持多种依赖类型
- 自动解析依赖关系
2. graph.js - 依赖图
文件路径: src/graph.js
核心功能:
- 构建依赖图
- 检测循环依赖
- 计算构建顺序
关键代码:
javascript
// 依赖图
export class DependencyGraph {
constructor() {
this.nodes = new Map()
this.edges = new Map()
}
// 添加节点
addNode(name, workspace) {
this.nodes.set(name, workspace)
this.edges.set(name, new Set())
}
// 添加边
addEdge(from, to) {
if (this.edges.has(from)) {
this.edges.get(from).add(to)
}
}
// 获取依赖
getDependencies(name) {
return Array.from(this.edges.get(name) || [])
}
// 获取被依赖
getDependents(name) {
const dependents = []
for (const [node, deps] of this.edges.entries()) {
if (deps.has(name)) {
dependents.push(node)
}
}
return dependents
}
// 拓扑排序
topologicalSort() {
const visited = new Set()
const result = []
const visit = (name) => {
if (visited.has(name)) return
visited.add(name)
for (const dep of this.getDependencies(name)) {
visit(dep)
}
result.push(name)
}
for (const name of this.nodes.keys()) {
visit(name)
}
return result.reverse()
}
// 检测循环依赖
detectCycle() {
const visited = new Set()
const recursionStack = new Set()
const hasCycle = (name) => {
if (recursionStack.has(name)) {
return true
}
if (visited.has(name)) {
return false
}
visited.add(name)
recursionStack.add(name)
for (const dep of this.getDependencies(name)) {
if (hasCycle(dep)) {
return true
}
}
recursionStack.delete(name)
return false
}
for (const name of this.nodes.keys()) {
if (hasCycle(name)) {
return true
}
}
return false
}
}设计要点:
- 使用 Map 和 Set 存储图结构
- 支持拓扑排序
- 支持循环依赖检测
3. build.js - 构建系统
文件路径: src/build.js
核心功能:
- 并行构建
- 增量构建
- 缓存构建结果
关键代码:
javascript
// 构建缓存
export class BuildCache {
constructor() {
this.cache = new Map()
}
get(key) {
return this.cache.get(key)
}
set(key, value) {
this.cache.set(key, value)
}
has(key) {
return this.cache.has(key)
}
clear() {
this.cache.clear()
}
}
// 构建系统
export class BuildSystem {
constructor(config = {}) {
this.config = config
this.cache = new BuildCache()
}
// 获取缓存键
getCacheKey(workspace) {
return `${workspace.name}:${workspace.version}:${JSON.stringify(workspace.getAllDependencies())}`
}
// 构建工作空间
async buildWorkspace(workspace) {
const cacheKey = this.getCacheKey(workspace)
if (this.cache.has(cacheKey)) {
console.log(`使用缓存: ${workspace.name}`)
return this.cache.get(cacheKey)
}
console.log(`构建: ${workspace.name}`)
const result = await this.buildInternal(workspace)
this.cache.set(cacheKey, result)
return result
}
// 内部构建
async buildInternal(workspace) {
// 模拟构建过程
await new Promise(resolve => setTimeout(resolve, 100))
return {
name: workspace.name,
success: true,
timestamp: Date.now()
}
}
// 构建所有工作空间(按拓扑顺序)
async buildAll(workspaces) {
const graph = new DependencyGraph()
for (const workspace of workspaces) {
graph.addNode(workspace.name, workspace)
for (const dep of Object.keys(workspace.getAllDependencies())) {
if (workspaces.find(ws => ws.name === dep)) {
graph.addEdge(workspace.name, dep)
}
}
}
const order = graph.topologicalSort()
const results = []
for (const name of order) {
const workspace = graph.nodes.get(name)
const result = await this.buildWorkspace(workspace)
results.push(result)
}
return results
}
// 并行构建
async buildParallel(workspaces) {
const promises = workspaces.map(ws =>
this.buildWorkspace(ws)
)
return Promise.all(promises)
}
// 增量构建
async buildIncremental(workspaces, changedFiles) {
const affected = this.getAffectedWorkspaces(workspaces, changedFiles)
console.log(`受影响的工作空间: ${affected.map(ws => ws.name).join(', ')}`)
const results = []
for (const workspace of affected) {
const result = await this.buildWorkspace(workspace)
results.push(result)
}
return results
}
// 获取受影响的工作空间
getAffectedWorkspaces(workspaces, changedFiles) {
const affected = new Set()
for (const workspace of workspaces) {
for (const file of changedFiles) {
if (file.startsWith(workspace.path)) {
affected.add(workspace)
break
}
}
}
// 添加依赖这些工作空间的工作空间
const graph = new DependencyGraph()
for (const workspace of workspaces) {
graph.addNode(workspace.name, workspace)
for (const dep of Object.keys(workspace.getAllDependencies())) {
if (workspaces.find(ws => ws.name === dep)) {
graph.addEdge(workspace.name, dep)
}
}
}
const moreAffected = new Set()
for (const workspace of affected) {
const dependents = graph.getDependents(workspace.name)
for (const dep of dependents) {
moreAffected.add(graph.nodes.get(dep))
}
}
return [...affected, ...moreAffected]
}
}设计要点:
- 支持缓存构建结果
- 支持按拓扑顺序构建
- 支持并行和增量构建
关键设计决策
1. 使用 Map 和 Set
原因:
- 快速查找
- 自动去重
- 内存效率高
实现:
javascript
this.workspaces = new Map()
this.edges = new Map()2. 拓扑排序
原因:
- 确保正确的构建顺序
- 避免依赖问题
- 支持增量构建
实现:
javascript
topologicalSort() {
const visited = new Set()
const result = []
const visit = (name) => {
if (visited.has(name)) return
visited.add(name)
for (const dep of this.getDependencies(name)) {
visit(dep)
}
result.push(name)
}
// ...
}3. 缓存构建结果
原因:
- 避免重复构建
- 提升构建速度
- 节省资源
实现:
javascript
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)
}
const result = await this.buildInternal(workspace)
this.cache.set(cacheKey, result)测试策略
单元测试
javascript
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { Workspace, WorkspaceManager } from '../src/workspace.js'
describe('Workspace 测试', () => {
it('应该创建工作空间', () => {
const workspace = new Workspace({
name: 'test',
path: '/test'
})
assert.ok(workspace)
})
it('应该添加工作空间', () => {
const manager = new WorkspaceManager()
const workspace = new Workspace({
name: 'test',
path: '/test'
})
manager.addWorkspace(workspace)
assert.strictEqual(manager.getWorkspace('test'), workspace)
})
})总结
Monorepo 的源代码体现了以下设计原则:
- 高效管理:使用 Map 和 Set 实现高效查找
- 依赖管理:使用依赖图管理包之间的依赖关系
- 构建优化:支持缓存、并行和增量构建
- 易于测试:提供清晰的接口
理解源代码有助于更好地使用和优化 Monorepo。
参考资源

扫描二维码关注"架构师AI杜"公众号,获取更多技术内容和最新动态
