How does npm3 decides to install flat vs. nested? - npm

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.

Related

NPM module (handontable) not installing sub-dependency(numbro) as project dependencies

What I am trying to do:
So, I am installing a package which has a dependency numbro (another package). For my use case, I need to use that package and initialize it with some value. (set default currency)
However, I am not able to use that package in my code. As from inspecting package-json.lock, I can see that the package isn't there as direct project dependency but is present inside handontable's dependencies.
I thought, I can add numbro directly inside my package.json file to initialize some values but from what it seems, adding it direct and setting default value there doesn't solves the issue.
To check further, I created a dummy angular project with only handontable and handontable/angular to see, if I can reproduce the issue there. However, after npm install, I could use numbro package in the dummy project and the reason being it was present as direct project dependency in package-json.lock file.
The versions of numbro, handontable, handontable/angular and angular are all same in both projects but why is it that in one project I can use the the sub-dependency in my angular project but in another I cannot ?
Original Project:
Dummy Fiddle project:
(installed as direct project dependency)
So, I fixed it with some help from handsontable support team. I deleted node_modules folder and package-json.lock file both.
After which npm install did the trick.
I had tried deleting node_modules folder earlier but doing that alone doesn't resolved the issue.

Enforcing shared dependencies in a monorepo

We have a monorepo using lerna and yarn workspaces. Multiple teams contribute packages to it and there are some common dependencies where we want to force people to use the same version.
What are the options to force all packages to use the same version of specific dependencies? Is there a way to achieve that without writing custom scripts?
I want to prevent this situation:
my-repo/
packages/
pkg-A/
package.json
"address-validator": 1.1.0
pkg-B/
package.json
"address-validator": 1.2.0
I know you can use lerna add or lerna run to add / upgrade in unison, but how to prevent an individual from unknowingly making their package unique?
I just noticed one nice solution to this problem in facebook's create-react-app. They import (all?) external dependencies in the react-dev-utils package and export them from there. Then all the other packages, like react-scripts, import dependencies from react-dev-utils.
This is nice because you only need to worry about using the latest version of one package (e.g. react-dev-utils) in order to use the latest version of all of the things you want to control. Also, it's flexible because you can override one of the dependencies by importing a different version directly.
So it could look like:
my-repo/
packages/
my-deps/
pkg1.js // <--- module.exports = require("pkg1");
package.json
"pkg1": 1.2.0
foo/
index.js // <--- const pkg1 = require("my-deps/pkg1")
package.json
"my-deps": 1.1.0

Some files not installed when my package is a nested dependency, even though they do get installed when it's added as a direct dependency

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.

NPM5, What is the difference of package-lock.json with package.json?

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.

Change entry point to npm dependency

I'm dependending on a library that publishes different module packages to npm (commonjs, ES2015, UMD bundles..) and specifies commonjs/index.js as the main point of the application.
However, I need my app to use the UMD bundle instead so I would need it to be bundles/index.umd.js instead.
I figured I could do this by looking into installed 'node_modules' and changing it directly in the package.json of downloaded library, but thats a very hacky thing to do.
Is there some other way of chanding the main file for the dependency?