Skip to content

Improve resolution of catalog dependencies#168

Open
royalty37 wants to merge 3 commits into0x80:mainfrom
royalty37:improved-catalog-handling
Open

Improve resolution of catalog dependencies#168
royalty37 wants to merge 3 commits into0x80:mainfrom
royalty37:improved-catalog-handling

Conversation

@royalty37
Copy link
Copy Markdown

When using the forceNpm flag with a project inside a pnpm workspace, catalog dependencies were not being resolved for dependencies of packages within my monorepo causing lockfile generation to fail.

Update to read catalogs from 'pnpm-workspace.yaml'.

Refactored resolveCatalogDependencies to extract catalog loading into a new
loadCatalogSource helper that:

  1. Checks pnpm-workspace.yaml first — reads catalog/catalogs fields if the
    file exists and contains them
  2. Falls back to package.json — reads catalog/catalogs from root or
    workspaces.catalog (Bun format)
// Before: only read from package.json
const rootManifest = await readTypedJson(path.join(workspaceRootDir, "package.json"));
const flatCatalog = rootManifest.catalog || rootManifest.workspaces?.catalog;

// After: check pnpm-workspace.yaml first, then fall back to package.json
async function loadCatalogSource(workspaceRootDir: string): Promise<CatalogSource> {
  const workspaceYamlPath = path.join(workspaceRootDir, "pnpm-workspace.yaml");

  if (await fs.pathExists(workspaceYamlPath)) {
    const rawContent = await fs.readFile(workspaceYamlPath, "utf-8");
    const yamlConfig = yaml.parse(rawContent) as CatalogSource | null;
    if (yamlConfig?.catalog || yamlConfig?.catalogs) {
      return { catalog: yamlConfig.catalog, catalogs: yamlConfig.catalogs };
    }
  }

  // Fall back to package.json (Bun format)
  const rootManifest = await readTypedJson(path.join(workspaceRootDir, "package.json"));
  return {
    catalog: rootManifest.catalog ?? rootManifest.workspaces?.catalog,
    catalogs: rootManifest.catalogs ?? rootManifest.workspaces?.catalogs,
  };
}

…endencies of included packages within the same monorepo
Copilot AI review requested due to automatic review settings April 2, 2026 03:18
@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 2, 2026

PR Summary

Medium Risk
Changes dependency version resolution logic by introducing YAML-based catalog loading and caching, which can affect generated manifests/lockfiles across workspaces if parsing or catalog precedence differs from current behavior.

Overview
Improves resolveCatalogDependencies to resolve catalog: specifiers from pnpm-workspace.yaml (pnpm format) before falling back to package.json (Bun format), with a cached loadCatalogSource helper and a warning on invalid YAML.

Adds comprehensive Vitest coverage for default and named catalogs, missing entries, YAML parse failures, and fallback/unchanged behaviors.

Written by Cursor Bugbot for commit 79a12a9. This will update automatically on new commits. Configure here.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves catalog dependency resolution in manifest processing by loading pnpm-style catalogs from pnpm-workspace.yaml (when present) before falling back to Bun-style catalogs in package.json, preventing failures when isolating projects inside pnpm workspaces.

Changes:

  • Added loadCatalogSource() helper to read catalogs from pnpm-workspace.yaml first, then fall back to package.json.
  • Updated resolveCatalogDependencies() to use the new catalog source loader.
  • Added a comprehensive Vitest suite for catalog resolution behavior across pnpm and Bun formats.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/lib/manifest/helpers/resolve-catalog-dependencies.ts Adds YAML-based catalog loading from pnpm-workspace.yaml with fallback to package.json.
src/lib/manifest/helpers/resolve-catalog-dependencies.test.ts Introduces tests covering pnpm-workspace and package.json catalog resolution scenarios.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/manifest/helpers/resolve-catalog-dependencies.ts Outdated
Comment thread src/lib/manifest/helpers/resolve-catalog-dependencies.ts
Comment thread src/lib/manifest/helpers/resolve-catalog-dependencies.test.ts Outdated
Comment thread src/lib/manifest/helpers/resolve-catalog-dependencies.test.ts
@0x80
Copy link
Copy Markdown
Owner

