All files / scripts/publish dependencies.ts

0% Statements 0/61
100% Branches 1/1
100% Functions 1/1
0% Lines 0/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                                                                                                                                                                                         
import type { PackageInfo } from '../monorepo/packages'
 
type Graph = Record<string, ReturnType<typeof parsePkg>>
 
export function parsePkg(pkg: PackageInfo) {
  const dependencies: string[] = []
  const devDependencies: string[] = []
  const peerDependencies: string[] = []
 
  // 获取运行时依赖(dependencies)
  if (pkg.manifest.dependencies) {
    Object.keys(pkg.manifest.dependencies).forEach((dep) => {
      const version = pkg.manifest.dependencies[dep]
      if (version.startsWith('workspace:')) {
        dependencies.push(dep)
      }
    })
  }
 
  // 获取开发时依赖(devDependencies)
  if (pkg.manifest.devDependencies) {
    Object.keys(pkg.manifest.devDependencies).forEach((dep) => {
      const version = pkg.manifest.devDependencies[dep]
      if (version.startsWith('workspace:')) {
        devDependencies.push(dep)
      }
    })
  }
 
  // peerDependencies
  if (pkg.manifest.peerDependencies) {
    Object.keys(pkg.manifest.peerDependencies).forEach((dep) => {
      const version = pkg.manifest.peerDependencies[dep]
      if (version.startsWith('workspace:')) {
        peerDependencies.push(dep)
      }
    })
  }
 
  return { dependencies, devDependencies, peerDependencies }
}
 
// 获取包的依赖图,区分开发时依赖和运行时依赖
export function buildDependencyGraph(packages: PackageInfo[]) {
  const graph: Graph = {}
 
  packages.forEach((pkg) => {
    // 添加到图中
    graph[pkg.name] = parsePkg(pkg)
  })
 
  return graph
}
 
// 拓扑排序算法
function topologicalSort(packages: PackageInfo[], graph: Graph) {
  const visited = new Set<string>()
  const order: PackageInfo[] = []
 
  const packagesNames = packages.map(item => item.name)
  // 先发布没有依赖的包
  function visit(pkgName: string) {
    if (visited.has(pkgName))
      return
    visited.add(pkgName)
 
    // 先发布运行时依赖(因为它们是生产环境的依赖)
    const pkg = graph[pkgName]
    pkg?.dependencies.forEach(visit)
 
    // 然后发布开发时依赖(它们不会影响生产环境,但需要优先处理开发环境)
    pkg?.devDependencies.forEach(visit)
 
    // 最后将当前包添加到发布顺序中
    order.push(packages[packagesNames.indexOf(pkgName)]!)
  }
 
  // 从所有包开始遍历
  packages.forEach((pkg) => {
    if (!visited.has(pkg.name)) {
      visit(pkg.name)
    }
  })
 
  return order // 返回发布顺序
}
 
// 根据依赖关系计算发布顺序
export function getPublishOrder(packages: PackageInfo[]) {
  const graph = buildDependencyGraph(packages)
  return topologicalSort(packages, graph)
}