Lerna does not support dependencies at the top level? - npm

I am in the process of switching my monorepo (back) from yarn (with workspaces) to lerna/npm, because yarn is too slow and unstable. However, I made an surprising discovery. With the following trivial package.json:
{
"devDependencies": { "lerna": "^2.11.0" },
"dependencies": { "typescript": "^2.9.1" }
}
and an empty lerna.json (in other words, no packages at all), then when I run
$ lerna bootstrap
it does not install anything at all in any top-level node_modules directory. And if for some reason I have a node_modules directory with no .bin subdirectory, then lerna bootstrap fails to create or populate the .bin subdirectory.
Is lerna not designed to actually specify top-level packages which are to be installed (along with their binaries in .bin)? I do notice that if I try lerna add on a lerna monorepo with no packages, it complains that "lerna WARN No packages found in scope where tslint can be added."
I could not find anything related to this in the documentation. With yarn/workspaces, I was using the ability to install global (top-level) versions of things like TypeScript for use in my build scripts while maintaining control over the version installed.

From the Lerna docs:
You can add the root as a managed location (in the packages array of lerna.json) - if that's something you need. This would cause lerna to link root's dependencies to your packages' directories, run postinstall script along with the others, etc.

Related

package-lock.json in npm workspaces

Given an npm workspace with the following structure
workspace
package.json
packages
package-a
package.json
package-b
package.json
When I run an install command in package-a this will generate a package-lock.json file in the root of the workspace but not in the package.json file itself.
Is there a way to also generate it in the packages?
I don't know if this solves your problem, but you can specifie the folder in which you would install with --prefix
npm install --prefix ./install/here
you can use the lerna tool to manage your workspace and install dependencies in each package. you can generate package-lock.json files in each package in your workspace.
The Original Tool for JavaScript Monorepos. Monorepo means a repository with multiple packages.
lerna.js.org
I hope this answer will show you the right direction.
In most cases, running npm install within that package directory should do the job. But as you said that this is creating a global package-lock.json. This might be because the package you are installing might be specifying the global path using the prefix field.
The "prefix" field, specifies the location where the package's dependencies should be installed.
So one thing you can do is to go to the package.json in package-a and then either remove the prefix field from the package.json file OR set its value as following :
{
"name": "my-package",
"version": "1.0.0",
"prefix": "./",
"dependencies": {
...
}
}
Now when you run npm install it should install the packages locally and make a local 'package-lock.json`.

Allow local project to depend on local lerna packages

I have a lerna repo for a project under development. It has several packages that depend on each other. To make development easier, none of the packages are published and they depend on the latest version of each other.
Directory tree
foo/
packages/
core/
package.json
errors/
package.json
foo/packages/core/package.json
{
...
dependencies: {
"#foo/errors": "*"
}
}
I have another project, bar, that I'm using to test the lerna project. Currently I'm linking to its dependencies using a local file: dependency:
bar/package.json
{
...
dependencies: {
"#foo/core": "../foo/packages/core"
}
}
This approach has given me a world of trouble.
Using npm, I'm constantly hit with ENOENT .DELETE errors. Removing my package-lock.json and reinstalling has taken years off my life.
Using yarn, I've been unable to yarn install in bar. Yarn follows the file: dependency to #foo/core, sees that it depends on #foo/errors and doesn't know about lerna's symlink. This causes it to fail, telling me it can't find #foo/errors.
This has made writing actual code for this project secondary to this mess of dependency management.
How can I make this (I feel fairly simple?) project structure work? Open to lerna/yarn/npm/pnpm/shell scripts/MS DOS at this point.
You should be able to accomplish this with npm link. Although I have not tried this using a local dependency that is not published on npm.
Directory tree
foo/
packages/
core/
package.json
errors/
package.json
bar/
package.json
foo/packages/core/package.json
{
...
dependencies: {
"#foo/errors": "*"
}
}
bar/package.json
{
...
dependencies: {
"#foo/core": "../foo/packages/core"
}
}
Run the following commands
cd foo
npx lerna clean
npx lerna bootstrap --hoist
npm run build # command to build your projects
cd packages/core
npm link
cd ../../../bar
npm i
npm link #foo/core
Removing package-lock.json files usually does more harm then good! And about not being able to find #foo/errors, if you ran npm bootstrap, #foo/core should be symlinked to #foo/errors. One possibility could be that your lerna scripts are using npm while you where running install/link with yarn.
Can you move your lerna up to a directory which hold both 'foo' and 'bar'?
Is that possible?
root/
foo/
packages/
core/
package.json
errors/
package.json
bar/
package.json
lerna.json
And in your lerna file, you can add your repos to packages
{
"lerna": "2.9.0",
"packages": [
"foo/packages/*",
"bar/",
],
}
Under slightly different conditions where one of the npm modules you are working is not part of your lerna repo, you can use lerna to exec the npm link command.
npx lerna exec -- npm link <npm_package_name>
This will npm link the external package in all of your lerna modules.
This should not be confused with lerna link which will do something similar for all submodules in your your lerna repo and is the current solution to the question.
Use can try like that:
foo/packages/core/package.json
{
...
dependencies: {
"#foo/errors": "file:../errors"
}
}
bar/package.json
{
...
dependencies: {
"#foo/core": "file:../foo/packages/core"
}
}

