Skip to content

pnpm catalog fails to parse version in package.json #1965

@yutakobayashidev

Description

@yutakobayashidev

When using Orval with the catalog feature of pnpm workspace, an error occurs because it cannot parse the version in package.json.

{
  "name": "test",
  "dependencies": {
    "@tanstack/react-query": "catalog:"
  }
}
packages:
  - packages/*

catalog:
  "@tanstack/react-query": "^5.67.2"
~/pnpm-test/packages/test$ pnpm orval
🍻 Start orval v7.6.0 - A swagger client generator for typescript
🛑 petstore - Error: Invalid argument not valid semver ('catalog:' received)

I tried to modify the code so that it parses the pnpm-workspace.yaml, extracts the catalog version, and maps it to package.json as follows. What do you think? There might be adjustments needed, like handling cases where a name is not found in the catalog and retaining the original 'catalog:'. If everything looks good, I can create a pull request.

interface PnpmWorkspace {
  packages?: string[];
  catalog?: Record<string, string>;
}

const loadPnpmWorkspace = async (
  workspace = process.cwd(),
): Promise<PnpmWorkspace | undefined> => {
  const workspacePath = await findUp(['pnpm-workspace.yaml'], {
    cwd: workspace,
  });

  if (!workspacePath) {
    return undefined;
  }

  try {
    const content = await fs.readFile(workspacePath, 'utf8');
    return yaml.load(content) as PnpmWorkspace;
  } catch {
    return;
  }
};

const replaceCatalogReferences = (
  pkg: PackageJson,
  catalog: Record<string, string>,
): PackageJson => {
  const replaceDependencies = (
    deps?: Record<string, string>,
  ): Record<string, string> | undefined => {
    if (!deps) return undefined;

    const newDeps: Record<string, string> = {};
    for (const [name, version] of Object.entries(deps)) {
      if (typeof version === 'string' && version === 'catalog:') {
        newDeps[name] = catalog[name] || version;
      } else {
        newDeps[name] = version;
      }
    }
    return newDeps;
  };

  return {
    ...pkg,
    dependencies: replaceDependencies(pkg.dependencies),
    devDependencies: replaceDependencies(pkg.devDependencies),
    peerDependencies: replaceDependencies(pkg.peerDependencies),
  };
};

export const loadPackageJson = async (
  packageJson?: string,
  workspace = process.cwd(),
): Promise<PackageJson | undefined> => {

  const pkgPath = packageJson
    ? normalizePath(packageJson, workspace)
    : await findUp(['package.json'], { cwd: workspace });

  if (!pkgPath || !fs.existsSync(pkgPath)) {
    return;
  }

  const pkg = await import(pkgPath);

  const pnpmWorkspace = await loadPnpmWorkspace(workspace);
  const catalog = pnpmWorkspace?.catalog;

  if (!catalog) {
    return pkg;
  }

  return replaceCatalogReferences(pkg, catalog);
};

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions