After updating NPM to version 5, I found package-lock.json file with package.json.
What is the difference between this two files?
What are the advantages of package-lock.json?
A package.json file: lists the packages that your project depends on. allows you to specify the versions of a package that your project can use using semantic versioning rules.
According to the npm docs,
package-lock.json is automatically generated for any operations where npm modifies either the node_modules tree, or package.json . It describes the exact tree that was generated, such that subsequent installs are able to generate identical trees, regardless of intermediate dependency updates.
This file is intended to be committed into source repositories, and serves various purposes:
Describe a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.
Provide a facility for users to "time-travel" to previous states of node_modules without having to commit the directory itself.
To facilitate greater visibility of tree changes through readable source control diffs.
Basically package-lock.json is used to optimize the installation process by allowing npm to skip repeated metadata resolutions for previously-installed packages.
Before npm 5.x.x, package.json was the source of truth for a project. What lived in package.json was law. npm users liked this model and grew very accustomed to maintaining their package file. However, when package-lock was first introduced, it acted contrary to how many people expected it to. Given a pre-existing package and package-lock, a change to the package.json (what many users considered the source of truth) was not reflected in the package-lock.
Example: Package A, version 1.0.0 is in the package and package-lock. In package.json, A is manually edited to version 1.1.0. If a user who considers package.json to be the source of truth runs npm install, they would expect version 1.1.0 to be installed. However, version 1.0.0 is installed, despite the fact that v1.1.0 is listed is the package.json.
Example: A module does not exist in the package-lock, but it does exist in the package.json. As a user who looks to package.json as the source of truth, I would expect for my module to be installed. However since the module is not present in package-lock, it isn’t installed, and my code fails because it cannot find the module.
Read more about package-lock.json in the Official npm Documentation!
package.json records only your direct dependencies and their versions.
package-lock.json records not only your direct dependencies and exact versions, but also those of all dependencies of your dependencies - the entire dependency tree, in other words, with exact versions.
It's the fact that package-lock.json records the exact versions of all dependencies of a project, including sub-dependencies, that ensures that builds will be identical each time. (This is why npm ci bases its build on package-lock.json, not package.json.)
Builds based on package.json (as with npm i) cannot guarantee that all sub-dependencies are the exact same versions each build (e.g., if the subdependency of one of your dependencies releases an update, but the version of your direct dependency doesn't change), even if exact version numbers for direct dependencies are specified in the package.json.
Related
The Webpack/Vue ecosystem is a very fragile one, with minor updates to loaders regularly breaking the build. It's basically a dedicated job to curate a working Webpack config together with a list of the exact dependency versions that are needed to make it work.
This Webpack config can easily be kept in a repository and then copied to many different projects and imported in their local webpack.config.js because webpack.config.js is just Javascript.
I'd like to do the same thing with package.json, i.e. have the curated list of dependencies in a separate file and when running npm install have them added to any other dependencies a project might have.
Do npm or yarn or any other external tools offer such a functionality?
Are you specifically trying to use a js file? If so, I don't have an answer, but if json is enough, you can just make an node package that just has the dependencies you want in it. Someone that includes your package will then pull in all the dependencies listed in your package because npm pulls in the dependencies of a project's dependencies.
Also see https://stackoverflow.com/a/55483109/14144258
I have to npm packages, one (say, "parent") depending on the other ("child"). My child package has a number of .js files, say main.js and other.js. The former is listed as main in the child's package.json.
Both of these should be included in the package. When I inspect the package generated with npm pack, it looks fine. When I add the published package as a dependency of the parent and inspect its contents in parent/node_modules/, both files are also present, as expected. I then publish parent as a separate package, with the child listed as a dependency.
However, when I then start a new project with the parent as a dependency, and I then install that project's node_modules/client, I see main.js but not other.js! This happens regardless of whether I install it through Yarn or npm. What could be the cause of this?
Well... There was a deeper root cause, so this is probably not going to help anybody, but just in case. The problem was that a different version of this package was getting installed when it was a nested dependency. The reason for this is that the dependency was a prerelease version, specified like ^0.0.1-<commit hash>. Since the commit hash can start with a number, I had an older version whose commit hash actually started with a higher number, and thus ended up getting installed when the parent specified the child dependency using a caret ^, but not when I directly added the child dependency.
Specifying the exact version as a dependency in the parent solved the issue.
Each dependency optionally has a dependencies property and a requires property. These are not necessarily both present, and their contents do not necessarily overlap. I assume requires holds anything listed in the package.json for that package, but I can't find any reference which properly describes the dependencies property.
Packages at each level are shared by other packages on that branch of the dependency tree. If a child package uses a different version of a package then it will be duplicated at that point of the tree.
I've deduced this with a little poking around the contents of a package-lock.json and by inferring from npm dedupe.
My project depends on angular2 beta.6 and another project that depends on angular2 beta.0.
package.json for my project
"dependencies": {
"angular2": "2.0.0-beta.6",
"another-project": "0.0.1"
}
package.json for another-project
"dependencies": {
"angular2": "2.0.0-beta.0",
}
When I npm install my project, it installs angular2 twice:
node_modules/angular2 (beta.6)
node_modules/another-project/angular2 (beta.0)
Trying to understand how npm3 decides to nest angular2 beta.0. Is it because both are called angular2 and hence they can not both sit at the top level?
Trying to understand how npm3 decides to nest angular2 beta.0. Is it because both are called angular2 and hence they can not both sit at the top level?
Yes, this is correct. Node code require's a module by name, using code like this:
require('angular2');
Node itself is not aware of the different versions, that's the job of npm, so it just uses whatever module matches in the require path first, relying on matching directory names.
npm accommodates this by installing specific versions in directories for each module when a conflict occurs, so that the require path will include that first.
Yes, it is because of the beta.0. Because npm has found another version of angular2 on the global level, it will install it locally.
npm3 will install globally dependencies only if there is no other versions of the dependencies on a higher level.
Here is a little example I've found :
[node_modules]
dep A v1.0
dep B v1.0
dep A v1.0 (uses root version)
dep C v1.0
dep A v2.0 (this version is different from the root version, so it will be an nested installation)
Flat dependencies were introduced in npm v3. The documentation can be found here https://docs.npmjs.com/how-npm-works/npm3.
To answer your question from the docs
However, since B v1.0 is already a top-level dep, we cannot install B v2.0 as a top level dependency. npm v3 handles this by defaulting to npm v2 behavior and nesting the new, different, module B version dependency under the module that requires it -- in this case, module C.
So order matters. The module you install first will get it's dependency on the top level. Subsequent modules will have nested dependencies.
you can run npm dedupe to remove nested module dependencies if they exist in the top level.
I'm coming from a background much more familiar with composer. I'm getting gulp (etc) going for the build processes and learning node and how to use npm as I go.
It's very odd (again, coming from a composer background) that a composer.lock-like manifest is not included by default. Having said that, I've been reading documentation on [shrinkwrap], [npm-lockdown], and [npm-seal]. ...and the more documentation I read, the more confused I become as to which I should be choosing (everyone thinks their way is the best way). One of the issues I notice is that npm-seal hasn't changed in 4 years and npm-lockdown in 8 months -- this all leads me to wonder if this because it's not needed with the newest version of npm...
What are the benefits / drawbacks of each?
In what cases would I use one over another in Project A, but use a different one in Project B?
How will each impact our development workflow?
PS: Brownie points if you include the most basic implementation example for each. ;)
npm shrinkwrap is the most standard way how to lock your dependencies. And yes, npm install does not create it by default which is a pity and it is something that npm creators definitely should change.
npm-lockdown is trying to do the same things as npm shrinkwrap, there are two minor points in which npm-lockdown is better: it handles optional dependencies better and it validates checksums of the package:
https://www.npmjs.com/package/lockdown#related-tools
Both these features seem not so relevant for me; I'm quite happy with npm shrinkwrap: For example, npmjs guarantees that once you upload certain package at certain version, it stays immutable - so checking sha checksums is not so hot (I've never encountered an error caused by this).
seal is meant to be used together with npm shrinkwrap. It adds the 'checksum checking' aspect. It looks abandoned and quite raw.
Good question - I'm going to skip everything but shrinkwrap because it is the de-facto way to do this, per NPM's docs.
Long story short the npm-shrinkwrap.json file is akin to your lock files you are used to in every other package manager, though NPM allows different versions of the same package to play nice together by isolation - literally scoping and copying different entire versions to node_modules at different levels of the tree. If two projects that are parent-child to each other use the exact same version, NPM will copy the version to only the parent and the child will traverse up the tree to find the package.
Best practice is simply to update package.json for your direct dependencies, run npm install, verify that things are working while developing, then run npm shrinkwrap when you are just about to commit and push. NOTE: make sure to rm npm-shrinkwrap.json before running npm install during active development - if your direct dependencies have changed, you want package.json to be used, and not the lock! Also include node_modules in your .gitignore or equivalent in your source control system. Then, when you are deploying and getting to run the project, run npm install like normal. If npm finds an npm-shrinkwrap.json file, it will use that to recursively pull all locked modules, and it will ignore package.json in both your project and all dependent projects.
You might find shrinkpack useful – it checks in the tarballs which npm install downloads and bundles them into your repository, before finally rewriting npm-shrinkwrap.json to point at that local bundle instead.
This way, your project is totally locked down, completely available offline, and much quicker to install.