Skip to content

refactor: improve type checking when finding workspace packages#8214

Merged
zkochan merged 19 commits intomainfrom
gluxon/workspace-find-packages-patterns
Jun 17, 2024
Merged

refactor: improve type checking when finding workspace packages#8214
zkochan merged 19 commits intomainfrom
gluxon/workspace-find-packages-patterns

Conversation

@gluxon
Copy link
Copy Markdown
Member

@gluxon gluxon commented Jun 16, 2024

PR Stack

Problem

After #8213, code within pnpm is expected to use the patterns argument when calling findWorkspacePackages. This avoids the pnpm-workspace.yaml file from being read multiple times, which could cause strange consistency issues if the file is modified while pnpm is running. It also just feels silly to re-read this file when it's already in-memory.

However, because the patterns argument is optional, it's easy to forget to pass in a value for this argument.

Changes

Making patterns required when calling findWorkspacePackages.

  • findWorkspacePackages should be used within the pnpm repo.
  • findWorkspacePackagesUsingManifest is now a convenience function that code outside of pnpm can call. This function continues to read pnpm-workspace.yaml automatically.

@gluxon gluxon changed the title Gluxon/workspace find packages patterns refactor: improve type checking when finding workspace packages Jun 16, 2024
const allProjects = await findWorkspacePackages(workspaceDir, {
const workspaceManifest = await readWorkspaceManifest(workspaceDir)
const allProjects = await findWorkspacePackagesUsingPatterns(workspaceDir, {
patterns: workspaceManifest?.packages ?? ['.', '**'],
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I added ?? ['.', '**'] here since that's what plugin-commands-completion is currently doing, but this feels like a bug to me. If there's no pnpm-workspace.yaml, I would think allProjects should be empty?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You may still use pnpm i -r even outside a workspace.

linkWorkspacePackages: false,
saveWorkspaceProtocol: 'rolling',
workspaceDir: process.cwd(),
workspaceManifest: { dir: process.cwd(), contents: workspaceManifest },
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's a bit annoying that tests calling the install/add/import handlers need to pass in workspaceManifest directly.

One way to fix this would be to better distinguish between a workspaceManifest field that's (a) not yet read or (b) read, but empty. We would then call readWorkspaceManifest in the former case.

But that's a lot of complexity to make a few tests easier to write. I decided not to make the workspaceManifest config field more complicated.

pnpm/src/main.ts Outdated
engineStrict: config.engineStrict,
nodeVersion: config.nodeVersion ?? config.useNodeVersion,
patterns: cliOptions['workspace-packages'] ?? config.workspaceManifest?.contents.packages,
patterns: cliOptions['workspace-packages'] ?? config.workspaceManifest?.contents.packages ?? ['.', '**'],
Copy link
Copy Markdown
Member Author

@gluxon gluxon Jun 16, 2024

Choose a reason for hiding this comment

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

This uncovered a potential problem with pnpm install --recursive. This is a case where a missing pnpm-workspace.yaml actually means everything is part of the workspace. I think this is a bug?

The current behavior of findWorkspacePackages() is:

  1. Use opts.patterns if provided.
  2. Use pnpm-workspace.yaml's packages field if the file is present.
  3. Use ['.', '**'] if pnpm-workspace.yaml is not present.

Although the patterns argument here on line 203 is non-null, undefined could get passed in here since noUncheckedIndexedAccess is false.

Is it intended that pnpm install --recursive treats all folders with package.json files as workspace packages even when pnpm-workspace.yaml isn't on disk? That's the current behavior here. I added ?? ['.', '**'] to make this more clear and to satisfy the required patterns argument.

@gluxon gluxon marked this pull request as ready for review June 16, 2024 06:33
@gluxon
Copy link
Copy Markdown
Member Author

gluxon commented Jun 16, 2024

Opening this for review, but it's not required for catalogs. I wanted to see what this would look like if we made improvements to the type safety of patterns. Curious for thoughts.

@zkochan
Copy link
Copy Markdown
Member

zkochan commented Jun 16, 2024

Why don't we just make it a required option and remove the possibility of reading the workspace manifest from within findWorkspacePackages?

@gluxon
Copy link
Copy Markdown
Member Author

gluxon commented Jun 16, 2024

That was roughly my intention with this PR.

There were 3 approaches I was considering:

  1. Require patterns in findWorkspacePackages.
  2. Require patterns in findWorkspacePackages, but leave around a findWorkspacePackagesUsingManifest function that reads pnpm-workspace.yaml for you. pnpm wouldn't use this, but other people using @pnpm/workspace.find-packages might find it convenient.
  3. Add a new function (i.e. findWorkspacePackagesUsingPatterns) that requires patterns, leaving findWorkspacePackages as-is.

I took the 3rd approach, but happy to change to either (1) or (2).

They all roughly have the effect of requiring patterns for code within pnpm, but have different requirements for people externally consuming the @pnpm/workspace.find-packages NPM package. I chose (3) since it was the one that didn't require a new major version of @pnpm/workspace.find-packages.

@gluxon gluxon force-pushed the gluxon/hoist-manifest-read-to-config branch 2 times, most recently from 9136a66 to a26819b Compare June 16, 2024 19:24
Base automatically changed from gluxon/hoist-manifest-read-to-config to main June 16, 2024 23:26
@zkochan
Copy link
Copy Markdown
Member

zkochan commented Jun 16, 2024

This needs a rebase.

@gluxon
Copy link
Copy Markdown
Member Author

gluxon commented Jun 17, 2024

Sounds good. I'll rebase now.

Did you have a preference on whether we should introduce a new function for convenience of @pnpm/workspace.find-packages users?

@gluxon gluxon force-pushed the gluxon/workspace-find-packages-patterns branch from 0560060 to 3858dd6 Compare June 17, 2024 05:51
@gluxon
Copy link
Copy Markdown
Member Author

gluxon commented Jun 17, 2024

Latest force push contains a few notable changes:

  • A few if (opts.workspaceDir) conditions needed to be updated to if (opts.workspaceDir && opts.workspacePackagePatterns).
  • I decided to switch the names. The findWorkspacePackages now requires patterns and there's a new findWorkspacePackageUsingManifest for external NPM package users.
  • I had to revert the change that made patterns optional for filterPackagesFromDir. fix: use --workspace-packages CLI over pnpm-workspace.yaml in more cases #8215 (comment)

pnpm/src/main.ts Outdated
engineStrict: config.engineStrict,
nodeVersion: config.nodeVersion ?? config.useNodeVersion,
patterns: config.workspacePackagePatterns,
patterns: config.workspacePackagePatterns ?? ['.', '**'],
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Here's my prior comments on this here:

I think the ?? ['.', '**'] is ugly and should be [] instead, but this would be a behavior change and breaks a few tests. I'm looking into fixing these tests, but wanted to push up a rebase for this PR in the meantime.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have made patterns optional, not default value is needed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

One thing I was worried about with patterns being optional is developers in the future writing code in pnpm forgetting to pass in a value when calling findWorkspacePackages. Everything might work as expected when testing, but in production packages might get included from the default filter ['.', '**'] that shouldn't, and then users will get confused.

I think that's a general footgun with optional parameters.

Comment on lines 127 to 130
if (opts.workspaceDir && opts.workspacePackagePatterns) {
workspacePackagesArr = await findWorkspacePackages(opts.workspaceDir, {
...opts,
patterns: opts.workspacePackagePatterns,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe just add a default value for patterns in findWorkspacePackages. Otherwise the tests have to be updated with workspacePackagePatterns, which isn't even used in most cases because allProjects is passed.

Copy link
Copy Markdown
Member Author

@gluxon gluxon Jun 17, 2024

Choose a reason for hiding this comment

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

I noticed that above too. #8214 (comment)

A lot of tests have a common options setup that defaults values from @pnpm/config. Perhaps we could set those to ['.', '**'] for tests.

@zkochan zkochan requested a review from weyert as a code owner June 17, 2024 13:05
@zkochan zkochan merged commit 75a98e1 into main Jun 17, 2024
@zkochan zkochan deleted the gluxon/workspace-find-packages-patterns branch June 17, 2024 15:34
@gluxon gluxon mentioned this pull request Jun 26, 2024
18 tasks
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.

2 participants