Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use real path for module root dir (#6524) #7491

Merged
merged 4 commits into from Jan 8, 2024
Merged

Conversation

grimly
Copy link
Contributor

@grimly grimly commented Jan 5, 2024

Resolves #6524

Intro

I was not able to verify the fix with the mentioned pd command. I applied the same changes to this repository as I did on my local installation of pnpm for the issue to be fixed.

I might not have fixed the original issue so I'll describe here the issue I faced and how I fixed it.

How to reproduce constantly the issue

  • This issue only concerns systems that are NOT case sensitive in their file path (basically Windows).
  • Place yourself in a folder where to create a test project, make sure the parent folder is either not existent or not important
  • Run the following commands (I'm using gitbash but equivalents in powershell or cmd behave the same) :
    [ ! -e parent ] || rm -rf parent
    mkdir -p parent/workspaces
    (
      cd Parent # Bad casing is INTENTIONAL and part of why the bug happen
      echo -e '{\n  "name": "parent",\n  "private": true\n}' > package.json
      echo -e 'packages:\n  - "workspaces/*"' > pnpm-workspace.yaml
      pnpm add -P rxjs
    )

My analysis

At some point in a pnpm add command, There is a mismatch when fetching the manifest of an importer because their rootDir uses the bad Parent folder name instead of the parent real folder name.

I threw some logs right before that call ( console.log([Object.keys(manifestsByPath), rootDir]) ) and I got the following log :

[
  [ 'E:\\code\\parent' ],
  'E:\\code\\Parent'
]

I saw that you fetch the real path earlier in the function to check either or not the folder is part of the workspaces so I added the same and it worked !
Of course my local change was different as I acted on the compiled output and here is what I did :

(Change is at line 190518 of pnpm.cjs)

          const isFromWorkspace = is_subdir_1.default.bind(null, calculatedRepositoryRoot);
          importers = await (0, p_filter_1.default)(importers, async ({ rootDir }) => isFromWorkspace(await fs_1.promises.realpath(rootDir)));
+         importers = await Promise.all(importers.map(async ({ rootDir, ...rest }) => ({ rootDir: await fs_1.promises.realpath(rootDir), ...rest })));
          if (importers.length === 0)
            return true;

Notes

If you have better knowledge of where this rootDir is from, you may want to update to the realpath earlier and save other unidentified bugs related to it.

@grimly grimly requested a review from zkochan as a code owner January 5, 2024 00:35
Copy link

welcome bot commented Jan 5, 2024

💖 Thanks for opening this pull request! 💖
Please be patient and we will get back to you as soon as we can.

@zkochan
Copy link
Member

zkochan commented Jan 6, 2024

I am not sure why this is needed. We run fast-glob on the workspace directory. And workspace directory should already be resolved to real path:

: await findUp([WORKSPACE_MANIFEST_FILENAME, 'pnpm-workspace.yml'], { cwd: await getRealPath(cwd) })

@grimly
Copy link
Contributor Author

grimly commented Jan 7, 2024

So I'll go deeper and find the source.

Here is my analysis on where the faulty values comes from :

Somehow my scenario above, using this current PR don't catch we're at the parent level.
Fixing the issue at the cwd level, pnpm shows the ERR_PNPM_ADDING_TO_ROOT error which is expected.

@@ -291,7 +291,7 @@ export async function getConfig (
...rcOptions.map((configKey) => [camelcase(configKey), npmConfig.get(configKey)]) as any, // eslint-disable-line
...Object.entries(cliOptions).filter(([name, value]) => typeof value !== 'undefined').map(([name, value]) => [camelcase(name), value]),
]) as unknown as ConfigWithDeprecatedSettings
const cwd = betterPathResolve(cliOptions.dir ?? npmConfig.localPrefix)
const cwd = await fs.promises.realpath(betterPathResolve(cliOptions.dir ?? npmConfig.localPrefix))
Copy link
Member

Choose a reason for hiding this comment

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

You can make this operation synchronous as we don't run anything concurrently at this point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I know for a fact you are already asynchronous really early in this function.
Also you do not start additional tasks so it should not be a problem.

Copy link
Member

Choose a reason for hiding this comment

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

That can be changed too. But not in this PR. Sync fs operations in Node.js are faster unless you can run them in parallel to something else.

@zkochan zkochan merged commit 2dfa61a into pnpm:main Jan 8, 2024
14 of 15 checks passed
Copy link

welcome bot commented Jan 8, 2024

Congrats on merging your first pull request! 🎉🎉🎉

zkochan pushed a commit that referenced this pull request Jan 9, 2024
@grimly grimly deleted the fix-6524 branch January 9, 2024 00:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants