Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/kind-items-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@manypkg/get-packages": minor
---

Added ignore missing package name to get-packages
83 changes: 82 additions & 1 deletion packages/get-packages/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,41 @@ let runTests = (getPackages: GetPackages) => {

it("should throw an error if a package.json is missing the name field", async () => {
try {
const allPackages = await getPackagesSync(f.copy("no-name-field"));
const allPackages = await getPackages(f.copy("no-name-field"));
expect.fail("Expected error to be thrown");
} catch (err) {
expect(
!!err && typeof err === "object" && "message" in err && err.message
).toBe(
'The following package.jsons are missing the "name" field:\npackages/pkg-a/package.json\npackages/pkg-b/package.json'
);
}
});

it("should not throw an error if a package.json is missing the name field when ignorePackagesWithMissingName is true", async () => {
const allPackages = await getPackages(f.copy("no-name-field"), {
ignorePackagesWithMissingName: true,
});

if (allPackages.packages === null) {
return expect(allPackages.packages).not.toBeNull();
}

// Should still find packages even without name fields
expect(allPackages.packages.length).toEqual(2);
expect(allPackages.tool.type).toEqual("yarn");

// The packages should have undefined names since they're missing the name field
expect(allPackages.packages[0].packageJson.name).toBeUndefined();
expect(allPackages.packages[1].packageJson.name).toBeUndefined();
});

it("should still throw an error if a package.json is missing the name field when ignorePackagesWithMissingName is false", async () => {
try {
const allPackages = await getPackages(f.copy("no-name-field"), {
ignorePackagesWithMissingName: false,
});
expect.fail("Expected error to be thrown");
} catch (err) {
expect(
!!err && typeof err === "object" && "message" in err && err.message
Expand Down Expand Up @@ -183,4 +217,51 @@ describe("getPackages", () => {

describe("getPackagesSync", () => {
runTests(getPackagesSync);

// Additional tests specific to sync version for the new ignorePackagesWithMissingName option
it("should throw an error if a package.json is missing the name field (sync)", () => {
try {
const allPackages = getPackagesSync(f.copy("no-name-field"));
expect.fail("Expected error to be thrown");
} catch (err) {
expect(
!!err && typeof err === "object" && "message" in err && err.message
).toBe(
'The following package.jsons are missing the "name" field:\npackages/pkg-a/package.json\npackages/pkg-b/package.json'
);
}
});

it("should not throw an error if a package.json is missing the name field when ignorePackagesWithMissingName is true (sync)", () => {
const allPackages = getPackagesSync(f.copy("no-name-field"), {
ignorePackagesWithMissingName: true,
});

if (allPackages.packages === null) {
return expect(allPackages.packages).not.toBeNull();
}

// Should still find packages even without name fields
expect(allPackages.packages.length).toEqual(2);
expect(allPackages.tool.type).toEqual("yarn");

// The packages should have undefined names since they're missing the name field
expect(allPackages.packages[0].packageJson.name).toBeUndefined();
expect(allPackages.packages[1].packageJson.name).toBeUndefined();
});

it("should still throw an error if a package.json is missing the name field when ignorePackagesWithMissingName is false (sync)", () => {
try {
const allPackages = getPackagesSync(f.copy("no-name-field"), {
ignorePackagesWithMissingName: false,
});
expect.fail("Expected error to be thrown");
} catch (err) {
expect(
!!err && typeof err === "object" && "message" in err && err.message
).toBe(
'The following package.jsons are missing the "name" field:\npackages/pkg-a/package.json\npackages/pkg-b/package.json'
);
}
});
});
21 changes: 16 additions & 5 deletions packages/get-packages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ export class PackageJsonMissingNameError extends Error {
/**
* Configuration options for `getPackages` and `getPackagesSync` functions.
*/
export interface GetPackagesOptions extends FindRootOptions {}
export interface GetPackagesOptions extends FindRootOptions {
/**
* This prevents the getPackages function to fail on files like `{ "type": "module" }`.
*/
ignorePackagesWithMissingName?: boolean;
}

/**
* Given a starting folder, search that folder and its parents until a supported monorepo
Expand All @@ -46,7 +51,7 @@ export async function getPackages(
if (!tool) throw new Error(`Could not find ${monorepoRoot.tool} tool`);

const packages: Packages = await tool.getPackages(monorepoRoot.rootDir);
validatePackages(packages);
validatePackages(packages, options?.ignorePackagesWithMissingName);
return packages;
}

Expand All @@ -63,11 +68,14 @@ export function getPackagesSync(
if (!tool) throw new Error(`Could not find ${monorepoRoot.tool} tool`);

const packages: Packages = tool.getPackagesSync(monorepoRoot.rootDir);
validatePackages(packages);
validatePackages(packages, options?.ignorePackagesWithMissingName);
return packages;
}

function validatePackages(packages: Packages) {
function validatePackages(
packages: Packages,
ignorePackagesWithMissingName = false
): void {
const pkgJsonsMissingNameField: string[] = [];

for (const pkg of packages.packages) {
Expand All @@ -76,7 +84,10 @@ function validatePackages(packages: Packages) {
}
}

if (pkgJsonsMissingNameField.length > 0) {
if (
pkgJsonsMissingNameField.length > 0 &&
ignorePackagesWithMissingName === false
) {
pkgJsonsMissingNameField.sort();
throw new PackageJsonMissingNameError(pkgJsonsMissingNameField);
}
Expand Down