6969 const fixed = new Set((config.fixed || []).flat());
7070 const roots = ["packages", "apps"];
7171 const skipDirNames = new Set(["dist", "node_modules", ".git", ".nx"]);
72- const packagePathByName = new Map();
72+ const packageMetaByName = new Map();
7373
7474 function walk(dir) {
7575 if (!fs.existsSync(dir)) return;
8484 const pkgDir = path.dirname(fullPath);
8585 const pkg = JSON.parse(fs.readFileSync(fullPath, "utf8"));
8686 if (fixed.has(pkg.name) && pkg.private !== true) {
87- const existing = packagePathByName.get(pkg.name);
88- if (!existing || pkgDir.length < existing.length) {
89- packagePathByName.set(pkg.name, pkgDir);
87+ const existing = packageMetaByName.get(pkg.name);
88+ if (!existing || pkgDir.length < existing.path.length) {
89+ packageMetaByName.set(pkg.name, {
90+ name: pkg.name,
91+ path: pkgDir,
92+ pkg,
93+ });
9094 }
9195 }
9296 }
@@ -97,9 +101,73 @@ jobs:
97101 walk(root);
98102 }
99103
100- // Deduplicate by package name and sort for stable output.
101- const sorted = Array.from(packagePathByName.values()).sort();
102- process.stdout.write(JSON.stringify(sorted));
104+ const dependencyFields = [
105+ "dependencies",
106+ "devDependencies",
107+ "optionalDependencies",
108+ "peerDependencies",
109+ ];
110+
111+ for (const meta of packageMetaByName.values()) {
112+ const internalDeps = new Set();
113+ for (const field of dependencyFields) {
114+ const deps = meta.pkg[field];
115+ if (!deps || typeof deps !== "object") continue;
116+ for (const depName of Object.keys(deps)) {
117+ if (packageMetaByName.has(depName)) {
118+ internalDeps.add(depName);
119+ }
120+ }
121+ }
122+ meta.internalDeps = internalDeps;
123+ }
124+
125+ const indegreeByName = new Map();
126+ const dependentsByName = new Map();
127+ for (const [name, meta] of packageMetaByName) {
128+ indegreeByName.set(name, meta.internalDeps.size);
129+ dependentsByName.set(name, new Set());
130+ }
131+
132+ for (const [name, meta] of packageMetaByName) {
133+ for (const depName of meta.internalDeps) {
134+ dependentsByName.get(depName).add(name);
135+ }
136+ }
137+
138+ const queue = Array.from(indegreeByName.entries())
139+ .filter(([, degree]) => degree === 0)
140+ .map(([name]) => name)
141+ .sort();
142+ const orderedNames = [];
143+
144+ while (queue.length) {
145+ const current = queue.shift();
146+ orderedNames.push(current);
147+ const dependents = Array.from(dependentsByName.get(current) || []).sort();
148+ for (const dependent of dependents) {
149+ const nextDegree = (indegreeByName.get(dependent) || 0) - 1;
150+ indegreeByName.set(dependent, nextDegree);
151+ if (nextDegree === 0) {
152+ queue.push(dependent);
153+ }
154+ }
155+ queue.sort();
156+ }
157+
158+ if (orderedNames.length !== packageMetaByName.size) {
159+ const remaining = Array.from(packageMetaByName.keys())
160+ .filter((name) => !orderedNames.includes(name))
161+ .sort((a, b) => {
162+ const pathA = packageMetaByName.get(a).path;
163+ const pathB = packageMetaByName.get(b).path;
164+ return pathA.localeCompare(pathB);
165+ });
166+ orderedNames.push(...remaining);
167+ }
168+
169+ const orderedPaths = orderedNames.map((name) => packageMetaByName.get(name).path);
170+ process.stdout.write(JSON.stringify(orderedPaths));
103171 ')
104172
105173 echo "paths_json=$paths_json" >> "$GITHUB_OUTPUT"
@@ -131,6 +199,7 @@ jobs:
131199 "--pnpm",
132200 "--packageManager=pnpm",
133201 "--comment=update",
202+ "--commentWithSha",
134203 ...paths,
135204 ];
136205
0 commit comments