Reusable pre-commit / prek hooks for JetBrains MPS projects.
MPS keeps a lot of its project structure in files that are easy to leave in an inconsistent state by hand or by a bad
merge: a module added on disk but never registered in .mps/modules.xml, a model dropped outside every model root, a
descriptor renamed but not its folder. MPS silently ignores most of these, so the mistake only surfaces much later.
These hooks catch them at commit time.
Add the repo to your project's .pre-commit-config.yaml:
repos:
- repo: https://github.com/specificlanguages/mps-pre-commit-hooks
rev: v0.1.0
hooks:
- id: mps-check-orphan-modules
- id: mps-check-unbuilt-modules
- id: mps-check-missing-modules
- id: mps-check-orphan-models
- id: mps-check-orphan-mpsr-files
- id: mps-check-zero-sized-xmls
- id: mps-check-module-naming
- id: mps-check-path-variablesThen pre-commit install (or just prek install). The hooks work the same under both runners.
Each hook is independent — enable only the ones you want. They all scan the whole repository (not just the staged files), since most of what they check are cross-file relationships. The structural checks (orphans, and missing/dangling references) run on every commit, because the problem they catch is often introduced by a commit that only deletes a file — which pre-commit would otherwise skip them for. The per-file content checks (zero-sized, naming, path variables) run only when a relevant file is added or changed.
Reports orphan modules — *.msd / *.mpl / *.devkit / *.mpst files present on disk but not registered in any
.mps/modules.xml. MPS opens a project from its modules.xml, so it silently ignores an unregistered module, and the
mistake otherwise surfaces only much later.
Reports unbuilt modules — modules that are not mentioned in any MPS build script.
Exclude demo and sandbox modules with --exclude:
- id: mps-check-unbuilt-modules
args: [--exclude=/code/applications, --exclude=_spreferences, --exclude=*.sandbox.msd]The reverse of the orphan-modules check: reports .mps/modules.xml entries whose modulePath points to a file that no
longer exists on disk (typically left behind when a module is moved or deleted). Entries addressed through a path
variable are ignored.
mps-check-missing-modules is read-only — it reports and fails. mps-fix-missing-modules removes the dangling
<modulePath> entries from their modules.xml; use it when you want the hook to repair them for you:
- id: mps-fix-missing-modulesThese are defined as separate hooks because the check hook may run in parallel with other read-only hooks whereas the
fix hook requires serial execution (require_serial: true).
Reports model files (*.mps / *.mpsr / .model) living outside every module's declared default model root. A model
outside any root is invisible to MPS — present on disk but never loaded — usually the result of a move that didn't
update the owning module, or a stray copy.
Reports *.mpsr files whose directory has no .model header file alongside them. The header describes the model;
without it MPS cannot load the roots, so the model is effectively lost.
Reports tracked MPS XML files that are zero bytes — model files (*.mps / *.mpsr / .model), module descriptors
(*.msd / *.mpl / *.devkit / *.mpst), and the per-project .mps/modules.xml / .mps/libraries.xml. A zero-byte
file here is almost always the result of a botched save, merge, or checkout; MPS won't load it, silently dropping
whatever it held.
Checks that every module descriptor (*.msd / *.mpl / *.devkit / *.mpst) agrees with its layout on disk. The
directory and the file must be named after the full module name: com.example.foo must be located in
com.example.foo/com.example.foo.mpl (likewise for other module types).
Modules can be excluded with --exclude, a repeatable glob written like a .gitignore pattern:
- id: mps-check-module-naming
args: [--exclude=_spreferences, --exclude=some.lang/sandbox]Reports path variables in .mps/libraries.xml and .mps/modules.xml. Replacing these paths with project-relative paths
makes it easier to check out and open the project.
mps-check-path-variables is check-only, it fails if a path variable is found. mps-fix-path-variables rewrites the
offending paths in place; use it when you want the hook to repair them for you:
- id: mps-fix-path-variablesThese are defined as separate hooks because the check hook may run in parallel with other read-only hooks whereas the
fix hook requires serial execution (require_serial: true).
The fix assumes the macro's value is the Git repository root and re-expresses the whole path relative to
$PROJECT_DIR$:
${mbeddr.github.core.home}/code/platform/com.mbeddr.doc
→ $PROJECT_DIR$/../../platform/com.mbeddr.doc
Caveat. This assumption holds for the common case where a variable points at the project's own checkout root. In other cases the rewritten path will be incorrect. Like other pre-commit fixers, the hook exits non-zero when it changes files, so you re-stage them deliberately.
MIT. See LICENSE.