Do yarn workspaces work with npm, too?

I checked out a repo which uses yarn instead of npm as build tool.
in the package.json, it defines workspaces to deal with multiple sub-projects:
{
"workspaces": [
"packages/*"
],
"dependencies": [],
"devDependencies": [
// long list
]
}
As a result, the root package.json does not contain any runtime dependency. Just the packages/*/package.json contain those.
To compile (and start in dev mode) I do:
yarn install
yarn start
I have found no documentation, that workspaces is also recognized and correctly used by npm.
Is there a way to make it work with npm, too?
Now that npm v7.0.0 is out, npm supports workspaces. You can manage multiple packages from within a singular top-level, root package. See more at https://github.blog/2020-10-13-presenting-v7-0-0-of-the-npm-cli/
Your workflows will not get npm v7.0.0 by default unless you install it using npm install -g npm#7.
Managing dependencies in a monorepo is not supported with npm. The Lerna package is used to manage JavaScript projects with cross dependencies. I believe Lerna uses Yarn under the hood, but with Yarn as your project's package manager, this feature is supported out of the box.
Here is a repo that might be helpful to see how Yarn and Lerna work together: https://github.com/Quramy/lerna-yarn-workspaces-example
npm has plans to implement workspaces somewhere along the v7, hopefully. They've written about it in their blog and there's an accepted proposal.
As of 2020-08-11 Workspaces are available in the v7 beta.
RFC 26 First phase of workspaces support is added. This changes
npm’s behavior when a root project’s package.json file contains a
workspaces field.
https://blog.npmjs.org/post/626173315965468672/npm-v7-series-beta-release-and-semver-major
You can view and download the beta here:
https://github.com/npm/cli/tags
NPM version 7 support workspace management. Now we can manage our workspace dependencies using npm workspace.
More on the workspace support can be found here official site here.
https://docs.npmjs.com/cli/v7/using-npm/workspaces
If you are looking for a simple example follow this link
https://github.com/pravanjan/npm-workspace-example/tree/master
Tested using node v16.6.1
npm 7.20.3

dependencies and devDependencies when using webpack

Does it make sense to put any modules into package.json dependencies when I use webpack?
When I want to develope a package, I use git clone <url> then npm install, then npm installs all dependencies and devDependencies from package.json file and it makes sense.
When I'm end-user and I just want to install some package into my node_modules to use it in my project, I run npm install package-name, then npm installs package-name with only its dependencies, and it makes sense too.
But does it make sense to put any modules into dependencies when I use webpack? The webpack will bundle all dependencies into eg. bundle.js, so, for me, there is no need to install dependencies then (while they're included into bundle.js file).
Let's assume that I put all neccessary modules into devDependencies (keep the dependencies object empty) for my project: my-project, bundle it with webpack and publish:
the developer-user will use git clone <url to my_project>, then run npm install, then npm will install devDependencies from package.json (and ommit empty dependencies object), then it is ready to develope.
the end-user will use npm install my-project, then npm will install my-project, do not install devDependencies (because this is for production) and do not install dependencies (because dependencies object in package.json remain empty). Putting anything into dependencies would double the dependencies: both the dependencies would be installed, and the same dependencies would be accessible in the bundle.js file.
Am I right?
You are correct that there may be no dependencies once it's been transpiled with webpack. However, some packages are multi-purpose and may be used in multiple ways, so in some circumstances dependencies may still be required.
If you look at the package.json specification, there are two possible entry points, 'main' and 'browser'. There is also the proposed 'module' entry point. It is currently under discussion about how to handle these in webpack and users seem to want webpack to prioritize them as module > browser > main, however browser is currently used by webpack first.
The idea behind prioritizing them in the order module > browser > main is presumably that browsers could use the pre-transpiled stuff directly in "browser", whereas another project calling require() or include() on your package would use non-transpiled code from the "module" entry. The "module" entry code could contain modern JavaScript with new features and the project/package requiring it could then transpile it to their own specifications, using "browserslist" for example.
I found this question because I was wondering the same thing...

Need more elaboration regarding, --save-dev

I see --save-dev mentioned in Gulp tutorials and from what I see, it adds npm functionality to a project's dependency.
But what does that mean exactly? Is that significant when the project gets moved from one machine to another?
Thank you for any clarification of --save-dev importance with Gulp.
In a npm package there 2 types of dependencies: the production ones and the development ones.
{
"dependencies": {
// .. a list of production dependencies
// i.e. angular or express
},
"devDependencies": {
// .. a list of dependencies strictly needed only in development mode
// i.e. gulp or grunt
}
}
You need the former to make the application run in production. The latter are used when in development mode, so everything around the build system, minification, etc...
Gulp, as a building system, is more a devDependency by nature, than a production dependency. This is why you often find in Gulp/Gulp plugins tutorials things are:
$ npm install --save-dev gulp
That --save-dev flag will put the installed dependency you're asking in the devDependencies bucket while using --save sets the dependency in the dependencies (production) one.