Monorepo & Multi-Project Setups
In a monorepo, each package can be its own mdt project with its own providers, consumers, data, and configuration. The mechanism is sub-project boundaries.
How sub-project boundaries work
Any directory below the project root that contains mdt.toml, .mdt.toml, or .config/mdt.toml is a separate project. The parent's scan skips it entirely. This applies at any depth, including direct children of the root.
my-monorepo/
mdt.toml # root project
.templates/
shared.t.md # root providers
readme.md # root consumers
docs/
.mdt.toml # docs/ is a separate project
readme.md
packages/
lib-a/
mdt.toml # lib-a is a separate project
readme.md
lib-b/
.config/
mdt.toml # lib-b is a separate project
readme.md
lib-c/
readme.md # no config: part of the root projectRunning mdt update at the root updates readme.md and packages/lib-c/readme.md, but nothing in docs/, lib-a/, or lib-b/. Run those with --path:
mdt update --path packages/lib-aWithout --path, mdt starts from the current directory and walks up, within the git repository, to the nearest directory with a config, like git and cargo do. Running mdt check inside packages/lib-a/ therefore checks lib-a, and running it inside packages/lib-c/ (no config) checks the root project.
Setting up a sub-project
An empty config file is enough to create a boundary:
touch packages/lib-a/mdt.tomlEach project resolves its config, *.t.md files, and [data] paths relative to its own root. Provider names only need to be unique within one project, so lib-a and lib-b can both define {@install}.
# packages/lib-a/mdt.toml
[data]
package = "package.json" # packages/lib-a/package.json, not the root'sSharing providers across projects
A project cannot see another project's providers by default. To share them, point [templates] paths at the shared directory. Paths are relative to the sub-project and may leave it:
# packages/lib-a/mdt.toml
[templates]
paths = ["../../.templates"]Now lib-a's consumers can use every provider in my-monorepo/.templates/. Block arguments fill in per-package values:
<!-- .templates/shared.t.md -->
<!-- {@badge:"crate_name"} -->
[](https://crates.io/crates/{{ crate_name }})
<!-- {/badge} --><!-- packages/lib-a/readme.md -->
<!-- {=badge:"lib-a"} -->
<!-- {/badge} -->Things to know:
- Only
*.t.mdfiles are read from a listed directory. Other files there, such as the rootreadme.md, are never treated as the sub-project's consumers. - Keep shared template files provider-only. A consumer block inside a shared
*.t.mdfile is checked and updated by every project that lists the directory. - Providers from a directory outside the project are a shared library: the ones a sub-project does not use are never reported as unused.
- The sub-project needs its own
[data]for any variables the shared providers use (see below). - A listed path that is not a directory is an error (
mdt::templates_path).
Data for shared providers
A shared provider is rendered with the consuming project's data, and the root's [data] does not apply to sub-projects. If a shared provider uses {{ package.version }} and the sub-project has no [data], the text is copied verbatim and mdt warns:
warning: provider block `version` in /path/to/my-monorepo/.templates/shared.t.md uses template variable(s) package.version, but this project has no `[data]`, so the text is copied without rendering; declare the namespace(s) under `[data]` in this project's mdt.tomlDeclare the namespace in the sub-project. Data paths may point outside it:
# packages/lib-a/mdt.toml
[templates]
paths = ["../../.templates"]
[data]
package = "../../package.json"CI checks
Run mdt check once per project:
- name: check docs
run: |
mdt check
mdt check --path docs
mdt check --path packages/lib-a
mdt check --path packages/lib-bOr loop over packages/*/, failing if any check fails:
status=0
for dir in . packages/*/; do
if [ "$dir" = . ] || [ -f "$dir/mdt.toml" ] || [ -f "$dir/.mdt.toml" ] || [ -f "$dir/.config/mdt.toml" ]; then
echo "Checking $dir"
mdt check --path "$dir" || status=1
fi
done
exit $statusOutput paths are relative to each project's root. With --format github, this means inline annotations for a sub-project can point at the wrong file; see CI Integration.