From 1bdb3687b6cbde6e1128253c8916bd4b2f2bc250 Mon Sep 17 00:00:00 2001 From: Gar Date: Thu, 1 Sep 2022 09:01:31 -0700 Subject: [PATCH] fix: standardize `x` `x@` and `x@*` BREAKING CHANGE: `x` and `x@` now return the same spec as `x@*` From https://github.com/npm/npm-package-arg/issues/45: Right now, `name@` and `name` are parsed with `{type:'tag', fetchSpec: 'latest'}`, but `name@*` is parsed as `{type: 'range'}`. But since `''` is a valid semver range, it should be parsed the same as `*`. This also saves npm-package-arg from guessing the default tag, which currently poses some semantic hazards. npm (via npm-pick-manifest) will prefer its default tag if given a range that includes it. But `name@latest` should always and only resolve to that specific version in the dist-tags. So npm-pick-manifest and pacote have to detect this and do some extra work to figure out if `latest` was actually specified or just guessed as a default. Closes npm/statusboard#460 --- lib/npa.js | 7 ++++--- test/basic.js | 26 +++++++++++++------------- test/realize-package-specifier.js | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/lib/npa.js b/lib/npa.js index cd1932b..10af67b 100644 --- a/lib/npa.js +++ b/lib/npa.js @@ -39,11 +39,12 @@ function npa (arg, where) { spec = arg } else if (nameEndsAt > 0) { name = namePart - spec = arg.slice(nameEndsAt + 1) + spec = arg.slice(nameEndsAt + 1) || '*' } else { const valid = validatePackageName(arg) if (valid.validForOldPackages) { name = arg + spec = '*' } else { spec = arg } @@ -113,7 +114,7 @@ function Result (opts) { this.name = undefined this.escapedName = undefined this.scope = undefined - this.rawSpec = opts.rawSpec == null ? '' : opts.rawSpec + this.rawSpec = opts.rawSpec || '' this.saveSpec = opts.saveSpec this.fetchSpec = opts.fetchSpec if (opts.name) { @@ -383,7 +384,7 @@ function fromAlias (res, where) { function fromRegistry (res) { res.registry = true - const spec = res.rawSpec === '' ? 'latest' : res.rawSpec.trim() + const spec = res.rawSpec.trim() // no save spec for registry components as we save based on the fetched // version, not on the argument so this can't compute that. res.saveSpec = null diff --git a/test/basic.js b/test/basic.js index b543c5e..13b4cce 100644 --- a/test/basic.js +++ b/test/basic.js @@ -44,10 +44,10 @@ t.test('basic', function (t) { name: '@foo/bar', escapedName: '@foo%2fbar', scope: '@foo', - rawSpec: '', + rawSpec: '*', saveSpec: null, - fetchSpec: 'latest', - type: 'tag', + fetchSpec: '*', + type: 'range', }, '@foo/bar@': { @@ -55,10 +55,10 @@ t.test('basic', function (t) { name: '@foo/bar', escapedName: '@foo%2fbar', scope: '@foo', - rawSpec: '', + rawSpec: '*', saveSpec: null, - fetchSpec: 'latest', - type: 'tag', + fetchSpec: '*', + type: 'range', }, '@foo/bar@baz': { @@ -113,11 +113,11 @@ t.test('basic', function (t) { registry: true, name: 'bar', escapedName: 'bar', - type: 'tag', + type: 'range', raw: 'bar', - rawSpec: '', + rawSpec: '*', saveSpec: null, - fetchSpec: 'latest', + fetchSpec: '*', }, }, @@ -366,10 +366,10 @@ t.test('basic', function (t) { name: 'git', type: 'alias', subSpec: { - type: 'tag', + type: 'range', registry: true, name: 'not-git', - fetchSpec: 'latest', + fetchSpec: '*', }, raw: 'git@npm:not-git', }, @@ -587,9 +587,9 @@ t.test('basic', function (t) { foo: { name: 'foo', escapedName: 'foo', - type: 'tag', + type: 'range', saveSpec: null, - fetchSpec: 'latest', + fetchSpec: '*', raw: 'foo', }, diff --git a/test/realize-package-specifier.js b/test/realize-package-specifier.js index 63c9483..03d6651 100644 --- a/test/realize-package-specifier.js +++ b/test/realize-package-specifier.js @@ -8,7 +8,7 @@ test('realize-package-specifier', function (t) { result = npa('a.tar.gz', '/test/a/b') t.equal(result.type, 'file', 'local tarball') result = npa('d', '/test/a/b') - t.equal(result.type, 'tag', 'remote package') + t.equal(result.type, 'range', 'remote package') result = npa('file:./a.tar.gz', '/test/a/b') t.equal(result.type, 'file', 'local tarball') result = npa('file:./b', '/test/a/b')