Skip to content

Commit

Permalink
RFC: registry: dependency specifiers
Browse files Browse the repository at this point in the history
Fix: #275
Close: #217
  • Loading branch information
isaacs committed Feb 6, 2021
1 parent 592826c commit 7d9fef3
Showing 1 changed file with 160 additions and 0 deletions.
160 changes: 160 additions & 0 deletions accepted/0000-registry-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# The `registry:` Dependency Specifier

## Summary

Add a dependency specifier which defines a registry base url, package name,
and optionally SemVer range or dist-tag.

## Motivation

Occasionally, users wish to use multiple npm registries. For example, they
may have some packages hosted on the public npm registry, and others within
a private registry on their company's intranet, or provided by a company
like GitHub, Jfrog, Sonatype, or others.

Currently, it is possible to map a scope to a given registry, and all
packages starting with that scope will be published to and installed from
the defined registry:

```ini
; .npmrc file
@company:registry = https://npm-registry.my-company.com
```

However, this does not address the following use cases:

- Users publish a package to one registry as a "staging" area, and then
later wish to "upgrade" it to the public npm registry for broader
distribution. Some code should fetch from the internal registry (for
example, for testing and validation prior to promotion), but this is
challenging unless the private registry also can serve _any_ public npm
package.

- Users have a set of unscoped package dependencies, some of which come
from the public registry, and others which have patches applied to them
(either to the code, or to the packument to add warnings via the
`deprecated` field for example). Again, this is only feasible right now
by making the internal registry capable of proxying the entire set of npm
packages.

- Alias package specifiers cannot point to any registries other than the
primary `--registry` configuration. It would be useful in many scenarios
to be able to alias a package to a copy found on a different registry.

- Migrating packages from one registry to another is particularly
challenging, requiring downloading the tarball locally and then
re-uploading it. It would be much simpler to script such migrations by
being able to do `npm publish <pkg from src registry> --registry=<dest
registry>`.

- Using multiple different registries in the best of cases always requires
using a `.npmrc` file for configuration, which is a challenge in some
environments, and always adds a layer of indirection. It would be more
straightforward at times to specify the registry right in the
package.json file or command line.

## Detailed Explanation

A new dependency specifier is added:

```
registry:<registry url>#<package name>[@<specifier>]
```

Where:

- `<registry>` is a fully qualified URL to an npm registry, which may not
contain a `hash` portion,
- `<package name>` is the (scoped or unscoped) name of the package to
resolve on the registry, and
- `<specifier>` is an optional dist-tag, version, or range.

If `<specifier>` is omitted, then it defaults to the `tag` config (or
`defaultTag` internal optional), which defaults to `latest`.

### Saving

When a package is installed using a registry specifier, it *must* be saved
using a registry specifier.

### Alias Specifiers

Alias specifiers desugar into registry specifiers with the default
configured registry url.

For example, the alias dependency spec `npm:foo@latest` will be equivalent
to `registry:https://registry.npmjs.org#foo@latest`.

### Deduping

Two packages with the same name and version which come from different
registries *must not* be deduped against one another unless:

- If either has a defined `integrity` value, then their `integrity` values
must match.
- If neither has a defined `integrity` value, they will be considered
dedupable if their `resolved` values match (for example, `registry-a`
lists the tarball in `registry-b` as its `dist.tarball` url.)

### Specifying Package Name

The `<package name>` portion is always required, even when it would match
the `name` portion of a complete named specifier.

For example, `foo@registry:https://url.com#[email protected]` is acceptable.
`foo@registry:https://url.com#1.x` is not valid, and will attempt to alias
`foo` to the `1.x` package.

This avoids the hazards of attempting to infer whether the `hash` portion
of the url is a SemVer, dist-tag, or package name.

### Examples:

- on the command line:

```bash
# the name may be specified
npm install forked@registry:https://internal.local#forked
# but is not required
npm install registry:https://internal.local#[email protected]
```

- in a `package.json` file

```json
{
"dependencies": {
"aliased": "registry:https://internal.com#[email protected]",
"forked": "registry:https://other-internal.com#[email protected]",
"patched": "registry:https://security-provider.com#patched@^1.4 || 2"
}
}
```

## Rationale and Alternatives

Use cases described are challenging to address in any other way.

Initial proposal used a `:` character to delimit the url from the package
specifier, but this is a poor choice, since `:` appears in registry urls.

[RFC PR #217](https://github.com/npm/rfcs/pull/217) addressed some of the
use cases described by defining a registry per _package_ underneath a
scope. However, analysis and discussion uncovered security concerns that
would make that approach unwise to implement. Packages with `registry:`
specifiers in their dependencies will fail to install on older npm versions
that do not support the new spec type, so there is no chance of fetching
from the _wrong_ registry.

## Implementation

- Add support for `registry:` specifiers in `npm-package-arg` module. **This
is a breaking change**, but adding `registry:` specifier support to
npm/cli is SemVer-minor.
- Upgrade all modules depending on `npm-package-arg` to ensure that they
will behave properly with `registry:` specifiers. (Note: this is most of
npm.)

## Prior Art

Alias specifiers already present in npm.

0 comments on commit 7d9fef3

Please sign in to comment.