Skip to content

Commit 50306cc

Browse files
committed
fix: apply peer dependency validation without changes to ui
1 parent 36c7b07 commit 50306cc

File tree

3 files changed

+71
-10
lines changed

3 files changed

+71
-10
lines changed

src/release-specification.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,11 @@ export async function waitForUserToEditReleaseSpecification(
172172
*
173173
* @param project - The project containing workspace packages.
174174
* @param packageName - The name of the package to find dependents for.
175-
* @param unvalidatedReleaseSpecificationPackages - The packages in the release specification.
176-
* @returns An array of package names that depend on the given package and are missing from the release spec.
175+
* @returns An array of package names that depend on the given package.
177176
*/
178-
export function findMissingUnreleasedDependents(
177+
export function findAllWorkspacePackagesThatDependOnPackage(
179178
project: Project,
180179
packageName: string,
181-
unvalidatedReleaseSpecificationPackages: Record<string, string | null>,
182180
): string[] {
183181
const dependentNames = Object.keys(project.workspacePackages).filter(
184182
(possibleDependentName) => {
@@ -189,6 +187,27 @@ export function findMissingUnreleasedDependents(
189187
},
190188
);
191189

190+
return dependentNames;
191+
}
192+
193+
/**
194+
* Finds all workspace packages that depend on the given package.
195+
*
196+
* @param project - The project containing workspace packages.
197+
* @param packageName - The name of the package to find dependents for.
198+
* @param unvalidatedReleaseSpecificationPackages - The packages in the release specification.
199+
* @returns An array of package names that depend on the given package and are missing from the release spec.
200+
*/
201+
export function findMissingUnreleasedDependents(
202+
project: Project,
203+
packageName: string,
204+
unvalidatedReleaseSpecificationPackages: Record<string, string | null>,
205+
): string[] {
206+
const dependentNames = findAllWorkspacePackagesThatDependOnPackage(
207+
project,
208+
packageName,
209+
);
210+
192211
return dependentNames.filter((dependentName) => {
193212
return !unvalidatedReleaseSpecificationPackages[dependentName];
194213
});

src/ui.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from './project.js';
1111
import { Package } from './package.js';
1212
import {
13+
findAllWorkspacePackagesThatDependOnPackage,
1314
findMissingUnreleasedDependenciesForRelease,
1415
findMissingUnreleasedDependentsForBreakingChanges,
1516
IncrementableVersionParts,
@@ -130,15 +131,31 @@ function createApp({
130131
app.use(express.static(UI_BUILD_DIR));
131132
app.use(express.json());
132133

133-
app.get('/api/packages', (_req, res) => {
134+
app.get('/api/packages', (req, res) => {
135+
const { majorBumps } = req.query;
136+
137+
const majorBumpsArray =
138+
typeof majorBumps === 'string'
139+
? majorBumps.split(',').filter(Boolean)
140+
: (req.query.majorBumps as string[] | undefined) || [];
141+
142+
const requiredDependents = [
143+
...new Set(
144+
majorBumpsArray.flatMap((majorBump) =>
145+
findAllWorkspacePackagesThatDependOnPackage(project, majorBump),
146+
),
147+
),
148+
];
149+
134150
const pkgs = Object.values(project.workspacePackages).filter(
135-
(pkg) => pkg.hasChangesSinceLatestRelease,
151+
(pkg) =>
152+
pkg.hasChangesSinceLatestRelease ||
153+
requiredDependents.includes(pkg.validatedManifest.name),
136154
);
137155

138156
const packages = pkgs.map((pkg) => ({
139157
name: pkg.validatedManifest.name,
140158
version: pkg.validatedManifest.version.version,
141-
location: pkg.directoryPath,
142159
}));
143160

144161
res.json(packages);

src/ui/App.tsx

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import './style.css';
2-
import React, { useState, useEffect } from 'react';
2+
import React, { useState, useEffect, useRef } from 'react';
33
import { createRoot } from 'react-dom/client';
44
import { SemVer } from 'semver';
55
import { ErrorMessage } from './ErrorMessage.js';
66
import { PackageItem } from './PackageItem.js';
77
import { Package, RELEASE_TYPE_OPTIONS, ReleaseType } from './types.js';
88

9+
// Helper function to compare sets
10+
const setsAreEqual = (a: Set<string>, b: Set<string>) => {
11+
if (a.size !== b.size) return false;
12+
return [...a].every((value) => b.has(value));
13+
};
14+
915
type SubmitButtonProps = {
1016
selections: Record<string, string>;
1117
packageDependencyErrors: Record<
@@ -67,16 +73,35 @@ function App() {
6773
new Set(),
6874
);
6975
const [showCheckboxes, setShowCheckboxes] = useState(false);
76+
const previousPackages = useRef<Set<string>>(new Set());
7077

7178
useEffect(() => {
72-
fetch('/api/packages')
79+
const majorBumps = Object.entries(selections)
80+
.filter(([_, type]) => type === 'major')
81+
.map(([pkgName]) => pkgName);
82+
83+
fetch(`/api/packages?majorBumps=${majorBumps.join(',')}`)
7384
.then((res) => {
7485
if (!res.ok) {
7586
throw new Error(`Received ${res.status}`);
7687
}
7788
return res.json();
7889
})
7990
.then((data: Package[]) => {
91+
const newPackageNames = new Set(data.map((pkg) => pkg.name));
92+
93+
// Only clean up selections if the package list actually changed
94+
if (!setsAreEqual(previousPackages.current, newPackageNames)) {
95+
setSelections((prev) =>
96+
Object.fromEntries(
97+
Object.entries(prev).filter(([pkgName]) =>
98+
newPackageNames.has(pkgName),
99+
),
100+
),
101+
);
102+
previousPackages.current = newPackageNames;
103+
}
104+
80105
setPackages(data);
81106
setLoadingChangelogs(
82107
data.reduce((acc, pkg) => ({ ...acc, [pkg.name]: false }), {}),
@@ -86,7 +111,7 @@ function App() {
86111
setError(err.message);
87112
console.error('Error fetching packages:', err);
88113
});
89-
}, []);
114+
}, [selections]);
90115

91116
const checkDependencies = async (selectionData: Record<string, string>) => {
92117
if (Object.keys(selectionData).length === 0) return;

0 commit comments

Comments
 (0)