Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adhere to semver rules when using workspace:^ dependencies, fixes #1290 #1291

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/clever-candles-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@changesets/assemble-release-plan": major
"@changesets/cli": major
"@changesets/get-release-plan": major
---

Use semver compat for workspace:^ and workspace:~ dependencies.

Prior to this change, all packages depending on an updated package via `workspace:^` or `workspace:~` got a version bump.
This leads to packages being released that don't need to be according to semver.

This is a bugfix in terms of supporting semver, but it's breaking backwards compatible behavior.

If you're using `workspace:^` or `workspace:~` dependencies in your project, ensure that changes to dependencies of a package come with a dedicated changeset for that package.
22 changes: 15 additions & 7 deletions packages/assemble-release-plan/src/determine-dependents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,23 @@ function getDependencyVersionRanges(
if (!versionRange) continue;

if (versionRange.startsWith("workspace:")) {
// intentionally keep other workspace ranges untouched
// this has to be fixed but this should only be done when adding appropriate tests
let workspaceRange = versionRange.replace(/^workspace:/, "");
switch (workspaceRange) {
case "*":
// workspace:* actually means the current exact version, and not a wildcard similar to a reguler * range
workspaceRange = dependencyRelease.oldVersion;
break;
case "~":
case "^":
// Use ^oldVersion for workspace:^ or ~oldVersion for workspace:~.
// The version range might have changed in dependent package, but that should have its own changeset bumping that package.
workspaceRange += dependencyRelease.oldVersion;
}
dependencyVersionRanges.push({
depType: type,
versionRange:
// intentionally keep other workspace ranges untouched
// this has to be fixed but this should only be done when adding appropriate tests
versionRange === "workspace:*"
? // workspace:* actually means the current exact version, and not a wildcard similar to a reguler * range
dependencyRelease.oldVersion
: versionRange.replace(/^workspace:/, ""),
versionRange: workspaceRange,
});
} else {
dependencyVersionRanges.push({
Expand Down
90 changes: 89 additions & 1 deletion packages/cli/src/commands/version/version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ describe("workspace range", () => {
]);
});

it("should bump dependent package when bumping a `workspace:^` dependency", async () => {
it("should not bump dependent package with a patch release for a `workspace:^` dependency", async () => {
const cwd = await testdir({
"package.json": JSON.stringify({
private: true,
Expand Down Expand Up @@ -1070,14 +1070,102 @@ describe("workspace range", () => {
expect(packages.packages.map((x) => x.packageJson)).toEqual([
{
name: "pkg-a",
version: "1.0.0",
dependencies: {
"pkg-b": "workspace:^",
},
},
{
name: "pkg-b",
version: "1.0.1",
},
]);
});

it("should not bump dependent package with a minor release for a `workspace:^` dependency", async () => {
const cwd = await testdir({
"package.json": JSON.stringify({
private: true,
workspaces: ["packages/*"],
}),
"packages/pkg-a/package.json": JSON.stringify({
name: "pkg-a",
version: "1.0.0",
dependencies: {
"pkg-b": "workspace:^",
},
}),
"packages/pkg-b/package.json": JSON.stringify({
name: "pkg-b",
version: "1.0.0",
}),
});

await writeChangeset(
{
releases: [{ name: "pkg-b", type: "minor" }],
summary: "a very useful summary for the change",
},
cwd
);
await version(cwd, defaultOptions, modifiedDefaultConfig);

let packages = await getPackages(cwd);
expect(packages.packages.map((x) => x.packageJson)).toEqual([
{
name: "pkg-a",
version: "1.0.0",
dependencies: {
"pkg-b": "workspace:^",
},
},
{
name: "pkg-b",
version: "1.1.0",
},
]);
});

it("should bump dependent package with a major release for a `workspace:^` dependency", async () => {
const cwd = await testdir({
"package.json": JSON.stringify({
private: true,
workspaces: ["packages/*"],
}),
"packages/pkg-a/package.json": JSON.stringify({
name: "pkg-a",
version: "1.0.0",
dependencies: {
"pkg-b": "workspace:^",
},
}),
"packages/pkg-b/package.json": JSON.stringify({
name: "pkg-b",
version: "1.0.0",
}),
});

await writeChangeset(
{
releases: [{ name: "pkg-b", type: "major" }],
summary: "a very useful summary for the change",
},
cwd
);
await version(cwd, defaultOptions, modifiedDefaultConfig);

let packages = await getPackages(cwd);
expect(packages.packages.map((x) => x.packageJson)).toEqual([
{
name: "pkg-a",
version: "1.0.1",
dependencies: {
"pkg-b": "workspace:^",
},
},
{
name: "pkg-b",
version: "2.0.0",
},
]);
});
Expand Down