Skip to content

Conversation

@Martinsos
Copy link
Member

@Martinsos Martinsos commented Nov 20, 2025

Description

Terminal output from wasp CLI on windows was messed up due to windows shells not showing ansi codes correctly. This PR makes default behaviour on Windows to not do any styling. It also brings a new env var that users can use to explicitly control this behaviour (styling or not).

Type of change

  • 🔧 Just code/docs improvement
  • 🐞 Bug fix
  • 🚀 New/improved feature
  • 💥 Breaking change

Checklist

  • I tested my change in a Wasp app to verify that it works as intended.

  • 🧪 Tests and apps:

    - [ ] I added unit tests for my change.
    - [ ] (if you fixed a bug) I added a regression test for the bug I fixed.
    - [ ] (if you added/updated a feature) I added/updated e2e tests in examples/kitchen-sink/e2e-tests.
    - [ ] (if you added/updated a feature) I updated the starter templates in waspc/data/Cli/templates, as needed.
    - [ ] (if you added/updated a feature) I updated the example apps in examples/, as needed.
    - [ ] (if you updated examples/tutorials) I updated the tutorial in the docs (and vice versa).

  • 📜 Documentation:

    - [ ] (if you added/updated a feature) I added/updated the documentation in web/docs/.

  • 🆕 Changelog: (if change is more than just code/docs improvement)

    • I updated waspc/ChangeLog.md with a user-friendly description of the change.
      - [ ] (if you did a breaking change) I added a step to the current migration guide in web/docs/migration-guides/.
      - [ ] I bumped the version in waspc/waspc.cabal to reflect the changes I introduced.

