Add formatting and static analysis scripts with usage instructions

- Introduced new scripts for auto-fixing formatting and performing static analysis across the project, including support for Lua, Rust, and TypeScript.
- Created `format.md` documentation to guide users on running the formatting scripts.
- Updated CI scripts to utilize the number of processor threads for improved performance during linting and testing.
- Enhanced logging in the `unwatch` function to provide clearer feedback on path watching status.

These changes streamline the formatting and analysis process, ensuring consistent code quality across the project.
This commit is contained in:
Joe 2026-02-17 19:07:50 -06:00
parent 5c4fd801db
commit dea60eb4cf
6 changed files with 309 additions and 26 deletions

View file

@ -0,0 +1,23 @@
# /format - Auto-fix Formatting & Static Analysis
Run formatting and static analysis (linting) across the entire project.
## Instructions
### 1. Run the Format Script
**Windows (PowerShell):**
```powershell
.\scripts\format.ps1
```
**macOS/Linux (Bash):**
```bash
bash scripts/format.sh
```
**Monitor the script output as it runs.** If a step fails, investigate the failure and fix it manually before re-running.
### 2. Report
Once formatting passes clean, provide a brief summary and confirm the final result.

View file

@ -352,22 +352,27 @@ impl VfsBackend for StdBackend {
} }
fn unwatch(&mut self, path: &Path) -> io::Result<()> { fn unwatch(&mut self, path: &Path) -> io::Result<()> {
// Only remove from watches if unwatch succeeds let was_watched = self.watches.contains(path);
// This keeps state consistent if unwatch fails (e.g., path wasn't directly watched)
match self.debouncer.unwatch(path) { match self.debouncer.unwatch(path) {
Ok(()) => { Ok(()) => {
log::info!("Unwatched path: {}", path.display()); if was_watched {
log::info!("Unwatched path: {}", path.display());
} else {
log::trace!(
"Unwatched path (was not actively watched): {}",
path.display()
);
}
self.watches.remove(path); self.watches.remove(path);
Ok(()) Ok(())
} }
Err(err) => { Err(err) => {
// If the path wasn't being watched (common when parent dir is watched),
// still remove from our tracking set but don't propagate the error
if matches!( if matches!(
err.kind, err.kind,
notify::ErrorKind::WatchNotFound | notify::ErrorKind::PathNotFound notify::ErrorKind::WatchNotFound | notify::ErrorKind::PathNotFound
) { ) {
log::info!( log::trace!(
"Path was not directly watched (likely covered by parent): {}", "Path was not directly watched (likely covered by parent): {}",
path.display() path.display()
); );

View file

@ -6,6 +6,7 @@ param(
) )
$RbxDom = -not $NoRbxDom $RbxDom = -not $NoRbxDom
$Threads = [Environment]::ProcessorCount
$ErrorActionPreference = "Continue" $ErrorActionPreference = "Continue"
$failures = @() $failures = @()
@ -56,19 +57,19 @@ $selene = $LASTEXITCODE
Record-Result "Selene" $selene Record-Result "Selene" $selene
Write-Step 6 "Rust Linting (Clippy) - Auto-fix" Write-Step 6 "Rust Linting (Clippy) - Auto-fix"
cargo clippy -j 16 --fix --allow-dirty --allow-staged 2>&1 | Out-Null cargo clippy -j $Threads --fix --allow-dirty --allow-staged 2>&1 | Out-Null
Write-Host "Verifying..." -ForegroundColor Yellow Write-Host "Verifying..." -ForegroundColor Yellow
cargo clippy -j 16 --all-targets --all-features 2>&1 cargo clippy -j $Threads --all-targets --all-features 2>&1
$clippy = $LASTEXITCODE $clippy = $LASTEXITCODE
Record-Result "Clippy" $clippy Record-Result "Clippy" $clippy
Write-Step 7 "Build Everything" Write-Step 7 "Build Everything"
cargo build -j 16 --locked --all-targets --all-features cargo build --locked --all-targets --all-features
$build = $LASTEXITCODE $build = $LASTEXITCODE
Record-Result "Build" $build Record-Result "Build" $build
Write-Step 8 "Run ALL Rust Tests" Write-Step 8 "Run ALL Rust Tests"
$testOutput = cargo test -j 16 --locked --all-features -- --test-threads=16 2>&1 $testOutput = cargo test --locked --all-features -- --test-threads=$Threads 2>&1
$rustTests = $LASTEXITCODE $rustTests = $LASTEXITCODE
$testOutput | Write-Host $testOutput | Write-Host
Record-Result "Rust Tests" $rustTests Record-Result "Rust Tests" $rustTests
@ -106,18 +107,18 @@ if ($RbxDom) {
Write-Step 13 "Rust Linting (Clippy) for rbx-dom - Auto-fix" Write-Step 13 "Rust Linting (Clippy) for rbx-dom - Auto-fix"
Push-Location rbx-dom Push-Location rbx-dom
cargo clippy -j 16 --fix --allow-dirty --allow-staged 2>&1 | Out-Null cargo clippy -j $Threads --fix --allow-dirty --allow-staged 2>&1 | Out-Null
Write-Host "Verifying..." -ForegroundColor Yellow Write-Host "Verifying..." -ForegroundColor Yellow
cargo clippy -j 16 --all-targets --all-features -- -D warnings 2>&1 cargo clippy -j $Threads --all-targets --all-features -- -D warnings 2>&1
$clippyRbxDom = $LASTEXITCODE $clippyRbxDom = $LASTEXITCODE
Pop-Location Pop-Location
Record-Result "Clippy (rbx-dom)" $clippyRbxDom Record-Result "Clippy (rbx-dom)" $clippyRbxDom
Write-Step 14 "Build rbx-dom" Write-Step 14 "Build rbx-dom"
Push-Location rbx-dom Push-Location rbx-dom
cargo build -j 16 --verbose cargo build --verbose
$buildRbxDom = $LASTEXITCODE $buildRbxDom = $LASTEXITCODE
cargo build -j 16 --all-features --verbose cargo build --all-features --verbose
$buildRbxDomAll = $LASTEXITCODE $buildRbxDomAll = $LASTEXITCODE
Pop-Location Pop-Location
Record-Result "Build (rbx-dom)" $buildRbxDom Record-Result "Build (rbx-dom)" $buildRbxDom
@ -125,9 +126,9 @@ if ($RbxDom) {
Write-Step 15 "Run ALL rbx-dom Rust Tests" Write-Step 15 "Run ALL rbx-dom Rust Tests"
Push-Location rbx-dom Push-Location rbx-dom
cargo test -j 16 --verbose -- --test-threads=16 cargo test --verbose -- --test-threads=$Threads
$testsRbxDom = $LASTEXITCODE $testsRbxDom = $LASTEXITCODE
cargo test -j 16 --all-features --verbose -- --test-threads=16 cargo test --all-features --verbose -- --test-threads=$Threads
$testsRbxDomAll = $LASTEXITCODE $testsRbxDomAll = $LASTEXITCODE
Pop-Location Pop-Location
Record-Result "Tests (rbx-dom)" $testsRbxDom Record-Result "Tests (rbx-dom)" $testsRbxDom

View file

@ -11,6 +11,8 @@ for arg in "$@"; do
esac esac
done done
THREADS=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)
failures=() failures=()
step() { step() {
@ -55,19 +57,19 @@ selene_exit=$?
record "Selene" $selene_exit record "Selene" $selene_exit
step 6 "Rust Linting (Clippy) - Auto-fix" step 6 "Rust Linting (Clippy) - Auto-fix"
cargo clippy -j 16 --fix --allow-dirty --allow-staged >/dev/null 2>&1 cargo clippy -j $THREADS --fix --allow-dirty --allow-staged >/dev/null 2>&1
echo "Verifying..." echo "Verifying..."
cargo clippy -j 16 --all-targets --all-features 2>&1 cargo clippy -j $THREADS --all-targets --all-features 2>&1
clippy_exit=$? clippy_exit=$?
record "Clippy" $clippy_exit record "Clippy" $clippy_exit
step 7 "Build Everything" step 7 "Build Everything"
cargo build -j 16 --locked --all-targets --all-features cargo build --locked --all-targets --all-features
build_exit=$? build_exit=$?
record "Build" $build_exit record "Build" $build_exit
step 8 "Run ALL Rust Tests" step 8 "Run ALL Rust Tests"
cargo test -j 16 --locked --all-features -- --test-threads=16 2>&1 cargo test --locked --all-features -- --test-threads=$THREADS 2>&1
rust_tests=$? rust_tests=$?
record "Rust Tests" $rust_tests record "Rust Tests" $rust_tests
@ -101,24 +103,24 @@ if [ "$RBX_DOM" = true ]; then
record "Selene (rbx_dom_lua)" $selene_rbxdom record "Selene (rbx_dom_lua)" $selene_rbxdom
step 13 "Rust Linting (Clippy) for rbx-dom - Auto-fix" step 13 "Rust Linting (Clippy) for rbx-dom - Auto-fix"
(cd rbx-dom && cargo clippy -j 16 --fix --allow-dirty --allow-staged >/dev/null 2>&1) (cd rbx-dom && cargo clippy -j $THREADS --fix --allow-dirty --allow-staged >/dev/null 2>&1)
echo "Verifying..." echo "Verifying..."
(cd rbx-dom && cargo clippy -j 16 --all-targets --all-features -- -D warnings 2>&1) (cd rbx-dom && cargo clippy -j $THREADS --all-targets --all-features -- -D warnings 2>&1)
clippy_rbxdom=$? clippy_rbxdom=$?
record "Clippy (rbx-dom)" $clippy_rbxdom record "Clippy (rbx-dom)" $clippy_rbxdom
step 14 "Build rbx-dom" step 14 "Build rbx-dom"
(cd rbx-dom && cargo build -j 16 --verbose) (cd rbx-dom && cargo build --verbose)
build_rbxdom=$? build_rbxdom=$?
(cd rbx-dom && cargo build -j 16 --all-features --verbose) (cd rbx-dom && cargo build --all-features --verbose)
build_rbxdom_all=$? build_rbxdom_all=$?
record "Build (rbx-dom)" $build_rbxdom record "Build (rbx-dom)" $build_rbxdom
record "Build (rbx-dom all-features)" $build_rbxdom_all record "Build (rbx-dom all-features)" $build_rbxdom_all
step 15 "Run ALL rbx-dom Rust Tests" step 15 "Run ALL rbx-dom Rust Tests"
(cd rbx-dom && cargo test -j 16 --verbose -- --test-threads=16) (cd rbx-dom && cargo test --verbose -- --test-threads=$THREADS)
tests_rbxdom=$? tests_rbxdom=$?
(cd rbx-dom && cargo test -j 16 --all-features --verbose -- --test-threads=16) (cd rbx-dom && cargo test --all-features --verbose -- --test-threads=$THREADS)
tests_rbxdom_all=$? tests_rbxdom_all=$?
record "Tests (rbx-dom)" $tests_rbxdom record "Tests (rbx-dom)" $tests_rbxdom
record "Tests (rbx-dom all-features)" $tests_rbxdom_all record "Tests (rbx-dom all-features)" $tests_rbxdom_all

126
scripts/format.ps1 Normal file
View file

@ -0,0 +1,126 @@
# Auto-fix Formatting & Static Analysis
# Usage: .\scripts\format.ps1 [-NoRbxDom]
param(
[switch]$NoRbxDom
)
$RbxDom = -not $NoRbxDom
$Threads = [Environment]::ProcessorCount
$ErrorActionPreference = "Continue"
$failures = @()
function Write-Step($number, $name) {
Write-Host ""
Write-Host "=== Step ${number}: $name ===" -ForegroundColor Cyan
}
function Record-Result($step, $exitCode) {
if ($exitCode -ne 0) {
$script:failures += $step
Write-Host "FAIL" -ForegroundColor Red
} else {
Write-Host "PASS" -ForegroundColor Green
}
}
# ─── Rojo ────────────────────────────────────────────────────────────────────
Write-Step 1 "Auto-fix Lua Formatting (Stylua)"
stylua plugin/src
$styluaRojo = $LASTEXITCODE
Record-Result "Stylua (Rojo)" $styluaRojo
Write-Step 2 "Auto-fix Rust Formatting"
cargo fmt
$rustfmt = $LASTEXITCODE
Record-Result "Rustfmt" $rustfmt
Write-Step 3 "Auto-fix TypeScript Formatting (Prettier)"
Push-Location vscode-rojo
npx prettier --write "src/**/*.ts"
$prettier = $LASTEXITCODE
Pop-Location
Record-Result "Prettier" $prettier
Write-Step 4 "Lua Static Analysis (Selene)"
selene plugin/src
$selene = $LASTEXITCODE
Record-Result "Selene" $selene
Write-Step 5 "Rust Linting (Clippy) - Auto-fix"
cargo clippy -j $Threads --fix --allow-dirty --allow-staged 2>&1 | Out-Null
Write-Host "Verifying..." -ForegroundColor Yellow
cargo clippy -j $Threads --all-targets --all-features 2>&1
$clippy = $LASTEXITCODE
Record-Result "Clippy" $clippy
# ─── rbx-dom ─────────────────────────────────────────────────────────────────
if ($RbxDom) {
Write-Step 6 "Auto-fix Lua Formatting (Stylua) for rbx_dom_lua"
stylua rbx-dom/rbx_dom_lua/src
$styluaRbxDom = $LASTEXITCODE
Record-Result "Stylua (rbx_dom_lua)" $styluaRbxDom
Write-Step 7 "Auto-fix Rust Formatting (rbx-dom)"
Push-Location rbx-dom
cargo fmt
$rustfmtRbxDom = $LASTEXITCODE
Pop-Location
Record-Result "Rustfmt (rbx-dom)" $rustfmtRbxDom
Write-Step 8 "Lua Static Analysis (Selene) for rbx_dom_lua"
selene rbx-dom/rbx_dom_lua/src
$seleneRbxDom = $LASTEXITCODE
Record-Result "Selene (rbx_dom_lua)" $seleneRbxDom
Write-Step 9 "Rust Linting (Clippy) for rbx-dom - Auto-fix"
Push-Location rbx-dom
cargo clippy -j $Threads --fix --allow-dirty --allow-staged 2>&1 | Out-Null
Write-Host "Verifying..." -ForegroundColor Yellow
cargo clippy -j $Threads --all-targets --all-features -- -D warnings 2>&1
$clippyRbxDom = $LASTEXITCODE
Pop-Location
Record-Result "Clippy (rbx-dom)" $clippyRbxDom
}
# ─── Report ──────────────────────────────────────────────────────────────────
Write-Host ""
Write-Host "=== FORMAT COMPLETE ===" -ForegroundColor Magenta
Write-Host ""
Write-Host "--- Rojo ---" -ForegroundColor White
Write-Host ""
Write-Host "Formatting:"
Write-Host " - Stylua: $(if ($styluaRojo -eq 0) {'PASS'} else {'FAIL'})"
Write-Host " - Rustfmt: $(if ($rustfmt -eq 0) {'PASS'} else {'FAIL'})"
Write-Host " - Prettier (vscode-rojo): $(if ($prettier -eq 0) {'PASS'} else {'FAIL'})"
Write-Host ""
Write-Host "Linting:"
Write-Host " - Selene: $(if ($selene -eq 0) {'PASS'} else {'FAIL'})"
Write-Host " - Clippy: $(if ($clippy -eq 0) {'PASS'} else {'FAIL'})"
if ($RbxDom) {
Write-Host ""
Write-Host "--- rbx-dom ---" -ForegroundColor White
Write-Host ""
Write-Host "Formatting:"
Write-Host " - Stylua (rbx_dom_lua): $(if ($styluaRbxDom -eq 0) {'PASS'} else {'FAIL'})"
Write-Host " - Rustfmt: $(if ($rustfmtRbxDom -eq 0) {'PASS'} else {'FAIL'})"
Write-Host ""
Write-Host "Linting:"
Write-Host " - Selene (rbx_dom_lua): $(if ($seleneRbxDom -eq 0) {'PASS'} else {'FAIL'})"
Write-Host " - Clippy: $(if ($clippyRbxDom -eq 0) {'PASS'} else {'FAIL'})"
}
Write-Host ""
if ($failures.Count -eq 0) {
Write-Host "--- Overall: PASS ---" -ForegroundColor Green
exit 0
} else {
Write-Host "--- Overall: FAIL ---" -ForegroundColor Red
Write-Host "Failed steps: $($failures -join ', ')" -ForegroundColor Red
exit 1
}

126
scripts/format.sh Normal file
View file

@ -0,0 +1,126 @@
#!/usr/bin/env bash
# Auto-fix Formatting & Static Analysis
# Usage: bash scripts/format.sh [--no-rbx-dom]
set -o pipefail
RBX_DOM=true
for arg in "$@"; do
case "$arg" in
--no-rbx-dom) RBX_DOM=false ;;
esac
done
THREADS=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)
failures=()
step() {
echo ""
echo "=== Step $1: $2 ==="
}
record() {
if [ "$2" -ne 0 ]; then
failures+=("$1")
echo "FAIL"
else
echo "PASS"
fi
}
# ─── Rojo ────────────────────────────────────────────────────────────────────
step 1 "Auto-fix Lua Formatting (Stylua)"
stylua plugin/src
stylua_rojo=$?
record "Stylua (Rojo)" $stylua_rojo
step 2 "Auto-fix Rust Formatting"
cargo fmt
rustfmt_exit=$?
record "Rustfmt" $rustfmt_exit
step 3 "Auto-fix TypeScript Formatting (Prettier)"
(cd vscode-rojo && npx prettier --write "src/**/*.ts")
prettier_exit=$?
record "Prettier" $prettier_exit
step 4 "Lua Static Analysis (Selene)"
selene plugin/src
selene_exit=$?
record "Selene" $selene_exit
step 5 "Rust Linting (Clippy) - Auto-fix"
cargo clippy -j $THREADS --fix --allow-dirty --allow-staged >/dev/null 2>&1
echo "Verifying..."
cargo clippy -j $THREADS --all-targets --all-features 2>&1
clippy_exit=$?
record "Clippy" $clippy_exit
# ─── rbx-dom ─────────────────────────────────────────────────────────────────
if [ "$RBX_DOM" = true ]; then
step 6 "Auto-fix Lua Formatting (Stylua) for rbx_dom_lua"
(cd rbx-dom && stylua rbx_dom_lua/src)
stylua_rbxdom=$?
record "Stylua (rbx_dom_lua)" $stylua_rbxdom
step 7 "Auto-fix Rust Formatting (rbx-dom)"
(cd rbx-dom && cargo fmt)
rustfmt_rbxdom=$?
record "Rustfmt (rbx-dom)" $rustfmt_rbxdom
step 8 "Lua Static Analysis (Selene) for rbx_dom_lua"
(cd rbx-dom && selene rbx_dom_lua/src)
selene_rbxdom=$?
record "Selene (rbx_dom_lua)" $selene_rbxdom
step 9 "Rust Linting (Clippy) for rbx-dom - Auto-fix"
(cd rbx-dom && cargo clippy -j $THREADS --fix --allow-dirty --allow-staged >/dev/null 2>&1)
echo "Verifying..."
(cd rbx-dom && cargo clippy -j $THREADS --all-targets --all-features -- -D warnings 2>&1)
clippy_rbxdom=$?
record "Clippy (rbx-dom)" $clippy_rbxdom
fi
# ─── Report ──────────────────────────────────────────────────────────────────
pass_or_fail() { [ "$1" -eq 0 ] && echo "PASS" || echo "FAIL"; }
echo ""
echo "=== FORMAT COMPLETE ==="
echo ""
echo "--- Rojo ---"
echo ""
echo "Formatting:"
echo " - Stylua: $(pass_or_fail $stylua_rojo)"
echo " - Rustfmt: $(pass_or_fail $rustfmt_exit)"
echo " - Prettier (vscode-rojo): $(pass_or_fail $prettier_exit)"
echo ""
echo "Linting:"
echo " - Selene: $(pass_or_fail $selene_exit)"
echo " - Clippy: $(pass_or_fail $clippy_exit)"
if [ "$RBX_DOM" = true ]; then
echo ""
echo "--- rbx-dom ---"
echo ""
echo "Formatting:"
echo " - Stylua (rbx_dom_lua): $(pass_or_fail $stylua_rbxdom)"
echo " - Rustfmt: $(pass_or_fail $rustfmt_rbxdom)"
echo ""
echo "Linting:"
echo " - Selene (rbx_dom_lua): $(pass_or_fail $selene_rbxdom)"
echo " - Clippy: $(pass_or_fail $clippy_rbxdom)"
fi
echo ""
if [ ${#failures[@]} -eq 0 ]; then
echo "--- Overall: PASS ---"
exit 0
else
echo "--- Overall: FAIL ---"
echo "Failed steps: $(IFS=', '; echo "${failures[*]}")"
exit 1
fi