Crates.io | blockwatch |
lib.rs | blockwatch |
version | 0.2.0 |
created_at | 2025-04-07 23:19:47.38736+00 |
updated_at | 2025-08-29 23:07:17.781504+00 |
description | Linter that tracks changes between dependent blocks of code |
homepage | https://github.com/mennanov/blockwatch |
repository | https://github.com/mennanov/blockwatch |
max_upload_size | |
id | 1624797 |
size | 194,744 |
Automatically spot inconsistencies in your documentation, configuration, and code whenever they change.
Have you ever updated a function but forgotten to update the README.md
example that uses it? Or changed a list of
supported items in your code but forgot to update the corresponding list in the documentation?
Keeping everything in sync manually is tedious and error-prone.
BlockWatch is a dependency tracker for your codebase. You declare relationships between blocks of code, if a block changes, BlockWatch will require that its dependents are also updated, catching inconsistencies before they are committed.
It keeps your codebase consistent by making dependencies between code and documentation explicit and verifiable.
It's a simple two-step process:
Mark a "source" block of code and give a name to a "dependent" block in another file (like your documentation).
In src/parsers/mod.rs, we define a list of languages. This block is marked as
affects="README.md:supported-grammar"
, creating a dependency link.
// src/parsers/mod.rs
// <block affects="README.md:supported-grammar-example">
pub(crate) fn language_parsers() -> anyhow::Result<HashMap<String, Rc<Box<dyn BlocksParser>>>> {
Ok(HashMap::from([
("rs".into(), rust_parser),
("js".into(), Rc::clone(&js_parser)),
("go".into(), go_parser),
]))
}
// </block>
In README.md
, we define the block that depends on the code above.
## Supported Languages
[//]: # (<block name="supported-grammar-example">)
- Rust
- JavaScript
- Go
[//]: # (</block>)
When you make a change, BlockWatch checks the git diff. If you modify the
SUPPORTED_LANGUAGES
array in src/parsers/mod.rs
without also modifying the block in README.md
, BlockWatch will
exit with an error, helping to prevent a bad commit.
# This command will now fail until README.md is updated
git diff --patch | blockwatch
This simple mechanism ensures your documentation and code never drift apart.
git diff
to automatically validate changes before you
commit.Requires the Rust toolchain:
cargo install blockwatch
Download a pre-built binary for your platform from the Releases page.
The simplest way to run it is by piping a git diff into the command:
git diff --patch | blockwatch
For automatic checks before each commit, use it with the pre-commit
framework.
Add this to your .pre-commit-config.yaml
:
repos:
- repo: local
hooks:
- id: blockwatch
name: blockwatch
entry: bash -c 'git diff --patch --unified=0 | blockwatch'
language: system
stages: [ pre-commit ]
pass_filenames: false
Add the following job to your workflow file to check for inconsistencies in pull requests.
# .github/workflows/main.yml
jobs:
blockwatch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2 # Required to diff against the base branch
- uses: mennanov/blockwatch-action@v1
BlockWatch supports a wide range of common languages.
.sh
, .bash
).cs
).c
, .cpp
, .cc
, .h
).css
).go
).html
, .htm
).java
).js
, .jsx
).kt
, .kts
).md
, .markdown
).php
, .phtml
).py
, .pyi
).rs
).sql
).toml
).ts
, .d.ts
, .tsx
).xml
).yaml
, .yml
)Have a custom file extension?
You can map it to a supported grammar:
# Treat .xhtml files as .xml
git diff --patch | blockwatch -E xhtml=xml
Blocks can affect other blocks in the same file. Just omit the filename in the affects
attribute.
// <block name="foo" affects=":bar, :buzz">
fn main() {
println!("Blocks can affect multiple other blocks declared in the same file");
println!("Just omit the file name in the 'affects' attribute");
}
// </block>
// <block name="bar">
// Some other piece of code.
// </block>
// <block name="buzz">
// One more.
// </block>
Blocks can reference each other.
// <block name="alice" affects=":bob">
fn foo() {
println!("Hi, Bob!");
}
// </block>
// <block name="bob" affects=":alice">
fn bar() {
println!("Hi, Alice!");
}
// </block>
Blocks can be nested inside one another.
// <block name="entire-file">
fn foo() {
println!("Hello");
}
// <block name="small-block">
fn bar() {
println!("Hi!");
}
// </block>
// </block>
<block><block>will not work</block></block>
.Contributions are welcome! A good place to start is by adding support for a new grammar.
cargo test