check-circular-dependency.mjs 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import RushSdk from '@rushstack/rush-sdk';
  6. const rushConfig = RushSdk.RushConfiguration.loadFromDefaultLocation();
  7. function getPackageDependencies(packageName, depsMap) {
  8. const projectInfo = rushConfig.getProjectByName(packageName);
  9. if (!projectInfo || depsMap[packageName]) return;
  10. // eslint-disable-next-line no-param-reassign
  11. depsMap[packageName] = [...projectInfo.dependencyProjects].map(
  12. p => p.packageName,
  13. );
  14. for (const dep of depsMap[packageName]) {
  15. getPackageDependencies(dep, depsMap);
  16. }
  17. }
  18. function main() {
  19. const { projects } = rushConfig;
  20. const depsMap = {};
  21. for (const project of projects) {
  22. getPackageDependencies(project.packageName, depsMap);
  23. }
  24. // 定义一个函数来进行深度优先搜索
  25. function dfs(node, visited, stack, graph, cycles) {
  26. visited.add(node); // 将当前节点标记为已访问
  27. stack.push(node); // 将当前节点添加到栈中
  28. // 遍历当前节点的所有邻居
  29. for (const neighbor of graph[node]) {
  30. // 如果邻居节点未被访问,则进行深度优先搜索
  31. if (!visited.has(neighbor)) {
  32. dfs(neighbor, visited, stack, graph, cycles);
  33. }
  34. // 如果邻居节点已经被访问,并且在栈中,则说明存在循环依赖
  35. else if (stack.includes(neighbor)) {
  36. cycles.push([...stack.slice(stack.indexOf(neighbor)), neighbor]); // 将循环依赖添加到数组中
  37. }
  38. }
  39. // 将当前节点从栈中弹出,回溯到上一个节点
  40. stack.pop();
  41. }
  42. // 定义一个函数来查找所有循环依赖
  43. function findCycles(graph) {
  44. const visited = new Set(); // 用于存储已经访问过的节点
  45. const stack = []; // 用于存储遍历过程中经过的节点
  46. const cycles = []; // 用于存储所有循环依赖
  47. // 遍历图中的所有节点
  48. for (const node in graph) {
  49. // 如果当前节点未被访问,则进行深度优先搜索
  50. if (!visited.has(node)) {
  51. dfs(node, visited, stack, graph, cycles);
  52. }
  53. }
  54. // 返回所有循环依赖
  55. return cycles;
  56. }
  57. const cycles = findCycles(depsMap);
  58. if (cycles.length) {
  59. for (const chain of cycles) {
  60. console.log('Circular dependency detected:');
  61. console.log(chain.join('\n->'));
  62. console.log('\n');
  63. if (process.env.CI === 'true') {
  64. console.log(
  65. `::add-message level=error::**检测到循环依赖:**${chain.join('->')}`,
  66. );
  67. }
  68. }
  69. process.exitCode = 1;
  70. }
  71. }
  72. main();