0x80 commented Apr 8, 2026

@royalty37 I'm curious, why would you want to use forceNpm if you're using a PNPM workspace? Are you deploying to a target that does not support PNPM?

With the forceNpm you're loosing locked versions since it's not a perfect translation...

@0x80
Copy link
Copy Markdown
Owner

0x80 commented Apr 8, 2026

@royalty37 In any case I think the PR seems fine. If you format the code so it passes checks I'll merge it

Copilot AI review requested due to automatic review settings April 19, 2026 21:57
@royalty37
Copy link
Copy Markdown
Author

@0x80 Sorry for delay. Have fixed the formatting issue.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to +62
describe("resolveCatalogDependencies", () => {
let tmpDir: string;

afterEach(async () => {
if (tmpDir) {
await fs.remove(tmpDir);
}
});
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mocks (mockLogger.debug/mockLogger.warn) aren’t reset between tests, so assertions like toHaveBeenCalledWith can become order-dependent as more cases are added. Add a beforeEach that calls vi.clearAllMocks() (or at least resets the logger mocks) to keep each test independent.

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +88
catalogSourceCache.set(workspaceRootDir, loadPromise);
return loadPromise;
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catalogSourceCache stores the in-flight loadPromise, but if reading/parsing ends up throwing (e.g., missing/invalid root package.json or other transient FS error), the rejected promise will stay cached and all subsequent calls for the same workspaceRootDir will immediately rethrow without retry. Consider deleting the cache entry on rejection (or only caching on success) so a later call can recover if the underlying files become readable.

Suggested change
catalogSourceCache.set(workspaceRootDir, loadPromise);
return loadPromise;
const cachedLoadPromise = loadPromise.catch((error) => {
catalogSourceCache.delete(workspaceRootDir);
throw error;
});
catalogSourceCache.set(workspaceRootDir, cachedLoadPromise);
return cachedLoadPromise;

Copilot uses AI. Check for mistakes.
lodash: "^4.0.0",
});
});

Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test suite covers YAML-only and package.json-only scenarios, but it doesn’t currently assert the documented precedence when both pnpm-workspace.yaml and package.json define catalogs. Add a test where both files exist with different versions for the same dependency and verify the YAML value wins (since loadCatalogSource checks YAML first).

Suggested change
it("prefers pnpm-workspace.yaml over package.json when both define the same catalog dependency", async () => {
tmpDir = await createTempWorkspace({
workspaceYaml: {
packages: ["packages/*"],
catalog: {
react: "^18.3.1",
},
},
packageJson: {
name: "root",
version: "0.0.0",
pnpm: {
catalog: {
react: "^18.2.0",
},
},
},
});
const result = await resolveCatalogDependencies(
{ react: "catalog:" },
tmpDir,
);
expect(result).toEqual({ react: "^18.3.1" });
});

Copilot uses AI. Check for mistakes.
@0x80
Copy link
Copy Markdown
Owner

0x80 commented Apr 21, 2026

@cursor review

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 21, 2026

Bugbot couldn't run

Bugbot is not enabled for your user on this team.

Ask your team administrator to increase your team's hard limit for Bugbot seats or add you to the allowlist in the Cursor dashboard.

@0x80
Copy link
Copy Markdown
Owner

0x80 commented Apr 21, 2026

@cursor review

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 21, 2026

Bugbot couldn't run

Bugbot is not enabled for your user on this team.

Ask your team administrator to increase your team's hard limit for Bugbot seats or add you to the allowlist in the Cursor dashboard.

@0x80
Copy link
Copy Markdown
Owner

0x80 commented Apr 22, 2026

@cursor review

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 22, 2026

Bugbot couldn't run

Bugbot is not enabled for your user on this team.

Ask your team administrator to increase your team's hard limit for Bugbot seats or add you to the allowlist in the Cursor dashboard.

@0x80
Copy link
Copy Markdown
Owner

0x80 commented Apr 22, 2026

@royalty37 please also address the issues that copilot found

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants