Skip to content

Add --stream flag to canonicalize subcommand#19796

Merged
RobinMalfait merged 3 commits intotailwindlabs:mainfrom
aptinio:feat/canonicalize-stream
Mar 16, 2026
Merged

Add --stream flag to canonicalize subcommand#19796
RobinMalfait merged 3 commits intotailwindlabs:mainfrom
aptinio:feat/canonicalize-stream

Conversation

@aptinio
Copy link
Contributor

@aptinio aptinio commented Mar 14, 2026

Summary

  • Adds --stream flag to tailwindcss canonicalize that reads candidate groups from stdin line by line and writes canonicalized results to stdout
  • Keeps the design system loaded across requests, making it suitable as a long-running sidecar process
  • Empty lines pass through, keeping request/response pairs aligned

Motivation

Non-JS tools (formatters, editor plugins, etc.) currently have no lightweight way to canonicalize Tailwind classes. The existing batch mode works for one-off use, but tools that need to canonicalize repeatedly pay the cost of loading the design system each time.

With --stream, a tool can start tailwindcss canonicalize --stream once and send candidate groups over stdin as needed:

$ echo -e "py-3 p-1 px-3\nmt-2 mr-2 mb-2 ml-2" | tailwindcss canonicalize --stream
p-3
m-2

Related discussion: #19736

Adds a streaming mode to `tailwindcss canonicalize` for use as a
long-running sidecar process. With `--stream`, the CLI reads candidate
groups from stdin line by line and writes canonicalized results to
stdout, keeping the design system loaded across requests.

This enables non-JS tools (formatters, editor plugins, etc.) to
canonicalize Tailwind classes via simple line-based I/O without needing
to reload the design system for each request.
@aptinio aptinio requested a review from a team as a code owner March 14, 2026 04:48
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 14, 2026

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

Adds a streaming mode to the canonicalize command via a new --stream CLI option and an exported streamStdin function that reads stdin line-by-line and writes canonicalized results to stdout in text, jsonl, or json. Internal processing was refactored: sortCandidates became canonicalize(designSystem, input: string) and candidate-group processing now returns CandidateGroupResult objects via createCandidateGroupResult. Tests were extended to cover streaming scenarios and a createOutput test helper was added.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding a --stream flag to the canonicalize subcommand, which is the primary focus of this pull request.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the motivation and functionality of the --stream flag with concrete examples and use cases.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

@RobinMalfait RobinMalfait self-assigned this Mar 15, 2026
Copy link
Member

@RobinMalfait RobinMalfait left a comment

Choose a reason for hiding this comment

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

Hey! I appreciate the PR. The canonicalize command was still in a proof of concept phase but was already merged so we can test things out.

That said, I do like the --stream option so that we don't have to spawn the command every time, just like you mentioned. One thing you can do is collect all groups first, and then call it once. But I can see that that might not be as ergonomic.

I'm also conflicted on the --stream name, we might change this before we properly release this. I like --stdio as an option as well, but we already communicate via stdin/stdout even without this long running process so let's keep --stream for now.

Thanks!

@RobinMalfait RobinMalfait enabled auto-merge (squash) March 16, 2026 14:58
@RobinMalfait RobinMalfait merged commit aaaefe8 into tailwindlabs:main Mar 16, 2026
9 checks passed
@aptinio
Copy link
Contributor Author

aptinio commented Mar 16, 2026

Thanks for the review and the merge!

On batching — in my case (Phoenix.LiveView.HTMLFormatter), the formatter hook processes each class attribute value one at a time, so collecting groups up front isn't an option. Streaming fits that model well.

One thing I'd love to see before this is properly released: renaming the subcommand to something like sort-classes, with canonicalization as a flag (e.g. --canonicalize). That'd likely better match what most consumers are using it for — class sorting — which is what the Prettier plugin does too. Happy to put up a PR for that if you're interested.

@aptinio aptinio deleted the feat/canonicalize-stream branch March 16, 2026 15:31
@RobinMalfait
Copy link
Member

The initial version used to be called candidates which could be invoked with some flags: candidates --sort --canonicalize --collapse, but we changed it to just canonicalize because that's what we're trying to do, generate the most canonical form of a set of classes which means that it will be sorted, that each utility is written in its canonical form and that multiple utilities would be collapsed into single classes when possible. While sort-classes could work, it does feel incomplete.

Maybe we rename it in the future, or introduce separate commands, but for now we want to keep it as canonicalize.

@aptinio
Copy link
Contributor Author

aptinio commented Mar 16, 2026

That makes sense — canonicalize covers more than just ordering. Thanks for the context on the earlier candidates iteration.

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.

2 participants