Skip to content
This repository has been archived by the owner on Aug 11, 2022. It is now read-only.

'npm link' is creating symlinks with a relative path to modules in global node_modules #13369

Closed
1 of 13 tasks
briankendall opened this issue Jul 13, 2016 · 4 comments
Closed
1 of 13 tasks

Comments

@briankendall
Copy link

briankendall commented Jul 13, 2016

I'm opening this issue because:

  • npm is crashing.
  • npm is producing an incorrect install.
  • npm is doing something I don't understand.
  • Other (see below for feature requests):

What's going wrong?

The command:

npm link ../path/to/linkedModule

...will correctly create a symlink in global node_modules (i.e. /usr/local/lib/node_modules) that has an absolute path to "linkedModule", but will then create a symlink in local node_modules (i.e. ./node_modules) with a relative path to the "linkedModule" link in /usr/local/lib/node_modules. This means that moving or copying the local module will break it.

How can the CLI team reproduce the problem?

Originallly produced in OS X:

mkdir module1; mkdir module2
cd module1
npm init
cd ../module2
npm init
npm link ../module1
ls -l node_modules

The symlink created in node_modules will be to something like:

../../../../../usr/local/lib/node_modules/module1

Instead of:

/usr/local/lib/node_modules/module1

This is breaking a scripts my dev team rely on, as we require modules be able to be copied to other parts of the disk without breaking.

Note that the previous version of npm we were using did not have this behavior, and would use absolute paths. Unfortunately I don't remember which version that was.

supporting information:

  • npm -v prints: 3.10.2
  • node -v prints: v4.4.7
  • npm config get registry prints: https://registry.npmjs.org/
  • OS X
  • Network issues:
    • Geographic location where npm was run: Northeastern United States
    • I use a proxy to connect to the npm registry.
    • I use a proxy to connect to the web.
    • I use a proxy when downloading Git repos.
    • I access the npm registry via a VPN
    • I don't use a proxy, but have limited or unreliable internet access.
  • Container:
    • I develop using Vagrant on Windows.
    • I develop using Vagrant on OS X or Linux.
    • I develop / deploy using Docker.
    • I deploy to a PaaS (Triton, Heroku).
@fantapop
Copy link

Looks like this change is what caused the new behaviour:

b820ed4

  function link (from, to, gently, abs, cb) {
    if (typeof cb !== 'function') {
      cb = abs
      abs = false
    }
    if (typeof cb !== 'function') {
      cb = gently
      gently = null
    }
    if (npm.config.get('force')) gently = false

    to = path.resolve(to)
    var toDir = path.dirname(to)
    var absTarget = path.resolve(toDir, from)
    var relativeTarget = path.relative(toDir, absTarget)
    var target = abs ? absTarget : relativeTarget

    chain(
      [
        [ensureFromIsNotSource, absTarget, to],
        [fs, 'stat', absTarget],
        [rm, to, gently],
        [mkdir, path.dirname(to)],
        [fs, 'symlink', target, to, 'junction']
      ],
      cb
    )
  }

On my box, cb is undefined so abs is getting set to false and relativeTarget is being chosen as the target.

@fantapop
Copy link

fantapop commented Nov 4, 2016

@iarna

This started biting us again so I dug in a bit more. Here is the history of how things happened from what I can tell.

Long ago, prior to version 2.7.5 all links (except on windows) were chosen to be be either absolute or relative based on the length:

  if (process.platform !== "win32") {
    // junctions on windows must be absolute
    target = path.relative(path.dirname(to), from)
    // if there is no folder in common, then it will be much
    // longer, and using a relative link is dumb.
    if (target.length >= from.length) target = from
  }

Of course, if the path has a bunch of ../../ in front of a path from root, the absolute path from the root will always be shorter than the relative path.

Then, in 3cf3b0c it was apparently decided that packages made available via a call to npm link should never be relative paths. A flag here is added to skip the block of code where the relative path could sometimes be triggered.

- if (process.platform !== "win32") {
+  if (!abs && process.platform !== "win32") {
      // junctions on windows must be absolute
      target = path.relative(path.dirname(to), from)
     // if there is no folder in common, then it will be much
     // longer, and using a relative link is dumb.
     if (target.length >= from.length) target = from
}

At this time the flag was only added to calls setting up a linked package:

$ cd shared-package
$ npm link

and not calls to use that linked package:

$ cd my-app
$ npm link shared-package

Note that even though the new flag wasn't being passed to link calls of the second format, the !abs conditional still allowed paths to enter this block and absolute paths were still chosen when it made sense.

Finally in commit b820ed4, the code is changed so that only direct requests for absolute paths are granted them. Otherwise the relative path is chosen even though it may be traversing all the way back up to the root.

It seems like the 2 options for fixing this issue are either:

  1. decide that all links created by npm link should be absolute and update this line to include the abs flag:

https://github.com/npm/npm/blob/latest/lib/link.js#L125

-           [symlink, pp, target],
+          [symlink, pp, target, false, true],
  1. resurrect the line of code that chooses the absolute path when the relative path traverses all the way back to the root.

maybe something like this here (https://github.com/npm/npm/blob/latest/lib/utils/link.js#L61)

-    var target = abs ? absTarget : relativeTarget
+    var target = (abs || absTarget.length <= relativeTarget.length) ? absTarget : relativeTarget

@legodude17
Copy link
Contributor

I like option 1 better. This seems like it will work fine. What was the reasoning for the changes that caused this @zkat?

@npm-robot
Copy link

We're closing this issue as it has gone seven days without activity and without being labeled. If we haven't even labeled in issue in seven days then we're unlikely to ever read it.

If you are still experiencing the issue that led to you opening this or this is a feature request you're still interested in then we encourage you to open a new issue. If this was a support issue, you may be better served by joining package.communty and asking your question there.

For more information about our new issue aging policies and why we've instituted them please see our blog post.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants