Read these alongside the root instructions and the Rewatch guide. All paths and commands below are relative to the repository root unless stated otherwise.
-
Initialization (
build::initialize_build)- Parse
rescript.jsonconfiguration - Discover packages and dependencies
- Set up compiler information
- Create initial
BuildState
- Parse
-
AST Generation (
build::parse)- Generate AST files using
bsc -bs-ast - Handle PPX transformations
- Process JSX
- Generate AST files using
-
Dependency Analysis (
build::deps)- Analyze module dependencies from AST files
- Build dependency graph
- Detect circular dependencies
-
Compilation (
build::compile)- Generate
bsccompiler arguments - Compile modules in dependency order
- Handle warnings and errors
- Generate JavaScript output
- Generate
-
Incremental Updates (
watcher.rs)- Watch for file changes
- Determine dirty modules
- Recompile only affected modules
- CLI Arguments: Add to
cli.rsinBuildArgsandWatchArgs - Configuration: Extend
config.rsfor newrescript.jsonfields - Build Logic: Modify appropriate
build/*.rsmodules - Thread Parameters: Pass new parameters through the build system chain
- Add Tests: Include unit tests for new functionality
-
Parameter Threading: New CLI flags need to be passed through:
main.rs→build::build()→initialize_build()→BuildStatemain.rs→watcher::start()→async_watch()→initialize_build()
-
Configuration Precedence: Command-line flags override
rescript.jsonconfig -
Error Handling: Use
anyhow::Resultfor error propagation -
Logging: Use
log::debug!for development debugging
# Run rewatch tests (from project root)
cargo test --manifest-path rewatch/Cargo.toml
# Test specific functionality
cargo test --manifest-path rewatch/Cargo.toml config::tests::test_get_warning_args
# Run clippy for code quality
cargo clippy --manifest-path rewatch/Cargo.toml --all-targets --all-features
# Check formatting
cargo fmt --check --manifest-path rewatch/Cargo.toml
# Build rewatch
cargo build --manifest-path rewatch/Cargo.toml --release
# Or use the Makefile shortcuts
make rewatch # Build rewatch
make test-rewatch # Run integration testsNote: The rewatch project is located in the rewatch/ directory with its own Cargo.toml file. All cargo commands should be run from the project root using the --manifest-path rewatch/Cargo.toml flag, as shown in the CI workflow.
Integration Tests: The make test-rewatch command runs bash-based integration tests located in rewatch/tests/suite.sh. These tests use the rewatch/testrepo/ directory as a test workspace with various package configurations to verify rewatch's behavior across different scenarios.
Running Individual Integration Tests: You can run individual test scripts directly by setting up the environment manually:
cd rewatch/tests
export REWATCH_EXECUTABLE="$(realpath ../target/debug/rescript)"
eval $(node ./get_bin_paths.js)
export RESCRIPT_BSC_EXE
export RESCRIPT_RUNTIME
source ./utils.sh
bash ./watch/06-watch-missing-source-folder.shThis is useful for iterating on a specific test without running the full suite.
- Build State: Use
log::debug!to inspectBuildStatecontents - Compiler Args: Check generated
bscarguments incompile.rs - Dependencies: Inspect module dependency graph in
deps.rs - File Watching: Monitor file change events in
watcher.rs
Rewatch supports OpenTelemetry (OTEL) tracing for build and watch commands. To visualize traces locally, run a Jaeger all-in-one container:
docker run -d --name jaeger \
-p 4317:4317 -p 4318:4318 -p 16686:16686 \
jaegertracing/all-in-oneThen run rewatch with the OTLP endpoint set:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 cargo run --manifest-path rewatch/Cargo.toml -- buildOpen http://localhost:16686 to view traces in the Jaeger UI.
Note: Use tracing::debug! (not log::debug!) for events you want to appear in OTEL traces — they use separate logging systems.
Rewatch follows the OTEL spec for configuration — no rewatch-specific knobs exist.
| Variable | Purpose |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
Base endpoint of the collector (e.g. http://localhost:4318). /v1/traces is appended for the trace exporter. Setting this (or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) is what enables telemetry — if neither is set, tracing is a no-op. |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
Full trace endpoint used verbatim. Overrides the general endpoint for traces. |
OTEL_EXPORTER_OTLP_HEADERS |
Extra headers on exporter requests (e.g. authorization=Bearer xyz). |
OTEL_SERVICE_NAME |
Service name reported on spans. Defaults to rewatch. |
OTEL_RESOURCE_ATTRIBUTES |
Comma-separated key=value pairs added as resource attributes (e.g. deployment.environment=ci,host.name=$HOSTNAME). |
RUST_LOG |
Controls which span/event levels are captured (e.g. RUST_LOG=info, RUST_LOG=rewatch=debug). Defaults to debug when telemetry is enabled. |
When running the rewatch binary directly (via cargo run or the compiled binary) during development, you need to set environment variables to point to the local compiler and runtime. Otherwise, rewatch will try to use the installed versions:
# Set the compiler executable path
export RESCRIPT_BSC_EXE=$(realpath _build/default/compiler/bsc/rescript_compiler_main.exe)
# Set the runtime path
export RESCRIPT_RUNTIME=$(realpath packages/@rescript/runtime)
# Now you can run rewatch directly
cargo run --manifest-path rewatch/Cargo.toml -- buildNote that the dev binary is ./rewatch/target/debug/rescript, not rewatch. The binary name is rescript because that's the package name in Cargo.toml.
This is useful when testing rewatch changes against local compiler modifications without running a full make build cycle.
Use -v for info-level logging or -vv for debug-level logging (e.g., to see which folders are being watched in watch mode):
cargo run --manifest-path rewatch/Cargo.toml -- -vv watch <folder>- Incremental Builds: Only recompile dirty modules
- Parallel Compilation: Use
rayonfor parallel processing - Memory Usage: Be mindful of
BuildStatesize in large projects - File I/O: Minimize file system operations
When clippy suggests refactoring that could impact performance, consider the trade-offs:
-
Parameter Structs vs Many Arguments: While clippy prefers parameter structs for functions with many arguments, sometimes the added complexity isn't worth it. Use
#[allow(clippy::too_many_arguments)]for functions that legitimately need many parameters and where a struct would add unnecessary complexity. -
Cloning vs Borrowing: Sometimes cloning is necessary due to Rust's borrow checker rules. If the clone is:
- Small and one-time (e.g.,
Vec<String>with few elements) - Necessary for correct ownership semantics
- Not in a hot path
Then accept the clone rather than over-engineering the solution.
- Small and one-time (e.g.,
-
When to Optimize: Profile before optimizing. Most "performance concerns" in build systems are negligible compared to actual compilation time.
-
Avoid Unnecessary Type Conversions: When threading parameters through multiple function calls, use consistent types (e.g.,
Stringthroughout) rather than converting betweenStringand&strat each boundary. This eliminates unnecessary allocations and conversions.
- Add to
BuildArgsandWatchArgsincli.rs - Update
From<BuildArgs> for WatchArgsimplementation - Pass through
main.rsto build functions - Thread through build system to where it's needed
- Add unit tests for the new functionality
- Update
compiler_args()inbuild/compile.rs - Consider both parsing and compilation phases
- Handle precedence between CLI flags and config
- Test with various
rescript.jsonconfigurations
- Use
packages.rsfor package discovery - Update
deps.rsfor dependency analysis - Handle both local and external dependencies
- Consider dev dependencies vs regular dependencies
- Modify
watcher.rsfor file change handling - Update
AsyncWatchArgsfor new parameters - Handle different file types (
.res,.resi, etc.) - Consider performance impact of watching many files
sleepis fragile — Prefer polling (e.g.,wait_for_file) over fixed sleeps. CI runners are slower than local machines.exit_watcheris async — It only signals the watcher to stop (removes the lock file), it doesn't wait for the process to exit. Avoid triggering config-change events before exiting, as the watcher may start a concurrent rebuild.sed -idiffers across platforms — macOS requiressed -i '' ..., Linux does not. Use thereplace/normalize_pathshelpers fromrewatch/tests/utils.shinstead of rawsed.