file:
specifiersspecifier
refers to the value part of the package.json
's dependencies
object. This is a semver expression for registry dependencies and
URLs and URL-like strings for other types.
file:
specifier is either an absolute path (eg /path/to/thing
, d:\path\to\thing
):
file:///absolute/path
with any number of leading slashes
being treated as a single slash. That is, file:/foo/bar
and
file:///foo/bar
reference the same package.../path/to/thing
, path\to\subdir
). Leading
slashes on a file specifier will be removed, that is 'file://../foo/barreferences the same package as same as
file:../foo/bar`. The latter is
considered canonical.file:
specifier points is:
.tar
, .tar.gz
or .tgz
containing
<dir>/package.json
.package.json
Relative specifiers are relative to the file they were found in, or, if provided on the command line, the CWD that the command was run from.
An absolute specifier found in a package.json
or npm-shrinkwrap.json
is
probably an error as it's unlikely to be portable between computers and
should warn.
A specifier provided as a command line argument that is on a different drive
is an error. That is, npm install file:d:/foo/bar
is an error if the
current drive is c
. The point of this rule is that if we can't produce a
relative path then it's an error.
On the command line, plain paths are allowed. These paths can be ambiguous
as they could be a path, a plain package name or a github shortcut. This
ambiguity is resolved by checking to see if either a directory exists that
contains a package.json
. If either is the case then the specifier is a
file specifier, otherwise it's a registry or github specifier.
A specifier is considered to match a dependency on disk when the realpath
of the fully resolved specifier matches the realpath
of the package on disk.
When saving to both package.json
and npm-shrinkwrap.json
they will be
saved using the file:../relative/path
form, and the relative path will be
relative to the project's root folder. This is particularly important to
note for the npm-shrinkwrap.json
as it means the specifier there will
be different then the original package.json
(where it was relative to that
package.json
).
When shrinkwrapping file specifiers, the contents of the destination
package's node_modules
WILL NOT be included in the shrinkwrap. If you want to lock
down the destination package's node_modules
you should create a shrinkwrap for it
separately.
This is necessary to support the mono repo use case where many projects file
to the same package. If each project included its own npm-shrinkwrap.json
then they would each have their own distinct set of transitive dependencies
and they'd step on each other any time you ran an install in one or the other.
NOTE: This should not have an effect on shrinkwrapping of other sorts of shrinkwrapped packages.
File-type specifiers pointing at a .tgz
or .tar.gz
or .tar
file will
install it as a package file in the same way we would a remote tarball. The
checksum of the package file should be recorded so that we can check for updates.
File-type specifiers that point at directories will necessarily not do
anything for fetch
and extract
phases.
The symlink should be created during the finalize
phase.
The preinstall
for file-type specifiers MUST be run AFTER the
finalize
phase as the symlink may be a relative path reaching outside the
current project root and a symlink that resolves in .staging
won't resolve
in the package's final resting place.
If the module is inside the package root that we're running the install for then dependencies of the linked package will be hoisted to the top level as usual.
If the module is outside the package root then dependencies will be installed inside
the linked module's node_modules
folder.
Removal should remove the symlink.
Removal MUST NOT remove the transitive dependencies IF they're installed in
the linked module's node_modules
folder.
In listings they should not include a version as the version is not
something npm
is concerned about. This also makes them easily
distinguishable from symlinks of packages that have other dependency
specifiers.
If you had run:
npm install --save file:../a
And then run:
npm ls
You would see:
example-package@1.0.0 /path/to/example-package
└── a → file:../a
example-package@1.0.0 /path/to/example-package
+-- a -> file:../a
Of note here: No version is included as the relevant detail is WHERE the package came from, not what version happened to be in that path.
Local specifiers should only show up in npm outdated
if they're missing
and when they do, they should be reported as:
Package Current Wanted Latest Location
a MISSING LOCAL LOCAL example-package
If a dependency with a local specifier is already installed then npm update
shouldn't do anything. If one is missing then it should be
installed as if you ran npm install
.