Repository: atlassian/changesets
Pull request #585 (2021-05-04 12:40:47)

Add support for workspace version alias
Adds support for `workspace:^` and `workspace:~`.

atlassian-cla-bot[bot]: Thank you for your submission! Like many open source projects, we ask that you sign our <a href=https://opensource.atlassian.com/cla>CLA (Contributor License Agreement)</a> before we can accept your contribution.
      <strong>If your email is listed below</strong>, please ensure that you sign the CLA with the <strong>same email address</strong>.</br>
      <strong>The following users still need to sign our CLA:</strong></br>❌javier-garcia-meteologica
<p><sub>Already signed the CLA? To re-check, try refreshing the page.</sub></p>
changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: a63b6de104b63b32089290c8c6b42072645b8d73

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 3 packages</summary>

  | Name                             | Type  |
| -------------------------------- | ----- |
| @changesets/apply-release-plan   | Patch |
| @changesets/cli                  | Patch |
| @changesets/get-dependents-graph | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/javier-garcia-meteologica/changesets/new/add-workspace-version-alias?filename=.changeset/rich-readers-pump.md&value=---%0A%22%40changesets%2Fapply-release-plan%22%3A%20patch%0A%22%40changesets%2Fcli%22%3A%20patch%0A%22%40changesets%2Fget-dependents-graph%22%3A%20patch%0A---%0A%0AAdd%20support%20for%20workspace%20version%20alias%0A)


Andarist: Could you point to any documentation about those two?
javier-garcia-meteologica: This feature was recently merged into pnpm and at yarn they are working to implement it.

https://github.com/pnpm/pnpm/pull/3368
https://github.com/yarnpkg/berry/issues/2658

There is no documentation yet about this feature, but I should probably write about it.
javier-garcia-meteologica: I was wrong, it's already documented in the [workspaces section of pnpm](https://pnpm.io/workspaces)
NMinhNguyen: Sorry for being annoying, but wanted to ask if there's still plans to merge this?

