npm / cli

the package manager for JavaScript
https://docs.npmjs.com/cli/
Other
8.44k stars 3.14k forks source link

[BUG] `node_modules/.package-lock.json` contains `lockfileVersion` 2 instead of 3 #3971

Open secondfry opened 2 years ago

secondfry commented 2 years ago

Is there an existing issue for this?

Current Behavior

npm install creates node_modules/.package-lock.json with "lockfileVersion": 2,.

Expected Behavior

As per https://docs.npmjs.com/cli/v7/configuring-npm/package-lock-json#lockfileversion npm v7 and later should set lockfileVersion of that hidden lockfile to 3.

2: The lockfile version used by npm v7, which is backwards compatible to v1 lockfiles. 3: The lockfile version used by npm v7, without backwards compatibility affordances. This is used for the hidden lockfile at node_modules/.package-lock.json, and will likely be used in a future version of npm, once support for npm v6 is no longer relevant.

Steps To Reproduce

mkdir test
cd test
npm init -y
npm install vue
grep lockfileVersion node_modules/.package-lock.json

Environment

lukekarrys commented 2 years ago

I agree that this is confusing wording but I think this is only a documentation issue.

In the docs you quoted, "lockfile version" is talking about the actual contents of the lockfile. In your example you can see the difference here:

❯ cat package-lock.json | jq '.dependencies | length'
21
❯ cat node_modules/.package-lock.json | jq '.dependencies | length'
0

The hidden lockfile is a "version 3 lockfile" because it lacks the dependencies array that exists for backwards compatibility.

But lockfileVersion: 2 is the config value present when the lockfile was created which gets stored in both lockfiles as you noticed.

❯ cat node_modules/.package-lock.json | jq '.lockfileVersion'
2

❯ cat package-lock.json | jq '.lockfileVersion'
2

You can set npm config set lockfile-version=3 --location=project and it will be set in your .npmrc and subsequent installs will set lockfileVersion: 3 in both lockfiles and remove the dependencies array from package-lock.json.

I'm going to leave this open with the action item:

secondfry commented 2 years ago

@lukekarrys

How is that a documentation issue if you yourself point to differences in those files and formats they use? Do you really expect others to test package-lock.json for length of dependencies field to determine file format instead of checking literal number of lockfileVersion? It undermines existence of lockfileVersion entirely.

I'm pretty sure that package-lock should contain lockfileVersion: 2 if it still has dependencies field and lockfileVersion: 3 if it does not anymore.

lockfileVersion: 2 has been in circulation for such long time that everyone has some kind of expectations around its format and node_modules/.package-lock.json currently breaks that "API".

lukekarrys commented 2 years ago

The lockfileVersion value in all (.)package-lock.json files is the config setting used to generate the actual tree. This is valuable for the edge case where the package-lock gets deleted but we are still able to generate a v2 shrinkwrap file if that's what was used originally (https://github.com/npm/cli/issues/3962).

Do you really expect others to test package-lock.json for length of dependencies?

The hidden lockfile is always a v3 lockfile regardless of what config setting was used, but we preserve the setting in both files. So there's never a reason to check what format it is. It's always v3.

However, you can use the lockfileVersion value to determine the format of package-lock.json.

secondfry commented 2 years ago

@lukekarrys

However, you can use the lockfileVersion value to determine the format of package-lock.json.

Well, currently I can't and if both package-lock.json and node_modules/.package-lock.json will have same lockfileVersion, but different contents I still won't be able to.

Let me explain the situation. I never knew this:

The lockfileVersion value in all (.)package-lock.json files is the config setting used to generate the actual tree.

For me lockfileVersion was a tool to judge the format of (.)package-lock.json, not the tree it generates. This conclusion is also backed by current documentation.

So I currently maintain in-house tool which parses package-lock.json to check installed and requested versions of packages. I've set internal checks for lockfileVersion so I support only versions up to 2, because 2 is backwards compatible to 1.

I do believe it is not only me who has such systems in place. So what could happen wrong:

  1. You bump lockfileVersion to 3 without changing the format. Here fix is simple to increase "compatible version" to 3.
  2. You remove backwards compatibility but lockfileVersion stays as 2. This is whats going on with node_modules/.package-lock.json right now.

So my proposal here is just to do what is already written in the documentation – just set that node_modules/.package-lock.json version to 3.

ljharb commented 2 years ago

The one inside node_modules is just for npm’s internal use tho; it’s not meant to be parsed by anyone else. You can run npm install --package-lock-only and get a lockfile if that’s something you need to ingest.

lukekarrys commented 2 years ago

So my proposal here is just to do what is already written in the documentation – just set that node_modules/.package-lock.json version to 3.

That would cause a regression on the issue I linked to above: https://github.com/npm/cli/issues/3962.

I do believe it is not only me who has such systems in place. So what could happen wrong:

I'm not sure I fully understand what you are trying to achieve in those steps, but the lockfileVersion of node_modules/.package-lock.json should stay in sync after reification:

npm i

cat package-lock.json | jq .lockfileVersion
# 2

cat node_modules/.package-lock.json | jq .lockfileVersion
# 2

# manually set lockfileVersion: 3
sed -i '' 's/"lockfileVersion": 2/"lockfileVersion": 3/' package-lock.json

npm i

cat package-lock.json | jq .lockfileVersion
# 3

cat node_modules/.package-lock.json | jq .lockfileVersion
# 3

It's still a better idea to use an .npmrc file because then your setting will persist regardless of what you do to the actual tree on disk.