Skip to content

Commit

Permalink
Allow dependecies to use the workspace: protocol and add `"workspac…
Browse files Browse the repository at this point in the history
…eProtocol": "require"` config option (#204)
  • Loading branch information
emmatown committed Mar 15, 2024
1 parent 3c9641c commit b56869a
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 7 deletions.
5 changes: 3 additions & 2 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@0.2.1/schema.json",
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "Thinkmill/manypkg" }
],
"commit": false,
"linked": [],
"access": "public"
"access": "public",
"baseBranch": "main"
}
5 changes: 5 additions & 0 deletions .changeset/forty-needles-doubt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@manypkg/cli": patch
---

Allow dependecies to use the `workspace:` protocol and support adding `"workspaceProtocol": "require"` to the `manypkg` config to require it.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ So that only a single version of an external package will be installed because h

### How it's fixed

The most commonly used range of the dependency is set as the range at every non-peer dependency place it is depended on.
If for some reason, every range is used the same amount of times, they'll all be fixed to the highest version.
The most commonly used range of the dependency is set as the range at every non-peer dependency place it is depended on. If for some reason, every range is used the same amount of times, they'll all be fixed to the highest version.

### Examples

Expand Down Expand Up @@ -287,6 +286,18 @@ Having a `repository` field is helpful so there is a link to the source of a pac

This is fixed by setting the correct URL.

## `workspace:` protocol required

If `"workspaceProtocol": "require"` is set in the `manypkg` config in the root `package.json`, all dependencies on internal packages are required to use the `workspace:` protocol.

### Why it's a rule

If you want to enforce the usage of the `workspace:` protocol.

#### How it's fixed

Dependencies are changed to `workspace:^`. Anything else is also allowed after the `workspace:` though.

## License