@Martinsos Martinsos changed the title Terminal output is now unstyled (with ansi codes) on Windows. Terminal output is now unstyled (ansi codes) on Windows. Nov 20, 2025
{-# NOINLINE isStylingDisabled #-}
isStylingDisabled :: Bool
isStylingDisabled =
case unsafePerformIO (lookupEnv "WASP_TERMINAL_STYLING") of
Copy link
Member Author

@Martinsos Martinsos Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing myself: should we document this somewhere, that we have this functionality now (this env var)? Hm probably should.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 21, 2025

Deploying wasp-docs-on-main with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2c85b90
Status: ✅  Deploy successful!
Preview URL: https://300f2db6.wasp-docs-on-main.pages.dev
Branch Preview URL: https://martin-win-unstyled-term-out.wasp-docs-on-main.pages.dev

View logs

@infomiho infomiho self-requested a review November 21, 2025 13:13
Copy link
Member

@cprecioso cprecioso left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job tracking this down!

Requesting changes:

  • Strongly on adding docs and improving changelog
  • Medium on removing the unsafePerformIO (though willing to let it go)
  • Weak in everything else

Btw, I can see on MS documentation that there should be ANSI support on both ConHost and WinTer: https://learn.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Core/about/about_ansi_terminals?view=powershell-5.1; I assume I might be missing something.
I'd have liked to see an issue with a reproduction, or at least more information on this same PR with an overview of affected systems and links to documentation supporting this solution.

Comment on lines +10 to +14
isSystemWindows :: Bool
isSystemWindows = System.Info.os == "mingw32"

isSystemMacOS :: Bool
isSystemMacOS = System.Info.os == "darwin"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idea, weak: if we'd be expecting further development that needs specific platform support, we could use a data System = Windows | MacOS | Linux thing that abstracts us from this.

{-# NOINLINE isStylingDisabled #-}
isStylingDisabled :: Bool
isStylingDisabled =
case unsafePerformIO (lookupEnv "WASP_TERMINAL_STYLING") of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

{-# NOINLINE isStylingDisabled #-}
isStylingDisabled :: Bool
isStylingDisabled =
case unsafePerformIO (lookupEnv "WASP_TERMINAL_STYLING") of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I dislike doing an unsafe thing for this. It'd make sense that this is something controlled at printing time, when we already have access to IO so that we don't need to do this. I understand this could mean a larger refactor so I'll let you evaluate if that's something we should do.

Comment on lines +21 to +24
isStylingDisabled =
case unsafePerformIO (lookupEnv "WASP_TERMINAL_STYLING") of
Nothing -> isSystemWindows
Just v -> isEnvVarValueTruthy v
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads strangely, I'd prefer to have something explicit like

Suggested change
isStylingDisabled =
case unsafePerformIO (lookupEnv "WASP_TERMINAL_STYLING") of
Nothing -> isSystemWindows
Just v -> isEnvVarValueTruthy v
isStylingDisabled =
case unsafePerformIO (lookupEnv "WASP_TERMINAL_STYLING") of
Nothing -> defaultValue
Just v -> isEnvVarValueTruthy v
where
defaultValue
| isSystemWindows = False
| otherwise = True

I understand it is not as concise (and there's some repetition in the cases) but it is really zippy to read through and understand.

Comment on lines +29 to +30
- Fixed terminal output on Windows: now it is unstyled on Windows, since ansi codes are often not shown correctly on Win shells.
This default behaviour can be explicitly controlled via `WASP_TERMINAL_STYLING=0|1` env var. ([#3403](https://github.com/wasp-lang/wasp/pull/3403))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't usually have multiline items in the changelog. Maybe actually noting this behaviour in the documentation is better that in the changelog.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this is now added in the wrong release, make an "Unreleased" heading

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think this could be better worded (or at least avoid the abbreviations)

Suggested change
- Fixed terminal output on Windows: now it is unstyled on Windows, since ansi codes are often not shown correctly on Win shells.
This default behaviour can be explicitly controlled via `WASP_TERMINAL_STYLING=0|1` env var. ([#3403](https://github.com/wasp-lang/wasp/pull/3403))
- On Windows, we disable terminal styling by default, in order to improve support. You can manually enable it back through [these instructions](link to docs). ([#3403](https://github.com/wasp-lang/wasp/pull/3403))

isSystemMacOS = System.Info.os == "darwin"

isEnvVarValueTruthy :: String -> Bool
isEnvVarValueTruthy envVarValue = envVarValue `notElem` ["0", "false", "no", "off"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the best place for it but I'm not sure there's a better one so I'll let it go.

@Martinsos
Copy link
Member Author

Martinsos commented Nov 24, 2025

Ok, so question is: do we want to turn it off on Windows for real?
What we know is that on Win server 22, in powershell, ansii codes work. In cmd, they don't.
I haven't tried it yet on Win 11.

So it might be we want to leave it on by default, or maybe we do some smart checking for support. But we still could offer the option to not color the output via env flag even if by default we go with it. Btw is there a standardized name for such env flag? Also look into that.

Potentially useful info: https://learn.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Core/about/about_ansi_terminals?view=powershell-5.1 .

Also, might we worth checking what others re doing.
Here is a quick output from asking Claude Code to analyze bun codebase (gotta check how/if it is correct, this is direct from AI):

1. Bun Explicitly Enables ANSI on Windows

  Bun proactively enables Virtual Terminal Processing on Windows at startup by calling
  SetConsoleMode() with the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag:

  // src/output.zig:172-218
  _ = c.SetConsoleMode(stdout,
      w.ENABLE_PROCESSED_OUTPUT |
      std.os.windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING |
      w.ENABLE_WRAP_AT_EOL_OUTPUT |
      mode
  );

  This enables ANSI escape sequences on Windows 10+ (including Windows Server 2016+).

  2. No Differentiation Between cmd.exe and PowerShell

  Critical finding: Bun does NOT differentiate between cmd.exe and PowerShell. Both are treated
  identically:

  - Uses uv_guess_handle(fd) on Windows which returns UV_TTY for any console
  - Applies the same console mode flags to both shells
  - No shell-specific detection code exists

  3. Windows Build-Based Color Detection

  Bun detects color support based on Windows build numbers, not shell type:

  // src/js/internal/tty.ts:56-172
  if (process.platform === "win32") {
    const { release } = require("node:os");
    OSRelease = release().split(".");

    if (+OSRelease[0] >= 10) {
      const build = +OSRelease[2];
      if (build >= 14931) return COLORS_16m;  // TrueColor (16M colors)
      if (build >= 10586) return COLORS_256;  // 256 colors
    }
    return COLORS_16;  // 16 colors for older Windows
  }

  Windows Server 2022 has build 20348+, which means it should support full TrueColor (24-bit) in
  both cmd.exe and PowerShell.

  4. ANSI Support Detection Method

  To detect ANSI support, Bun checks:

  // src/js/internal/util/colors.ts:16-22
  shouldColorize(stream) {
    if (process.env.FORCE_COLOR !== undefined) {
      return getColorDepth(process.env) > 2;
    }

    return stream?.isTTY &&
           (stream.getColorDepth?.() > 2 : true);
  }

  5. Environment Variable Support

  Bun respects these environment variables (in priority order):
  - FORCE_COLOR - Force colors (empty/"1"/"true" = 16, "2" = 256, "3" = TrueColor)
  - NODE_DISABLE_COLORS - Disable colors
  - NO_COLOR - Disable colors
  - TERM=dumb - Disable colors

@Martinsos
Copy link
Member Author

Martinsos commented Nov 26, 2025

@FranjoMindek and @cprecioso linked https://clig.dev/#output , and this is the most relevant part:

Disable color if your program is not in a terminal or the user requested it. These things should disable colors:

  • stdout or stderr is not an interactive terminal (a TTY). It’s best to individually check—if you’re piping stdout to another program, it’s still useful to get colors on stderr.
  • The NO_COLOR environment variable is set and it is not empty (regardless of its value).
  • The TERM environment variable has the value dumb.
  • The user passes the option --no-color.
  • You may also want to add a MYAPP_NO_COLOR environment variable in case users want to disable color specifically for your program.

Further reading: no-color.org

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants