Skip to content

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 的源代码体现了以下设计原则:

  1. 高效管理:使用 Map 和 Set 实现高效查找
  2. 依赖管理:使用依赖图管理包之间的依赖关系
  3. 构建优化:支持缓存、并行和增量构建
  4. 易于测试:提供清晰的接口

理解源代码有助于更好地使用和优化 Monorepo。

参考资源

架构师AI杜公众号二维码

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