Patch:
diff --git a/.changeset/stale-melons-shout.md b/.changeset/stale-melons-shout.md
new file mode 100644
index 0000000..1b988de
--- /dev/null
+++ b/.changeset/stale-melons-shout.md
@@ -0,0 +1,7 @@
+---
+"@changesets/apply-release-plan": patch
+"@changesets/cli": patch
+"@changesets/get-dependents-graph": patch
+---
+
+Add support for `workspace:^` and `workspace:~` dependency ranges.
diff --git a/__fixtures__/workspace-alias-range-dep/.changeset/README.md b/__fixtures__/workspace-alias-range-dep/.changeset/README.md
new file mode 100644
index 0000000..fd188f5
--- /dev/null
+++ b/__fixtures__/workspace-alias-range-dep/.changeset/README.md
@@ -0,0 +1,3 @@
+# We just want a file in here so git collects it
+
+For this we have deliberately not included a config file, as we want to test the defaults
\ No newline at end of file
diff --git a/__fixtures__/workspace-alias-range-dep/.changeset/config.json b/__fixtures__/workspace-alias-range-dep/.changeset/config.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/__fixtures__/workspace-alias-range-dep/.changeset/config.json
@@ -0,0 +1,1 @@
+{}
diff --git a/__fixtures__/workspace-alias-range-dep/package.json b/__fixtures__/workspace-alias-range-dep/package.json
new file mode 100644
index 0000000..0031ef5
--- /dev/null
+++ b/__fixtures__/workspace-alias-range-dep/package.json
@@ -0,0 +1,11 @@
+{
+  "private": true,
+  "name": "workspace-alias-range-dep",
+  "description": "3 projects, one depending on the rest with `workspace:^` and `workspace:~` ranges",
+  "version": "1.0.0",
+  "bolt": {
+    "workspaces": [
+      "packages/*"
+    ]
+  }
+}
diff --git a/__fixtures__/workspace-alias-range-dep/packages/pkg-a/package.json b/__fixtures__/workspace-alias-range-dep/packages/pkg-a/package.json
new file mode 100644
index 0000000..f03c33a
--- /dev/null
+++ b/__fixtures__/workspace-alias-range-dep/packages/pkg-a/package.json
@@ -0,0 +1,8 @@
+{
+  "name": "pkg-a",
+  "version": "1.0.0",
+  "dependencies": {
+    "pkg-b": "workspace:^",
+    "pkg-c": "workspace:~"
+  }
+}
diff --git a/__fixtures__/workspace-alias-range-dep/packages/pkg-b/package.json b/__fixtures__/workspace-alias-range-dep/packages/pkg-b/package.json
new file mode 100644
index 0000000..be4760e
--- /dev/null
+++ b/__fixtures__/workspace-alias-range-dep/packages/pkg-b/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "pkg-b",
+  "version": "1.0.0"
+}
diff --git a/__fixtures__/workspace-alias-range-dep/packages/pkg-c/package.json b/__fixtures__/workspace-alias-range-dep/packages/pkg-c/package.json
new file mode 100644
index 0000000..888ba5c
--- /dev/null
+++ b/__fixtures__/workspace-alias-range-dep/packages/pkg-c/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "pkg-c",
+  "version": "1.0.0"
+}
diff --git a/__fixtures__/workspace-version-alias-dep/.changeset/README.md b/__fixtures__/workspace-version-alias-dep/.changeset/README.md
new file mode 100644
index 0000000..fd188f5
--- /dev/null
+++ b/__fixtures__/workspace-version-alias-dep/.changeset/README.md
@@ -0,0 +1,3 @@
+# We just want a file in here so git collects it
+
+For this we have deliberately not included a config file, as we want to test the defaults
\ No newline at end of file
diff --git a/__fixtures__/workspace-version-alias-dep/.changeset/config.json b/__fixtures__/workspace-version-alias-dep/.changeset/config.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/__fixtures__/workspace-version-alias-dep/.changeset/config.json
@@ -0,0 +1,1 @@
+{}
diff --git a/__fixtures__/workspace-version-alias-dep/package.json b/__fixtures__/workspace-version-alias-dep/package.json
new file mode 100644
index 0000000..148971d
--- /dev/null
+++ b/__fixtures__/workspace-version-alias-dep/package.json
@@ -0,0 +1,11 @@
+{
+  "private": true,
+  "name": "simple-project",
+  "description": "Four projects with workspace version aliases",
+  "version": "1.0.0",
+  "bolt": {
+    "workspaces": [
+      "packages/*"
+    ]
+  }
+}
diff --git a/__fixtures__/workspace-version-alias-dep/packages/pkg-a/package.json b/__fixtures__/workspace-version-alias-dep/packages/pkg-a/package.json
new file mode 100644
index 0000000..827bbb3
--- /dev/null
+++ b/__fixtures__/workspace-version-alias-dep/packages/pkg-a/package.json
@@ -0,0 +1,9 @@
+{
+  "name": "pkg-a",
+  "version": "1.0.0",
+  "dependencies": {
+    "pkg-b": "workspace:*",
+    "pkg-c": "workspace:^",
+    "pkg-d": "workspace:~"
+  }
+}
diff --git a/__fixtures__/workspace-version-alias-dep/packages/pkg-b/package.json b/__fixtures__/workspace-version-alias-dep/packages/pkg-b/package.json
new file mode 100644
index 0000000..be4760e
--- /dev/null
+++ b/__fixtures__/workspace-version-alias-dep/packages/pkg-b/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "pkg-b",
+  "version": "1.0.0"
+}
diff --git a/__fixtures__/workspace-version-alias-dep/packages/pkg-c/package.json b/__fixtures__/workspace-version-alias-dep/packages/pkg-c/package.json
new file mode 100644
index 0000000..888ba5c
--- /dev/null
+++ b/__fixtures__/workspace-version-alias-dep/packages/pkg-c/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "pkg-c",
+  "version": "1.0.0"
+}
diff --git a/__fixtures__/workspace-version-alias-dep/packages/pkg-d/package.json b/__fixtures__/workspace-version-alias-dep/packages/pkg-d/package.json
new file mode 100644
index 0000000..c4b60fc
--- /dev/null
+++ b/__fixtures__/workspace-version-alias-dep/packages/pkg-d/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "pkg-d",
+  "version": "1.0.0"
+}
diff --git a/packages/apply-release-plan/src/version-package.ts b/packages/apply-release-plan/src/version-package.ts
index c145333..f7c5978 100644
--- a/packages/apply-release-plan/src/version-package.ts
+++ b/packages/apply-release-plan/src/version-package.ts
@@ -59,7 +59,14 @@ export default function versionPackage(
           continue;
         const usesWorkspaceRange = depCurrentVersion.startsWith("workspace:");
         if (usesWorkspaceRange) {
-          depCurrentVersion = depCurrentVersion.substr(10);
+          const workspaceDepVersion = depCurrentVersion.replace(
+            /^workspace:/,
+            ""
+          );
+          depCurrentVersion =
+            workspaceDepVersion === "^" || workspaceDepVersion === "~"
+              ? "*"
+              : workspaceDepVersion;
         } else if (bumpVersionsWithWorkspaceProtocolOnly === true) {
           continue;
         }
diff --git a/packages/get-dependents-graph/src/get-dependency-graph.ts b/packages/get-dependents-graph/src/get-dependency-graph.ts
index 9ac4b44..b01b3ee 100644
--- a/packages/get-dependents-graph/src/get-dependency-graph.ts
+++ b/packages/get-dependents-graph/src/get-dependency-graph.ts
@@ -12,7 +12,7 @@ const DEPENDENCY_TYPES = [
 ] as const;
 
 const getAllDependencies = (config: PackageJSON) => {
-  const allDependencies = new Map();
+  const allDependencies = new Map<string, string>();
 
   for (const type of DEPENDENCY_TYPES) {
     const deps = config[type];
@@ -70,9 +70,15 @@ export default function getDependencyGraph(
       if (!match) continue;
 
       const expected = match.packageJson.version;
+      const usesWorkspaceRange = depVersion.startsWith("workspace:");
 
-      if (depVersion.startsWith("workspace:")) {
-        depVersion = depVersion.substr(10);
+      if (usesWorkspaceRange) {
+        depVersion = depVersion.replace(/^workspace:/, "");
+
+        if (depVersion === "*" || depVersion === "^" || depVersion === "~") {
+          dependencies.push(depName);
+          continue;
+        }
       } else if (opts?.bumpVersionsWithWorkspaceProtocolOnly === true) {
         continue;
       }
diff --git a/packages/git/src/index.ts b/packages/git/src/index.ts
index 8604828..31bc96b 100644
--- a/packages/git/src/index.ts
+++ b/packages/git/src/index.ts
@@ -38,8 +38,6 @@ async function getAllTags(cwd: string): Promise<Set<string>> {
     .trim()
     .split("\n");
 
-  console.log("gonna return set", new Set(tags));
-
   return new Set(tags);
 }
 


Test patch:
diff --git a/packages/apply-release-plan/src/index.test.ts b/packages/apply-release-plan/src/index.test.ts
index d68dcc8..28d6728 100644
--- a/packages/apply-release-plan/src/index.test.ts
+++ b/packages/apply-release-plan/src/index.test.ts
@@ -269,6 +269,71 @@ describe("apply release plan", () => {
         }
       });
     });
+    it("should not update workspace version aliases", async () => {
+      const releasePlan = new FakeReleasePlan(
+        [
+          {
+            id: "some-id",
+            releases: [{ name: "pkg-b", type: "minor" }],
+            summary: "a very useful summary"
+          },
+          {
+            id: "some-id",
+            releases: [{ name: "pkg-c", type: "minor" }],
+            summary: "a very useful summary"
+          },
+          {
+            id: "some-id",
+            releases: [{ name: "pkg-d", type: "minor" }],
+            summary: "a very useful summary"
+          }
+        ],
+        [
+          {
+            changesets: ["some-id"],
+            name: "pkg-b",
+            newVersion: "1.1.0",
+            oldVersion: "1.0.0",
+            type: "minor"
+          },
+          {
+            changesets: ["some-id"],
+            name: "pkg-c",
+            newVersion: "1.1.0",
+            oldVersion: "1.0.0",
+            type: "minor"
+          },
+          {
+            changesets: ["some-id"],
+            name: "pkg-d",
+            newVersion: "1.1.0",
+            oldVersion: "1.0.0",
+            type: "minor"
+          }
+        ]
+      );
+      let { changedFiles } = await testSetup(
+        "workspace-version-alias-dep",
+        releasePlan.getReleasePlan(),
+        releasePlan.config
+      );
+      let pkgPath = changedFiles.find(a =>
+        a.endsWith(`pkg-a${path.sep}package.json`)
+      );
+
+      if (!pkgPath) throw new Error(`could not find an updated package json`);
+      let pkgJSON = await fs.readJSON(pkgPath);
+
+      expect(pkgJSON).toEqual({
+        name: "pkg-a",
+        version: "1.1.0",
+        dependencies: {
+          "pkg-b": "workspace:*",
+          "pkg-c": "workspace:^",
+          "pkg-d": "workspace:~"
+        }
+      });
+    });
     it("should update workspace ranges only with bumpVersionsWithWorkspaceProtocolOnly", async () => {
       const releasePlan = new FakeReleasePlan(
         [
diff --git a/packages/assemble-release-plan/src/index.test.ts b/packages/assemble-release-plan/src/index.test.ts
index 3cdecfe..8fb1e76 100644
--- a/packages/assemble-release-plan/src/index.test.ts
+++ b/packages/assemble-release-plan/src/index.test.ts
@@ -120,6 +120,37 @@ describe("assemble-release-plan", () => {
     expect(releases[1].newVersion).toEqual("1.0.1");
     expect(releases[1].changesets).toEqual([]);
   });
+  it("should assemble the release plan with workspace:^ and workspace:~ dependents", () => {
+    setup.updateDependency("pkg-b", "pkg-a", "workspace:~");
+    setup.updateDependency("pkg-c", "pkg-a", "workspace:^");
+    setup.addChangeset({
+      id: "big-cats-delight",
+      releases: [{ name: "pkg-a", type: "major" }]
+    });
+
+    let { releases } = assembleReleasePlan(
+      setup.changesets,
+      setup.packages,
+      {
+        ...defaultConfig,
+        bumpVersionsWithWorkspaceProtocolOnly: true
+      },
+      undefined
+    );
+
+    expect(releases.length).toEqual(3);
+
+    expect(releases[0].name).toEqual("pkg-a");
+    expect(releases[0].newVersion).toEqual("2.0.0");
+
+    expect(releases[1].name).toEqual("pkg-b");
+    expect(releases[1].newVersion).toEqual("1.0.1");
+    expect(releases[1].changesets).toEqual([]);
+
+    expect(releases[2].name).toEqual("pkg-c");
+    expect(releases[2].newVersion).toEqual("1.0.1");
+    expect(releases[2].changesets).toEqual([]);
+  });
   it("should assemble release plan without dependent through dev dependency", () => {
     setup.updateDevDependency("pkg-b", "pkg-a", "^1.0.0");
     setup.addChangeset({
diff --git a/packages/cli/src/commands/version/version.test.ts b/packages/cli/src/commands/version/version.test.ts
index 8d9018c..15d45bf 100644
--- a/packages/cli/src/commands/version/version.test.ts
+++ b/packages/cli/src/commands/version/version.test.ts
@@ -362,6 +362,39 @@ describe("workspace range", () => {
       }
     ]);
   });
+
+  it("should bump dependant package when bumping a `workspace:^` dependency", async () => {
+    const cwd = f.copy("workspace-alias-range-dep");
+
+    await writeChangeset(
+      {
+        releases: [{ name: "pkg-b", type: "patch" }],
+        summary: "a very useful summary for the change"
+      },
+      cwd
+    );
+    await versionCommand(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:^",
+          "pkg-c": "workspace:~"
+        }
+      },
+      {
+        name: "pkg-b",
+        version: "1.0.1"
+      },
+      {
+        name: "pkg-c",
+        version: "1.0.0"
+      }
+    ]);
+  });
 });
 
 describe("same package in different dependency types", () => {
@@ -1227,4 +1260,37 @@ describe("pre", () => {
       }
     ]);
   });
+  it("should bump dependant of prerelease package when bumping a `workspace:~` dependency", async () => {
+    const cwd = f.copy("workspace-alias-range-dep");
+    await pre(cwd, { command: "enter", tag: "alpha" });
+
+    await writeChangeset(
+      {
+        releases: [{ name: "pkg-c", type: "patch" }],
+        summary: "a very useful summary for the change"
+      },
+      cwd
+    );
+    await versionCommand(cwd, defaultOptions, modifiedDefaultConfig);
+
+    let packages = await getPackages(cwd);
+    expect(packages.packages.map(x => x.packageJson)).toEqual([
+      {
+        name: "pkg-a",
+        version: "1.0.1-alpha.0",
+        dependencies: {
+          "pkg-b": "workspace:^",
+          "pkg-c": "workspace:~"
+        }
+      },
+      {
+        name: "pkg-b",
+        version: "1.0.0"
+      },
+      {
+        name: "pkg-c",
+        version: "1.0.1-alpha.0"
+      }
+    ]);
+  });
 });