Project Structure
The Standard defines 3 possible project structure.
Monorepo
This is common for large projects, where multiple small projects and external dependencies are integrated.
mono-dev
is installed into a monorepo as a git submodule:
magoo install https://github.com/Pistonight/mono-dev packages/mono-dev --name mono-dev --branch main
A typical monorepo should look like:
- .github/
- packages/
- some-js-package/
- src/
- .gitignore
- package.json
- Taskfile.yml
- some-rust-package/
- src/
- .gitignore
- Cargo.toml
- Taskfile.yml
- mono-dev/ -> https://github.com/Pistonight/mono-dev
- .gitignore
- .gitmodules
- LICENSE
- README.md
- package.json
- pnpm-workspace.yaml
- pnpm-lock.yaml
- Cargo.toml
- Cargo.lock
- Taskfile.yml
The guidelines:
- All meaningful code and config (including build scripts) should be
divided into packages in
packages
directory. - Each package should have a
Taskfile.yml
that defines tasks for the package - Package can depend on other’s tasks by including their
Taskfile.yml
- It’s preferred for a package to depend on another package through the ecosystem,
rather than copying files into other packages. For example, if a Rust package
generates TypeScript code. It’s preferred for the TypeScript code be generated inside the Rust package’s
directory. The Rust package can make a package.json to double as a Node package
and be installed via
package.json
- The root
Taskfile.yml
should include all packages’s Taskfile.yml under the namespace identical to the directory name. Note that the directory name doesn’t have to be the same as the package name. This is to save typing common prefixes. Aliasing the package is not recommended for projects with a lot of packages. - The root
Taskfile.yml
should definecheck
,test
andbuild
for checking, testing and building all packages. These tasks can be used in CI to simplify the setup - Additionally, the root
Taskfile.yml
should declare aninstall
task for installing node modules, along with running post-install tasks. Declaring post-install inTaskfile.yml
is recommended compared to using lifecycle scripts with NPM, as those are NPM specific.
The root Cargo.toml
should declare a Cargo Workspace like:
[workspace]
resolver = "2"
members = [
"packages/some-rust-package"
]
The root pnpm-workspace.yaml
should declare a PNPM Workspace like:
packages:
- packages/some-js-package
catalog:
react: ^18
The catalog
protocol is a PNPM feature that allows dependency versions
to be easily synced.
Atomrepo
This is a term I made up. In the Standard, it refers to a repository that is only meant to be used within a monorepo. The most common scenario is when the project needs external dependencies that’s not covered by NPM or Cargo.
The biggest benefit of an Atomrepo compared to publishing then consuming
the package through a public registry, is that updating code is VERY fast.
I just need to edit the code locally, commit and push it to git, and run magoo update
to update the submodule reference. This skips the need to wait for CI/publish,
while maintaining production-grade standard for the project.
Since an atomrepo is meant to be inside a monorepo, it doesn’t have any strict package structure. The only limitation is that it should not contain any submodules, as recursive submodules will be PAIN. Dependencies should also be installed into the monorepo. This friction helps ensure the submodule dependency chain doesn’t grow out of control.
Also since an atomrepo is inside a monorepo, it can reference mono-dev
in the same
repo with the path ../mono-dev
, or ./node_modules/mono-dev
if PNPM is used.
Singlerepo
This is also a term I made up. This basically refers to simple projects that only have to deal with one ecosystem or one language, such as a CLI tool. Because the project structure is very simple, it doesn’t justify creating a monorepo.
mono-dev
can still be helpful here as it defines common build tasks that
can be included in one line. If PNPM is used, mono-dev
should be
directly from GitHub
.
pnpm i -D https://github.com/Pistonight/mono-dev
It’s important to install mono-dev this way, as the ECMAScript configs assumes mono-dev is found in node_modules!
If PNPM is not involved, then there are 2 options:
- Add it as a git submodule:
magoo install https://github.com/Pistonight/mono-dev mono-dev --name mono-dev --branch main
- Just clone and gitignore it:
tasks:
install:
cmds:
- rm -rf mono-dev
- git clone https://github.com/Pistonight/mono-dev --depth 1
The second option is better for rust projects that meant to be installed from git, so users don’t have to clone the submodule.