Skip to content

Commit

Permalink
feat: save exactly what the user asked for
Browse files Browse the repository at this point in the history
BREAKING CHANGE: "npm install" will now save the exact range the user gives if one is given.

When adding a new dep to the tree, if a range is given that exact range
is what will be saved to your package.json.  If a dist-tag is used, that
version will be saved with `save-prefix`.  A bare package name is
treated like the dist-tag `latest`.

This also means that `npm update --save` is no longer possible, as that
feature was predicated off of the old behavior of always choosing the
save-prefix and the current version being saved.
  • Loading branch information
wraithgar committed Aug 15, 2023
1 parent 8ecbcb9 commit 39610ba
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 148 deletions.
8 changes: 3 additions & 5 deletions docs/lib/content/commands/npm-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ packages.
If no package name is specified, all packages in the specified location (global
or local) will be updated.

Note that by default `npm update` will not update the semver values of direct
dependencies in your project `package.json`, if you want to also update
values in `package.json` you can run: `npm update --save` (or add the
`save=true` option to a [configuration file](/configuring-npm/npmrc)
to make that the default behavior).
Note that `npm update` will not update the values of direct dependencies
in your project `package.json`. You will need to use [`npm
install`](/commmands/npm-install) to do that.

### Example

Expand Down
2 changes: 2 additions & 0 deletions workspaces/arborist/lib/arborist/build-ideal-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,8 @@ module.exports = cls => class IdealTreeBuilder extends cls {
if (isTag) {
// translate tag to a version
spec = npa(`${mani.name}@${mani.version}`)
// let reify know it was originally a tag to inform how to save it in package.json
spec.fromTag = true
}
spec.name = mani.name
}
Expand Down
58 changes: 25 additions & 33 deletions workspaces/arborist/lib/arborist/reify.js
Original file line number Diff line number Diff line change
Expand Up @@ -1228,52 +1228,46 @@ module.exports = cls => class Reifier extends cls {
process.emit('time', 'reify:save')

const updatedTrees = new Set()
const updateNodes = nodes => {
for (const { name, tree: addTree } of nodes) {
const updateNodes = specs => {
for (const { raw: rawSpec, fromTag, name, tree: addTree } of specs) {
// addTree either the root, or a workspace
const edge = addTree.edgesOut.get(name)
const pkg = addTree.package
const req = npa.resolve(name, edge.spec, addTree.realpath)
const { rawSpec, subSpec } = req

const spec = subSpec ? subSpec.rawSpec : rawSpec
const child = edge.to

// if we tried to install an optional dep, but it was a version
// that we couldn't resolve, this MAY be missing. if we haven't
// blown up by now, it's because it was not a problem, though, so
// just move on.
if (!child || !addTree.isTop) {
if (!edge.to || !addTree.isTop) {
continue
}

let newSpec
const pkg = addTree.package
const req = npa.resolve(name, edge.spec, addTree.realpath)

const spec = req.subSpec || req

let newSpec = req.saveSpec

// True if the dependency is getting installed from a local file path
// In this case it is not possible to do the normal version comparisons
// as the new version will be a file path
const isLocalDep = req.type === 'directory' || req.type === 'file'
if (req.registry) {
const version = child.version
const prefixRange = version ? this[_savePrefix] + version : '*'
// if we installed a range, then we save the range specified
// if it is not a subset of the ^x.y.z. eg, installing a range
// of `1.x <1.2.3` will not be saved as `^1.2.0`, because that
// would allow versions outside the requested range. Tags and
// specific versions save with the save-prefix.
const isRange = (subSpec || req).type === 'range'

let range = spec
if (
!isRange ||
spec === '*' ||
subset(prefixRange, spec, { loose: true })
) {
range = prefixRange
// Tags and versions save with the save-prefix.
// Ranges save exactly as requested
// NOTE: In the past ranges were saved with the widest range between
// either exactly what they asked or {_savePrefix}{version}
newSpec = '*' // XXX is this '*' possible?
if (edge.to.version) {
newSpec = `${this[_savePrefix]}${edge.to.version}`
}
// Not from a tag, not currently a tag, and not a bare name
if (!fromTag && !spec.type !== 'tag' && rawSpec !== spec.name) {
newSpec = spec.rawSpec
}
if (req.type === 'alias') {
newSpec = `npm:${spec.name}@${newSpec}`
}

const pname = child.packageName
const alias = name !== pname
newSpec = alias ? `npm:${pname}@${range}` : range
} else if (req.hosted) {
// save the git+https url if it has auth, otherwise shortcut
const h = req.hosted
Expand Down Expand Up @@ -1301,8 +1295,6 @@ module.exports = cls => class Reifier extends cls {
const rel = relpath(addTree.realpath, p).replace(/#/g, '%23')
newSpec = `file:${rel}`
}
} else {
newSpec = req.saveSpec
}

if (options.saveType) {
Expand Down Expand Up @@ -1350,7 +1342,7 @@ module.exports = cls => class Reifier extends cls {
const exactVersion = node => {
for (const edge of node.edgesIn) {
try {
if (semver.subset(edge.spec, node.version)) {
if (subset(edge.spec, node.version)) {
return false
}
} catch {
Expand Down
Loading

0 comments on commit 39610ba

Please sign in to comment.