fix: suppress ANSI escape codes in deno fmt/lint subprocess output#865
fix: suppress ANSI escape codes in deno fmt/lint subprocess output#865
Conversation
When swamp invokes deno fmt and deno lint as subprocesses with piped
stdout/stderr, deno still emits ANSI color codes unless NO_COLOR=1 is
set. This causes raw escape sequences to appear in log output, making
it hard to read and breaking structured logging.
Add `env: { ...Deno.env.toObject(), NO_COLOR: '1' }` to all four
Deno.Command invocations that capture deno fmt/lint output:
- checkExtensionQuality() fmt check (extension_quality_checker.ts)
- checkExtensionQuality() lint check (extension_quality_checker.ts)
- createExtensionFmtDeps() runFmt (fmt.ts)
- createExtensionFmtDeps() runLint (fmt.ts)
Spreads the parent environment so PATH and other required vars are
preserved; only NO_COLOR is overridden.
Also adds two unit tests to assert that fmt and lint issue output
contains no ANSI escape sequences.
Fixes #862
There was a problem hiding this comment.
CLI UX Review
Blocking
None.
Suggestions
None.
Verdict
PASS — This PR fixes a UX regression where raw ANSI escape codes (e.g. \x1b[1m\x1b[31merror\x1b[0m) were leaking into log-mode output when swamp extension fmt ran deno fmt/deno lint subprocesses on a color-capable terminal. The fix is minimal and targeted: adding NO_COLOR: "1" to the subprocess env in the four affected invocations. No flags, help text, JSON output shape, or command structure changed. Two new tests directly assert the fix by checking that captured output contains no \x1b[ sequences.
There was a problem hiding this comment.
Code Review
Clean, well-scoped bug fix. The approach of setting NO_COLOR=1 in the subprocess environment is the standard way to suppress ANSI codes and correctly preserves the rest of the environment via Deno.env.toObject().
Blocking Issues
None.
Suggestions
- Test assertions style (
extension_quality_checker_test.ts:337,353): Consider usingassert(fmtIssue !== undefined)orassertExists(fmtIssue)from@std/assertinstead ofassertEquals(fmtIssue !== undefined, true)— it reads more naturally and gives a better failure message. Non-blocking since existing tests in this file use the same pattern.
DDD review: Changes are appropriately scoped to infrastructure concerns (subprocess environment configuration). No domain model impact.
All CLAUDE.md requirements satisfied:
- ✓ License headers present on all modified files
- ✓ No
anytypes introduced - ✓ Named exports used
- ✓ Unit tests next to source files
- ✓ No libswamp import boundary violations
- ✓ Minimal blast radius — only touches the four relevant subprocess invocations
There was a problem hiding this comment.
Adversarial Review
Medium
-
Test fragility in CI — lint ANSI test may pass vacuously (
extension_quality_checker_test.ts:344-356): The lint test uses"// deno-lint-ignore no-explicit-any\nexport const x = 1;\n"to trigger a lint error (unused ignore directive viaban-unused-ignore). This relies onban-unused-ignorebeing enabled by default in Deno's linter. If a future Deno version changes this default, the test would fail to produce a lint issue and theassertEquals(lintIssue !== undefined, true)assertion would fail — but at least it would fail loudly rather than silently pass. Acceptable risk. -
Deno.env.toObject()snapshots env at call time (extension_quality_checker.ts:267,284,fmt.ts:79,92): Each invocation copies the full process environment. This is fine for correctness (and no worse than the previous implicit inheritance behavior), but worth noting that any env var present in the parent process — including potentially sensitive ones — is explicitly forwarded. SinceDeno.Commandinherits the parent environment by default whenenvis not specified, this is actually equivalent to the prior behavior plusNO_COLOR, so no new exposure.
Low
bundle.tssubprocess not updated (src/domain/models/bundle.ts:331): This file also spawnsdenoPathwith piped stdout/stderr and captures output for error messages, but was not updated withNO_COLOR. However, it already callsstripAnsiCode()at line 347 as a post-hoc cleanup, so it's handled — just via a different strategy. Noting for completeness; no action needed.
Verdict
PASS — Clean, minimal fix. The NO_COLOR environment variable is the standard convention (no-color.org), the spread preserves the existing env, and the four call sites are all correctly patched. Tests are reasonable. No critical or high issues found.
Summary
Fixes raw ANSI escape sequences leaking into log output when
swamp extension fmtrunsdeno fmtanddeno lintas subprocesses.Deno.Commandinherits the parent TTY environment, causing deno to emit color codes even when stdout/stderr are piped and captured as strings.env: { ...Deno.env.toObject(), NO_COLOR: "1" }to all four subprocess invocations that capture deno fmt/lint output, suppressing ANSI codes while preservingPATHand other required environment variables.Affected locations:
src/domain/extensions/extension_quality_checker.tscheckExtensionQualitydeno fmt --checksrc/domain/extensions/extension_quality_checker.tscheckExtensionQualitydeno lintsrc/libswamp/extensions/fmt.tscreateExtensionFmtDepsdeno fmt(fix mode)src/libswamp/extensions/fmt.tscreateExtensionFmtDepsdeno lint --fixUser impact: Extension authors running
swamp extension fmton a system with a color-capable terminal will now see clean, readable error messages instead of output like\x1b[1m\x1b[31merror\x1b[0m.Test plan
extension_quality_checker_test.tsassert that fmt and lint issue output contains no ANSI escape sequences (\x1b[)extension_quality_checker_test.tstests passswamp.club, unrelated to this change)deno checkcleandeno lintcleandeno fmtcleanFixes #862
🤖 Generated with Claude Code