Contributing
How to change Mapo itself: set up the monorepo, make a change, verify it and hand it over for review. It is written for people and for AI agents alike. Agents also read the AGENTS.md files in the repository, which hold the rules and link back to these pages.
Building an app with Mapo is a different job: start from Getting Started instead.
Prerequisites
- Node.js ^22.19 || ^24.11 || >=26. The repo ships an
.nvmrc: runnvm use. - pnpm 8.
package.jsonpinspnpm@8.15.6throughpackageManager.
Initial setup
pnpm install
pnpm buildpnpm build builds every package and app through Turborepo. Run it once after cloning: downstream packages resolve each other from dist/, and the MCP server needs its generated knowledge base.
Dev setup (after the initial build)
When you work on packages, switch them to stub mode instead of rebuilding after every change:
# Stub and watch all packages at once (from the monorepo root)
pnpm dev:packages
# Or stub a single package
pnpm --filter @mapomodule/store dev:prepareStub mode replaces dist/ with files that re-export from src/, so the IDE resolves types immediately and source changes need no rebuild. After stubbing, regenerate the app's auto-import types:
cd apps/example-e2e # or whichever app you use
pnpm nuxt prepareIf you run pnpm build on a package during development, re-run its dev:prepare and then nuxt prepare on the app.
To try your changes in a project that installed Mapo from npm, including a backoffice running in Docker, link this workspace into it with pnpm dev:link: see Testing changes in a consumer app.
Where things live
| Path | What it holds |
|---|---|
packages/@mapomodule/ | The published @mapomodule/* packages |
packages/mapomodule/ | The mapomodule meta-module |
apps/example-e2e/ | The reference app: one page per scenario, an in-memory mock API, the MCP test bed |
apps/example/ | Core demo against a real Camomilla backend |
apps/example-theme/ | Theming and MapoOverride demo, no backend |
apps/example-custom-form/ | Custom field registry demo (Vuetify fields) |
docs/ | This site, and the knowledge base of the MCP server |
scripts/ | Build helpers, pnpm dev:link, and the release announcement on Slack |
How the packages relate is explained in Concepts.
How a change is made
Every change, from a one-line fix to a new feature, goes through the same steps:
- Orient. Read the doc of the area you touch and check
git status: work in progress in the tree or in the stash belongs to someone, so leave it alone. - Reuse before you build. Look for an existing component, composable, field type, slot or Nuxt UI component first. A new shared piece needs a second user.
- Implement, following the procedure for what you add:
- Test. New logic leaves a Vitest test behind, and a bug fix leaves a regression test. → Testing and verification
- Document in the same change: the narrative page, the API reference, the sidebar, and the MCP recipes when a new task becomes possible. → Writing documentation
- Verify. Run the gates; for UI or runtime changes, also check the reference app. → Testing and verification
- Hand over. Stage only the files of this change and describe what to review, what you could not verify, and what is still open.
Definition of done
- [ ]
pnpm build,pnpm lint,pnpm typecheck,pnpm testandpnpm docs:buildpass, with no new errors or warnings. - [ ] New logic has tests; root
pnpm exec vitest runpasses too (it also covers core, store and uikit). - [ ] The docs describe the new behaviour: narrative page, API reference, sidebar entry.
- [ ] Public APIs are reflected in the MCP server: recipes, and a rebuilt knowledge base whose tests pass.
- [ ] UI or runtime changes are checked in
apps/example-e2e, or the check is listed for the reviewer. - [ ] The diff contains only this change.
Git hooks
Two Husky hooks run on every commit.
pre-commit runs lint-staged on the staged files:
prettier --writeformats.ts,.tsx,.vue,.jsonand.mdfiles.vitest related --runruns the tests that import the staged.ts,.tsxand.vuefiles, directly or transitively.
commit-msg validates the message against Conventional Commits with commitlint:
feat(core): add useMapoAuth composable ✔
fix(form): correct nested accessor path ✔
wip: stuff ✘Commits
The commit type drives the version bump of the affected packages (see Release Process): fix is a patch, feat a minor, feat! or a BREAKING CHANGE footer a major. Choose it for what the change means to consumers.
- Types in use:
feat,fix,docs,chore,refactor,test,build,ci,style. - The scope is the package or area:
core,store,uikit,form,utils,i18n,mcp,mapomodule,docs,example,example-e2e,example-theme,deps,ci. - A sweeping mechanical change (formatting, renames across packages) is its own commit.
CI and releases
| Workflow | Trigger | What it runs |
|---|---|---|
CI | Pull request to main | build + lint + typecheck, then build + test |
Docs Pages | Push to main | Builds this site and deploys it to GitHub Pages |
CD | Manual dispatch, never on main | pnpm release or pnpm release:dry through multi-semantic-release; a real release is then announced on Slack |
Releases are cut by maintainers. The details are in Release Process.