Copyright (c) 2023 Thinkmill Labs Pty Ltd. Licensed under the MIT License.
3 changes: 2 additions & 1 deletion packages/cli/src/checks/INTERNAL_MISMATCH.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ export default makeCheck<ErrorType>({
for (let depName in deps) {
let range = deps[depName];
let dependencyWorkspace = allWorkspaces.get(depName);

if (
dependencyWorkspace !== undefined &&
!range.startsWith("npm:") &&
!range.startsWith("workspace:") &&
!semver.satisfies(dependencyWorkspace.packageJson.version, range)
) {
errors.push({
Expand Down
46 changes: 46 additions & 0 deletions packages/cli/src/checks/WORKSPACE_REQUIRED.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { makeCheck, NORMAL_DEPENDENCY_TYPES } from "./utils";
import { Package } from "@manypkg/get-packages";

export type ErrorType = {
type: "WORKSPACE_REQUIRED";
workspace: Package;
depType: typeof NORMAL_DEPENDENCY_TYPES[number];
depName: string;
};

export default makeCheck<ErrorType>({
validate: (workspace, allWorkspaces, root, opts) => {
if (opts.workspaceProtocol !== "require") return [];
let errors: ErrorType[] = [];
for (let depType of NORMAL_DEPENDENCY_TYPES) {
let deps = workspace.packageJson[depType];
if (deps) {
for (let depName in deps) {
if (
allWorkspaces.has(depName) &&
!deps[depName].startsWith("workspace:")
) {
errors.push({
type: "WORKSPACE_REQUIRED",
workspace,
depName,
depType,
});
}
}
}
}

return errors;
},
fix: (error) => {
let deps = error.workspace.packageJson[error.depType];
if (deps && deps[error.depName]) {
deps[error.depName] = "workspace:^";
}
return { requiresInstall: true };
},
print: (error) =>
`${error.workspace.packageJson.name} has a dependency on ${error.depName} without using the workspace: protocol but this project requires using the workspace: protocol, please change it to workspace:^ or etc.`,
type: "all",
});
11 changes: 10 additions & 1 deletion packages/cli/src/checks/__tests__/INTERNAL_MISMATCH.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ describe("internal mismatch", () => {
let errors = makeCheck.validate(dependsOnOne, ws, rootWorkspace, {});
expect(errors.length).toEqual(0);
});
it("should allow workspace: protocol", () => {
let ws = getWS();
let dependsOnOne = getFakeWS("depends-on-one");
dependsOnOne.packageJson.dependencies = {
"pkg-1": "workspace:^",
};
ws.set("depends-on-one", dependsOnOne);
let errors = makeCheck.validate(dependsOnOne, ws, rootWorkspace, {});
expect(errors.length).toEqual(0);
});
it("should error if internal version is not compatible", () => {
let ws = getWS();
let dependsOnOne = getFakeWS("depends-on-one");
Expand Down Expand Up @@ -100,5 +110,4 @@ describe("internal mismatch", () => {
expect(errors.length).toEqual(0);
}
);

});
54 changes: 54 additions & 0 deletions packages/cli/src/checks/__tests__/WORKSPACE_REQUIRED.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { getWS, getFakeWS, getRootWS } from "../../test-helpers";
import makeCheck from "../WORKSPACE_REQUIRED";
let rootWorkspace = getRootWS();

test("should not error if not using workspaceProtocol: require", () => {
let ws = getWS();
let dependsOnOne = getFakeWS("depends-on-one");
dependsOnOne.packageJson.dependencies = {
"pkg-1": "^1.0.0",
};
ws.set("depends-on-one", dependsOnOne);
let errors = makeCheck.validate(dependsOnOne, ws, rootWorkspace, {});
expect(errors.length).toEqual(0);
});

test("should error if using workspaceProtocol: require", () => {
let ws = getWS();
let dependsOnOne = getFakeWS("depends-on-one");
dependsOnOne.packageJson.dependencies = {
"pkg-1": "^1.0.0",
};
ws.set("depends-on-one", dependsOnOne);
let errors = makeCheck.validate(dependsOnOne, ws, rootWorkspace, {
workspaceProtocol: "require",
});
expect(errors).toEqual([
{
type: "WORKSPACE_REQUIRED",
workspace: dependsOnOne,
depName: "pkg-1",
depType: "dependencies",
},
]);
});

test("should fix if using workspaceProtocol: require", () => {
let ws = getWS();
let dependsOnOne = getFakeWS("depends-on-one");
dependsOnOne.packageJson.dependencies = {
"pkg-1": "^1.0.0",
};
ws.set("depends-on-one", dependsOnOne);
const errors = makeCheck.validate(dependsOnOne, ws, rootWorkspace, {
workspaceProtocol: "require",
});
expect(errors).toHaveLength(1);
const result = makeCheck.fix(errors[0], {
workspaceProtocol: "require",
});
expect(dependsOnOne.packageJson.dependencies).toEqual({
"pkg-1": "workspace:^",
});
expect(result).toEqual({ requiresInstall: true });
});
2 changes: 2 additions & 0 deletions packages/cli/src/checks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import MULTIPLE_DEPENDENCY_TYPES from "./MULTIPLE_DEPENDENCY_TYPES";
import ROOT_HAS_DEV_DEPENDENCIES from "./ROOT_HAS_DEV_DEPENDENCIES";
import UNSORTED_DEPENDENCIES from "./UNSORTED_DEPENDENCIES";
import INCORRECT_REPOSITORY_FIELD from "./INCORRECT_REPOSITORY_FIELD";
import WORKSPACE_REQUIRED from "./WORKSPACE_REQUIRED";

export let checks = {
EXTERNAL_MISMATCH,
Expand All @@ -16,4 +17,5 @@ export let checks = {
ROOT_HAS_DEV_DEPENDENCIES,
UNSORTED_DEPENDENCIES,
INCORRECT_REPOSITORY_FIELD,
WORKSPACE_REQUIRED,
};
6 changes: 5 additions & 1 deletion packages/cli/src/checks/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ export const DEPENDENCY_TYPES = [
"peerDependencies",
] as const;

export type Options = { defaultBranch?: string; ignoredRules?: string[] };
export type Options = {
defaultBranch?: string;
ignoredRules?: string[];
workspaceProtocol?: "allow" | "require";
};

type RootCheck<ErrorType> = {
type: "root";
Expand Down

0 comments on commit b56869a

Please sign in to comment.