[BUG] npm init @usr/foo fails when @user/create-foo is unpublished but installed globally #5596
Closed
2 tasks done
Labels
Bug
thing that needs fixing
Needs Triage
needs review for next steps
Release 8.x
work is associated with a specific npm 8 release
Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
I have written an npm initializer @usr/create-foo and installed it globally with
npm install -g
but not published.I then attempt to use this initializer with
npm init @usr/foo
but this fails after attempting to find the package in the registry, where it doesn't exist because it hasn't been published.npm init @usr/foo
is translated tonpm exec @usr/create-foo
as described in the docs. But this fails to find the bin from the globally installed package.package.json of the initializer includes:
Documentation for package.json includes: If you have a single executable, and its name should be the name of the package, then you can just supply it as a string.
Here, the package name is
@usr/create-foo
so the name of the executable should be@usr/create-foo
and this is consistent with the executable thatnpm exec @usr/create-foo
attempts to execute.The problem is that
npm install -g .
in the directory of the initializer package doesn't install an executable@usr/create-foo
as the documentation indicates it should. Rather, it installscreate-foo
. Therefore,npm exec @usr/create-foo
can't find the executable: it hasn't been installed as such but rather with the different namecreate-foo
.The problem is compounded by the fact that
npm exec @usr/create-foo
looks for a locally installed package but it doesn't look for a globally installed package. It goes straight to trying to download the package, ignoring the globally installed package.I hacked libnpmexec/lib/index.js to look for the globally installed package. It gets the manifest OK and derives the name of the executable:
create-foo
rather than@usr/create-foo
. But still it doesn't work because next it checks that every package in the manifest list (there is only one) is installed locally - again ignoring the globally installed package.A bit more hacking and I could have it working.
I can simply ignore the 'sugar' of
npm init @usr/foo
and run the bin directly:create-foo
works fine although@usr/create-foo
does not, despite the documentation for package.json.Rather than bin being set to a string value in package.json I also tried:
But even with the executable name given explicitly rather than implied by the package name,
npm install -g
installs the executable ascreate-foo
rather than@usr/create-foo
.Expected Behavior
If package name is
@usr/create-foo
and the value of bin is a string, then the executable name should be@usr/create-foo
, according to the package.json documentation. While this is what I expect based on the documentation, I think it is problematic: attempting to execute@usr/create-foo
will look forcreate-foo
in a local subdirectory@usr
rather finding it in /bin which is in the path - where the globally installed package executables are installed.npm init @usr/foo
andnpm exec @usr/create-foo
should find the globally installed package and use it if there is no locally installed package of that name. Having found it, they should resolve the executable name consistently with wherenpm install -g
installs it, despite that being inconsistent with the documentation. Given that the package is globally installed, neither command should attempt to download it from the registry.Installing the executable to the bin directory with the scope refix removed is problematic. Consider the case of several scoped packages named
@scope/create
for different scopes, for use withnpm init @scope
. These would be translated tonpm exec @scope/create
which appears distinct for each scope but if the scope prefix is removed then each executable is simplycreate
and the executables from the various scoped packages will collide. Yet one of the motivations for using scoped packages is exactly to avoid name collisions. Stripping the scope prefix eliminates this advantage. On the other hand, keeping it with the separator character being the same as the system path separator is problematic. Then the executable would be installed into a subdirectory of the bin directory or it would be installed with a name that includes the path separator, which would have to be escaped. Neither is very appealing. The npm exec command at least could find the executable in the .bin directory of the installed package, rather than the global bin directory. That would avoid name conflicts and allownpm init @scope
to work as expected. I assume this is effectively what happens when the package is downloaded from the registry: it is downloaded to an npx cache and the executable is run from the copy in that cache, never installed to the global bin directory where it might collide with other such executables.Steps To Reproduce
@usr/create-foo
with an executable, either with bin value being a string - the path of the script that implements the executable, or an object where key is the same as the package name and value is the same path.npm install -g .
in the root directory of the packagecreate-foo
rather than@usr/create-foo
npm init @usr/foo
andnpm exec @usr/create-foo
- both will fail after failing attempted download of the package from the registry, despite it being installed globally.Environment
The text was updated successfully, but these errors were encountered: