RustyWind

repository·master·Indexed 20 days ago

https://github.com/avencera/rustywind

A high-performance CLI tool (v0.26.0) designed to sort Tailwind CSS classes and remove duplicates across various template languages. It supports a wide range of source languages including HTML, Svelte, Astro, JSX/TSX, Django, Jinja, Twig, Liquid, Handlebars, ERB, EJS, PHP, Blade, Lit, and Ruby. The tool provides flexible sorting strategies via Vite CSS URLs, CSS files, JSON configurations, or a default pattern sorter, and includes a native Rust xtask for development automation, fuzz testing, and real-world project comparison.

Tokens
16K
Snippets
53
Records
68
Agent score
67%

What's inside rustywind

  1. Configure language and template syntax

    master

    RustyWind infers template syntax from file extensions. If your file uses an extension that doesn't match its content, or if you are using STDIN, you can override the language profile using the --language (or -l) flag.

    Supported language profiles: html, svelte, astro, jsx, tsx, django, jinja, twig, liquid, handlebars, erb, ejs, php, blade, lit, ruby.

    Behavior by profile:

    • JSX/TSX: Preserves program text, comments, and dynamic attributes; sorts quoted class and className attributes.
    • Astro: Preserves frontmatter, expressions, dynamic class attributes, and class:list directives; sorts static quoted class attributes.
    • Unrecognized extensions: Uses conservative legacy-compatible extraction (sorts simple static attributes, leaves template-looking attributes unchanged).

    Example:

    rustywind --language svelte Component.html
    rustywind --language svelte Component.html
  2. Configure sort order via CSS or Config file

    master

    You can control the order in which classes are sorted using three different methods:

    1. Config File: Provide a JSON file containing a sortOrder array.
    2. CSS File: Provide a path to a CSS file; RustyWind will use the order of classes found in that file.
    3. Vite CSS: Provide a URL to a CSS file generated by Vite (experimental).
    # Using a config file
    rustywind --config-file config.json .
    
    # Using a local CSS file for sort order
    rustywind --output-css-file styles.css .
    
    # Using a Vite-generated CSS URL
    rustywind --vite-css "http://127.0.0.1:5173/src/assets/main.css" . --dry-run
  3. Handle input via STDIN

    master

    When using --stdin, RustyWind reads content from the standard input stream. Because STDIN doesn't have a filename, you should use the --stdin-filename (or -f) flag to help RustyWind infer the correct source language for parsing.

    # Pipe content to rustywind and specify the filename for language inference
    echo "<HTML_OR_COMPONENT_CONTENT>" | rustywind --stdin -f components/my-component.svelte
  4. Use RustyWind xtask for development automation

    master

    The xtask crate provides native Rust automation tools for the RustyWind project, replacing previous Python scripts. All automation tasks are executed via the cargo xtask <command> interface.

    Prerequisites

    • Node.js: version 20.19 or newer and npm installed system-wide.
    • Git: required for fetching real-world comparison corpora.

    Common Workflow

    • Compare against real-world projects: Use cargo xtask compare run to validate RustyWind against snapshots of JSX, TSX, Svelte, and Astro Tailwind projects.
    • Fuzz testing: Use cargo xtask fuzz run to execute parallel fuzz tests with automatic failure analysis.
    cargo xtask <command>
  5. Use RustyWind to sort Tailwind CSS classes

    master

    RustyWind is a CLI tool that scans your project and sorts Tailwind CSS classes, while also removing duplicate classes.

    Basic Usage

    • Preview changes: Run with a path to see updated file contents in the terminal. rustywind .
    • Apply changes: Reorganize classes in place by using the --write flag. rustywind --write .
    • Dry run: List files that would be changed without printing content. rustywind --dry-run .
    • CI Mode: Exit with an error code if unsorted classes are found. rustywind --check-formatted .

    Working with STDIN

    You can pipe file contents directly to RustyWind:

    echo "<FILE CONTENTS>" | rustywind --stdin

    To ensure correct template syntax inference when using STDIN, provide a filename:

    cat Component.svelte | rustywind --stdin --stdin-filename Component.svelte
    rustywind --write .
  6. Develop and extend RustyWind xtask

    master

    If you are contributing to the xtask crate, use the following workflow:

    Building and Testing

    # Build the xtask package
    cargo build --package xtask
    
    # Run tests for the xtask package
    cargo test --package xtask

    Adding New Commands

    1. Create a new file in xtask/src/commands/.
    2. Implement pub fn run(...) -> Result<()>.
    3. Add the module to xtask/src/commands.rs.
    4. Add a variant to the appropriate enum in xtask/src/main.rs.
    5. Add a corresponding match arm in the match expression in main.rs.

    Code Style Guidelines

    • Use color-eyre for error handling.
    • Start inline comments with lowercase.
    • Use capitalized doc comments (///).
    • Minimize function nesting.
    • Use meaningful variable names.
    cargo build --package xtask
  7. Set up the fuzz test environment

    master

    The fuzz setup command prepares the environment for fuzz testing by building the RustyWind release binary and installing necessary npm dependencies in tests/fuzz.

    Note: You typically do not need to run this manually, as cargo xtask fuzz run automatically triggers setup if prerequisites are missing. Use this command if you want to pre-build the environment.

    cargo xtask fuzz setup
    cargo xtask fuzz setup
  8. Install RustyWind

    master

    RustyWind can be installed via several package managers depending on your environment:

    npm / yarn

    yarn global add rustywind
    # or
    npm install -g rustywind

    Homebrew (macOS and Linux)

    brew install avencera/tap/rustywind

    Rust / Cargo

    cargo install rustywind
    # or
    cargo binstall rustywind

    Shell Script

    curl -LSfs https://avencera.github.io/rustywind/install.sh | sh -s -- --git avencera/rustywind

    Docker

    docker run --rm -v $PWD:/app avencera/rustywind:latest <rustywind arguments>
    npm install -g rustywind
  9. How UtilityMap works

    master

    The UtilityMap uses a two-tier approach to resolve Tailwind utility classes to their underlying CSS properties:

    • Tier 1: Exact Matches: A high-performance lookup (using ahash::AHashMap) for static utilities that do not change based on parameters. For example, container maps to --tw-container-component and flex maps to display.
    • Tier 2: Pattern Matching: For utilities that include values (like grid-cols-12 or p-4) or arbitrary values (like bg-[#000]), the map uses pattern matching to identify the base property (e.g., grid-template-columns or background-color).
  10. Sort Tailwind CSS classes with RustyWind

    master

    RustyWind is used to sort Tailwind CSS classes within language-aware source documents. To ensure the sorter correctly distinguishes between static class text and embedded template code (like logic inside JSX or template literals), you should use the RustyWind::sort_document method with a SourceDocument instance. This allows the tool to respect the specific syntax of the language being processed.

    // Conceptual usage pattern
    let document = SourceDocument::new(content, SourceLanguage::Jsx);
    let report = RustyWind::sort_document(document);
  11. Use different sorting strategies with `Sorter`

    master

    The RustyWind instance can be configured with different sorting behaviors via the sorter field:

    1. Sorter::PatternSorter: Uses a predefined pattern-based approach (e.g., sorting by category like margin < display < padding < variants).
    2. Custom Sorter: You can provide a custom sorter using Sorter::new(HashMap<String, i32>), where the HashMap maps class names to an integer weight. Lower weights are sorted earlier.

    Example of a custom weight-based sorter:

    use std::collections::HashMap;
    
    let mut weights = HashMap::new();
    weights.insert("grid-cols-[1fr,2fr]".to_string(), 1);
    weights.insert("flex".to_string(), 0);
    
    let app = RustyWind {
        sorter: Sorter::new(weights),
        class_wrapping: ClassWrapping::CommaSingleQuotes,
        ..RUSTYWIND_DEFAULT
    };
  12. Handle wrapped class lists (comma-separated)

    master

    RustyWind supports class lists that are wrapped in quotes and separated by commas (e.g., 'class1', 'class2'). This is configured via the class_wrapping field in the RustyWind struct using ClassWrapping::CommaSingleQuotes or ClassWrapping::CommaDoubleQuotes.

    When this mode is active, the sorter parses the wrapped tokens, sorts them, deduplicates them (if configured), and re-renders them with the original quoting style.