In a typical .NET Core project (with Visual Studio 2019) we have a structure like this:
Example/
|-- Example.Common/
| |-- FileExample1.cs
| |-- FileExample2.cs
| `-- Example.Common.csproj
|-- Example.WebAPI/
| |-- Controllers/
| |-- Program.cs
| |-- Startup.cs
| `-- Example.WebAPI.csproj
|-- Example.CLI/
| |-- Program.cs
| `-- Example.CLI.csproj
|-- Example.sln
In this project, both Example.CLI and Example.Web.API references the Example.Common project. The Example.sln file have references to all three *.csproj and each csproj have its own dependencies, that can be of one of these 4 kinds:
<!-- It is a NuGet package (similar to Node.js npm packages) that always resolves from the public nuget.org registry -->
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="2.18.3" />
<!-- It is local, never resolves from registry -->
<ProjectReference Include="../Example.Common/Example.Common.csproj" />
<!-- Resolves from a machine-wide installed DLL -->
<Reference Include="Global.Dependency.Example" />
<!-- Resolves from a local DLL -->
<Reference Include="Local.Dependency.Example">
<HintPath>path/to/Local.Dependency.Example.dll</HintPath>
</Reference>
When running local during development, if I change something in the Common source code and run the CLI project, it automatically rebuilds the Common and copy the DLL to the destination so the CLI can run a version with these latest changes. Even if I have more projects in the solution, it will just rebuild the projects that the CLI depends and if there are any changes on it since the last run.
When deploying, it rebuilds everything and resolve the local dependencies locally. My problem with Node.js and NPM packages is that:
I want do the same thing in a Node.js project, share the common source code to be used in web-api and cli, and I want to break each one of these components of the project in a npm package, so each package can have its own dependencies.
In a Node.js project, I have a similar structure:
example/
|-- common/
| |-- file-example1.js
| |-- file-example2.js
| `-- package.json
|-- web-api/
| |-- controllers/
| |-- index.js
| |-- routes.js
| `-- package.json
|-- cli/
| |-- index.js
| `-- package.json
|-- package.json
The problem is how Node.js and npm/yarn/pnpm resolves the dependencies. I tried using Yarn Workspaces, Lerna, Lerna + Yarn Workspaces, but it seems that all these tools were made for packages that are published to registries, and not to help modularize a single project.
What I want is an easy way to do this:
yarn install every time or creating npm link manuallyI tried:
link: protocol, works good for development, but when I run yarn install --production it tries to resolve using the registry. Won't work because any of my packages will never be publish to any registry.file: protocol, works good for deployment, but for development, when I make changes in the common package, I need to delete the common folder inside node_modules and run yarn install again. Even with file: protocol, I still need a way to build in the correct order, otherwise, npm/yarn would copy just the source code of the dependencies to the destination node_modules folder.Short answer:
lerna bootstrap
lerna run dev
bootstrap will install dependencies to the respective packages.
Example: common should be installed in web-api.
Lerna will install common in web-api as a node_module.
in package.json adding private: true will also ensure lerna publish will not publish those projects.
(Long answer:)
Given the folder directory
example/
|-- common/
| |-- file-example1.js
| |-- file-example2.js
| `-- package.json
|-- web-api/
| |-- controllers/
| |-- index.js
| |-- routes.js
| `-- package.json
|-- package.json
1. Yarn workspaces.
example/package.json
{
"private": true,
"workspaces": ["common", "web-api"]
}
Initialise yarn in respective sub-folders.
Terminal
~/example $ cd ./common
~/example/common $ yarn init -y
~/example/common $ cd ../web-api
~/example/web-api $ yarn init -y
in example/common/package.json
{
"name": "common",
"version": 1.0.0
...
}
in example/web-api/package.json
{
"name" "web-api",
"dependencies": {
"common": "1.0.0"
}
}
Terminal (whilst in web-api dir)
~/example/web-api $ yarn install
In the root directory there should be a node_modules which should include:
web-api/
common/
yarn will create a symlink between the common and webapi from the node_modules folder that is created at the root level.
However, yarnpkg convention is to use
{
"private": true,
"workspaces": ["packages/"]
}
Reference: Ben awad tutorial on yarn workspaces
2. lerna
Lerna uses yarn workspaces under the hood ref but you require a lerna.json config (which can specify the package manager you require npm, yarn...)
lerna.json
{
"packages": [
"packages/*"
],
"version": "0.0.0"
}
There's extra commands in lerna such as:
- lerna diff common which will give you the git diff since the last commit, or.
- lerna run test which will run the test script in each of your packages. (use --scope={common} to only target common test script).
In create-react-app they also include a "changelog" field which i assume would how people would automatically prefix their commit messages.
Reference: Ben awad tutorial on lerna
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With