tailwhip 0.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tailwhip-0.9/LICENSE +25 -0
- tailwhip-0.9/PKG-INFO +179 -0
- tailwhip-0.9/README.md +150 -0
- tailwhip-0.9/pyproject.toml +82 -0
- tailwhip-0.9/setup.cfg +4 -0
- tailwhip-0.9/tailwhip/__init__.py +11 -0
- tailwhip-0.9/tailwhip/__main__.py +8 -0
- tailwhip-0.9/tailwhip/cli.py +158 -0
- tailwhip-0.9/tailwhip/configuration.py +119 -0
- tailwhip-0.9/tailwhip/configuration.toml +427 -0
- tailwhip-0.9/tailwhip/files.py +193 -0
- tailwhip-0.9/tailwhip/process.py +235 -0
- tailwhip-0.9/tailwhip/sorting.py +231 -0
- tailwhip-0.9/tailwhip/tests/__init__.py +1 -0
- tailwhip-0.9/tailwhip/tests/test_cli.py +240 -0
- tailwhip-0.9/tailwhip/tests/test_file_finder.py +230 -0
- tailwhip-0.9/tailwhip/tests/test_sorting.py +465 -0
- tailwhip-0.9/tailwhip/tests/test_write.py +114 -0
- tailwhip-0.9/tailwhip.egg-info/PKG-INFO +179 -0
- tailwhip-0.9/tailwhip.egg-info/SOURCES.txt +22 -0
- tailwhip-0.9/tailwhip.egg-info/dependency_links.txt +1 -0
- tailwhip-0.9/tailwhip.egg-info/entry_points.txt +2 -0
- tailwhip-0.9/tailwhip.egg-info/requires.txt +4 -0
- tailwhip-0.9/tailwhip.egg-info/top_level.txt +1 -0
tailwhip-0.9/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Copyright (c) 2025, Martin Mahner <https://elephant.house/>
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
|
11
|
+
and/or other materials provided with the distribution.
|
|
12
|
+
* Neither the name django-markup nor the names of its contributors
|
|
13
|
+
may be used to endorse or promote products derived from this software without
|
|
14
|
+
specific prior written permission.
|
|
15
|
+
|
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
17
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
18
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
20
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
21
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
22
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
23
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
24
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
25
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
tailwhip-0.9/PKG-INFO
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tailwhip
|
|
3
|
+
Version: 0.9
|
|
4
|
+
Summary: A pure Python Tailwind CSS class sorter that works with any HTML or CSS file, including Django templates and other template languages.
|
|
5
|
+
Author-email: Martin Mahner <martin@mahner.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bartTC/tailwhip/
|
|
8
|
+
Project-URL: Repository, https://github.com/bartTC/tailwhip/
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: dynaconf
|
|
25
|
+
Requires-Dist: rich
|
|
26
|
+
Requires-Dist: typer
|
|
27
|
+
Requires-Dist: wcmatch
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# Tailwhip — Tailwind CSS class sorter
|
|
31
|
+
|
|
32
|
+
[](https://github.com/bartTC/tailwhip/actions/workflows/test.yml)
|
|
33
|
+
[](https://www.python.org/downloads/)
|
|
34
|
+
[](https://opensource.org/licenses/MIT)
|
|
35
|
+
|
|
36
|
+
Tailwhip is a pure Python Tailwind CSS class sorter that works with any HTML or CSS
|
|
37
|
+
file — including Django templates and other templating languages.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
## Why Tailwhip?
|
|
42
|
+
|
|
43
|
+
The [official Prettier plugin][1] for sorting Tailwind classes doesn’t play well
|
|
44
|
+
with many template languages, such as Django. While there are Prettier plugins that
|
|
45
|
+
add limited support for [Jinja templates][2], they often require configuration
|
|
46
|
+
workarounds or restrict what you can do with Prettier.
|
|
47
|
+
|
|
48
|
+
Tailwhip takes a more pragmatic approach. Instead of trying to parse and understand
|
|
49
|
+
every possible template syntax, it focuses on sorting Tailwind classes reliably, and
|
|
50
|
+
ignores class attributes that contain template syntax.
|
|
51
|
+
|
|
52
|
+
[1]: https://github.com/tailwindlabs/prettier-plugin-tailwindcss
|
|
53
|
+
[2]: https://github.com/davidodenwald/prettier-plugin-jinja-template
|
|
54
|
+
|
|
55
|
+
How it works:
|
|
56
|
+
|
|
57
|
+
1. It finds all `class=""` attributes and `@apply` directives in the given files.
|
|
58
|
+
2. It sorts the contained classes according to the official Tailwind CSS class order.
|
|
59
|
+
3. If a class attribute contains template syntax (e.g., `{{ ... }}` or `{% ... %}`),
|
|
60
|
+
Tailwhip leaves it untouched.
|
|
61
|
+
|
|
62
|
+
This approach ensures Tailwhip works across diverse environments — Django, Flask,
|
|
63
|
+
Jinja2, or even custom templating engines — without breaking your templates or
|
|
64
|
+
requiring complicated setup.
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
Tailwhip requires Python 3.11 or later.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
$ uvx tailwhip [options] [filepath...]
|
|
72
|
+
|
|
73
|
+
# Find all .html and .css files in the templates directory
|
|
74
|
+
$ uvx tailwhip templates/
|
|
75
|
+
|
|
76
|
+
# Preview changes
|
|
77
|
+
$ uvx tailwhip templates/ -vv
|
|
78
|
+
|
|
79
|
+
# Actually apply changes
|
|
80
|
+
$ uvx tailwhip templates/ --write
|
|
81
|
+
|
|
82
|
+
# Sort classes in .scss files
|
|
83
|
+
$ uvx tailwhip "templates/**/*.scss"
|
|
84
|
+
|
|
85
|
+
# Standard glob patterns are supported
|
|
86
|
+
$ uvx tailwhip "static/**/*.{css,scss}" "templates/**/*.htm[l]"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
You can also install it with pip and use it as a Python library:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
$ pip install tailwhip
|
|
93
|
+
|
|
94
|
+
$ tailwhip templates/
|
|
95
|
+
$ python -m tailwhip templates/
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
See `--help` for all options and features.
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
Tailwhip works great out of the box with sensible defaults, but you can customize its behavior to match your project's needs. There are two ways to configure Tailwhip:
|
|
103
|
+
|
|
104
|
+
### Option 1: `pyproject.toml`
|
|
105
|
+
|
|
106
|
+
Add a `[tool.tailwhip]` section to your project's `pyproject.toml`:
|
|
107
|
+
|
|
108
|
+
```toml
|
|
109
|
+
[tool.tailwhip]
|
|
110
|
+
# Increase verbosity to see detailed changes
|
|
111
|
+
verbosity = 2
|
|
112
|
+
|
|
113
|
+
# Customize file patterns to include JSX/TSX files
|
|
114
|
+
default_globs = [
|
|
115
|
+
"**/*.html",
|
|
116
|
+
"**/*.css",
|
|
117
|
+
"**/*.jsx",
|
|
118
|
+
"**/*.tsx",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# Add your custom Tailwind colors
|
|
122
|
+
custom_colors = ["brand", "accent", "company"]
|
|
123
|
+
|
|
124
|
+
# Add template syntax for your templating engine
|
|
125
|
+
skip_expressions = ["{{", "{%", "<%", "[[", "]]"]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Option 2: Custom Configuration File
|
|
129
|
+
|
|
130
|
+
Create a `tailwhip.toml` file anywhere in your project and pass it via the `--configuration` flag:
|
|
131
|
+
|
|
132
|
+
```toml
|
|
133
|
+
# tailwhip.toml
|
|
134
|
+
verbosity = 3
|
|
135
|
+
|
|
136
|
+
default_globs = [
|
|
137
|
+
"**/*.html",
|
|
138
|
+
"**/*.css",
|
|
139
|
+
"**/*.liquid", # Shopify Liquid templates
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
custom_colors = ["primary", "secondary", "accent"]
|
|
143
|
+
|
|
144
|
+
skip_expressions = ["{{", "{%", "<%", "{-"] # Add Nunjucks syntax
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Then use it with:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
$ tailwhip templates/ --configuration=tailwhip.toml
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Configuration Precedence
|
|
154
|
+
|
|
155
|
+
Settings are loaded in this order (later sources override earlier ones):
|
|
156
|
+
|
|
157
|
+
1. **Default values** (built into Tailwhip)
|
|
158
|
+
2. **`pyproject.toml`** (`[tool.tailwhip]` section)
|
|
159
|
+
3. **Custom config file** (via `--configuration` flag)
|
|
160
|
+
4. **CLI arguments** (e.g., `--write`, `-v`, `--quiet`)
|
|
161
|
+
|
|
162
|
+
CLI arguments always take precedence, so you can override any config value on the command line.
|
|
163
|
+
|
|
164
|
+
### Available Configuration Options
|
|
165
|
+
|
|
166
|
+
For a complete list of all configuration options with detailed explanations, see the [default configuration file](https://github.com/bartTC/tailwhip/blob/main/tailwhip/configuration.toml). It includes:
|
|
167
|
+
|
|
168
|
+
- **Output settings**: `verbosity`, `write_mode`
|
|
169
|
+
- **File discovery**: `default_globs`
|
|
170
|
+
- **Template handling**: `skip_expressions`
|
|
171
|
+
- **Sorting behavior**: `utility_groups`, `variant_groups`
|
|
172
|
+
- **Color recognition**: `tailwind_colors`, `custom_colors`
|
|
173
|
+
- **Pattern matching**: `class_regex`, `apply_regex` (advanced)
|
|
174
|
+
|
|
175
|
+
Most users only need to customize `custom_colors` and occasionally `default_globs` or `skip_expressions`. The sorting algorithm is based on Tailwind best practices and rarely needs modification.
|
|
176
|
+
|
|
177
|
+
## Changelog
|
|
178
|
+
|
|
179
|
+
See [CHANGELOG.md](CHANGELOG.md) for a complete list of changes and version history.
|
tailwhip-0.9/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Tailwhip — Tailwind CSS class sorter
|
|
2
|
+
|
|
3
|
+
[](https://github.com/bartTC/tailwhip/actions/workflows/test.yml)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Tailwhip is a pure Python Tailwind CSS class sorter that works with any HTML or CSS
|
|
8
|
+
file — including Django templates and other templating languages.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Why Tailwhip?
|
|
13
|
+
|
|
14
|
+
The [official Prettier plugin][1] for sorting Tailwind classes doesn’t play well
|
|
15
|
+
with many template languages, such as Django. While there are Prettier plugins that
|
|
16
|
+
add limited support for [Jinja templates][2], they often require configuration
|
|
17
|
+
workarounds or restrict what you can do with Prettier.
|
|
18
|
+
|
|
19
|
+
Tailwhip takes a more pragmatic approach. Instead of trying to parse and understand
|
|
20
|
+
every possible template syntax, it focuses on sorting Tailwind classes reliably, and
|
|
21
|
+
ignores class attributes that contain template syntax.
|
|
22
|
+
|
|
23
|
+
[1]: https://github.com/tailwindlabs/prettier-plugin-tailwindcss
|
|
24
|
+
[2]: https://github.com/davidodenwald/prettier-plugin-jinja-template
|
|
25
|
+
|
|
26
|
+
How it works:
|
|
27
|
+
|
|
28
|
+
1. It finds all `class=""` attributes and `@apply` directives in the given files.
|
|
29
|
+
2. It sorts the contained classes according to the official Tailwind CSS class order.
|
|
30
|
+
3. If a class attribute contains template syntax (e.g., `{{ ... }}` or `{% ... %}`),
|
|
31
|
+
Tailwhip leaves it untouched.
|
|
32
|
+
|
|
33
|
+
This approach ensures Tailwhip works across diverse environments — Django, Flask,
|
|
34
|
+
Jinja2, or even custom templating engines — without breaking your templates or
|
|
35
|
+
requiring complicated setup.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Tailwhip requires Python 3.11 or later.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
$ uvx tailwhip [options] [filepath...]
|
|
43
|
+
|
|
44
|
+
# Find all .html and .css files in the templates directory
|
|
45
|
+
$ uvx tailwhip templates/
|
|
46
|
+
|
|
47
|
+
# Preview changes
|
|
48
|
+
$ uvx tailwhip templates/ -vv
|
|
49
|
+
|
|
50
|
+
# Actually apply changes
|
|
51
|
+
$ uvx tailwhip templates/ --write
|
|
52
|
+
|
|
53
|
+
# Sort classes in .scss files
|
|
54
|
+
$ uvx tailwhip "templates/**/*.scss"
|
|
55
|
+
|
|
56
|
+
# Standard glob patterns are supported
|
|
57
|
+
$ uvx tailwhip "static/**/*.{css,scss}" "templates/**/*.htm[l]"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You can also install it with pip and use it as a Python library:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
$ pip install tailwhip
|
|
64
|
+
|
|
65
|
+
$ tailwhip templates/
|
|
66
|
+
$ python -m tailwhip templates/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
See `--help` for all options and features.
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
Tailwhip works great out of the box with sensible defaults, but you can customize its behavior to match your project's needs. There are two ways to configure Tailwhip:
|
|
74
|
+
|
|
75
|
+
### Option 1: `pyproject.toml`
|
|
76
|
+
|
|
77
|
+
Add a `[tool.tailwhip]` section to your project's `pyproject.toml`:
|
|
78
|
+
|
|
79
|
+
```toml
|
|
80
|
+
[tool.tailwhip]
|
|
81
|
+
# Increase verbosity to see detailed changes
|
|
82
|
+
verbosity = 2
|
|
83
|
+
|
|
84
|
+
# Customize file patterns to include JSX/TSX files
|
|
85
|
+
default_globs = [
|
|
86
|
+
"**/*.html",
|
|
87
|
+
"**/*.css",
|
|
88
|
+
"**/*.jsx",
|
|
89
|
+
"**/*.tsx",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
# Add your custom Tailwind colors
|
|
93
|
+
custom_colors = ["brand", "accent", "company"]
|
|
94
|
+
|
|
95
|
+
# Add template syntax for your templating engine
|
|
96
|
+
skip_expressions = ["{{", "{%", "<%", "[[", "]]"]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Option 2: Custom Configuration File
|
|
100
|
+
|
|
101
|
+
Create a `tailwhip.toml` file anywhere in your project and pass it via the `--configuration` flag:
|
|
102
|
+
|
|
103
|
+
```toml
|
|
104
|
+
# tailwhip.toml
|
|
105
|
+
verbosity = 3
|
|
106
|
+
|
|
107
|
+
default_globs = [
|
|
108
|
+
"**/*.html",
|
|
109
|
+
"**/*.css",
|
|
110
|
+
"**/*.liquid", # Shopify Liquid templates
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
custom_colors = ["primary", "secondary", "accent"]
|
|
114
|
+
|
|
115
|
+
skip_expressions = ["{{", "{%", "<%", "{-"] # Add Nunjucks syntax
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Then use it with:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
$ tailwhip templates/ --configuration=tailwhip.toml
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Configuration Precedence
|
|
125
|
+
|
|
126
|
+
Settings are loaded in this order (later sources override earlier ones):
|
|
127
|
+
|
|
128
|
+
1. **Default values** (built into Tailwhip)
|
|
129
|
+
2. **`pyproject.toml`** (`[tool.tailwhip]` section)
|
|
130
|
+
3. **Custom config file** (via `--configuration` flag)
|
|
131
|
+
4. **CLI arguments** (e.g., `--write`, `-v`, `--quiet`)
|
|
132
|
+
|
|
133
|
+
CLI arguments always take precedence, so you can override any config value on the command line.
|
|
134
|
+
|
|
135
|
+
### Available Configuration Options
|
|
136
|
+
|
|
137
|
+
For a complete list of all configuration options with detailed explanations, see the [default configuration file](https://github.com/bartTC/tailwhip/blob/main/tailwhip/configuration.toml). It includes:
|
|
138
|
+
|
|
139
|
+
- **Output settings**: `verbosity`, `write_mode`
|
|
140
|
+
- **File discovery**: `default_globs`
|
|
141
|
+
- **Template handling**: `skip_expressions`
|
|
142
|
+
- **Sorting behavior**: `utility_groups`, `variant_groups`
|
|
143
|
+
- **Color recognition**: `tailwind_colors`, `custom_colors`
|
|
144
|
+
- **Pattern matching**: `class_regex`, `apply_regex` (advanced)
|
|
145
|
+
|
|
146
|
+
Most users only need to customize `custom_colors` and occasionally `default_globs` or `skip_expressions`. The sorting algorithm is based on Tailwind best practices and rarely needs modification.
|
|
147
|
+
|
|
148
|
+
## Changelog
|
|
149
|
+
|
|
150
|
+
See [CHANGELOG.md](CHANGELOG.md) for a complete list of changes and version history.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tailwhip"
|
|
3
|
+
version = "0.9"
|
|
4
|
+
description = "A pure Python Tailwind CSS class sorter that works with any HTML or CSS file, including Django templates and other template languages."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Martin Mahner", email = "martin@mahner.org" }
|
|
8
|
+
]
|
|
9
|
+
license = "MIT"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
13
|
+
"Environment :: Web Environment",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Programming Language :: Python",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
]
|
|
24
|
+
requires-python = ">=3.11"
|
|
25
|
+
dependencies = [
|
|
26
|
+
"dynaconf",
|
|
27
|
+
"rich",
|
|
28
|
+
"typer",
|
|
29
|
+
"wcmatch",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/bartTC/tailwhip/"
|
|
34
|
+
Repository = "https://github.com/bartTC/tailwhip/"
|
|
35
|
+
|
|
36
|
+
[dependency-groups]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest",
|
|
39
|
+
"pytest-cov",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
tailwhip = "tailwhip:main"
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.package-data]
|
|
46
|
+
tailwhip = ["configuration.toml"]
|
|
47
|
+
|
|
48
|
+
[tool.uv]
|
|
49
|
+
package = true
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
target-version = "py39"
|
|
53
|
+
lint.select = ["ALL"]
|
|
54
|
+
lint.ignore = [
|
|
55
|
+
# Valid exclusions.
|
|
56
|
+
"COM812", # Conflicts with ruff format
|
|
57
|
+
"E501", # Line too long (>88)
|
|
58
|
+
"D203", # incorrect-blank-line-before-class (conflicts with D211)
|
|
59
|
+
"D212", # Don't require summary on first line
|
|
60
|
+
"ERA001", # Found commented-out code
|
|
61
|
+
"FBT", # Flake Boolean Trap (don't use arg=True in functions)
|
|
62
|
+
"FIX", # Line contains TODO/FIXME. Consider resolving.
|
|
63
|
+
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint.extend-per-file-ignores]
|
|
67
|
+
"test*.py" = [
|
|
68
|
+
"S101", # Use of `assert` detected
|
|
69
|
+
"PLR2004", # Magic value used in comparison, consider replacing <int> with a constant variable
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
[tool.pytest.ini_options]
|
|
73
|
+
python_files = ["*.py"]
|
|
74
|
+
|
|
75
|
+
[tool.coverage]
|
|
76
|
+
run.omit = [
|
|
77
|
+
"tailwhip/tests/*",
|
|
78
|
+
]
|
|
79
|
+
report.exclude_lines = [
|
|
80
|
+
"pragma: no cover",
|
|
81
|
+
"if TYPE_CHECKING:",
|
|
82
|
+
]
|
tailwhip-0.9/setup.cfg
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Tailwhip - Sort Tailwind CSS classes in HTML and CSS files."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib import metadata
|
|
6
|
+
|
|
7
|
+
from tailwhip.cli import main
|
|
8
|
+
|
|
9
|
+
__version__ = metadata.version("tailwhip")
|
|
10
|
+
__author__ = "Martin Mahner"
|
|
11
|
+
__all__ = ["__author__", "__version__", "main"]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""CLI entrypoint."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from importlib import metadata
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Annotated
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
from tailwhip.configuration import (
|
|
15
|
+
CONSOLE_THEME,
|
|
16
|
+
VerbosityLevel,
|
|
17
|
+
config,
|
|
18
|
+
get_pyproject_toml_data,
|
|
19
|
+
update_configuration,
|
|
20
|
+
)
|
|
21
|
+
from tailwhip.files import apply_changes, find_files
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def version_callback(value: bool) -> None:
|
|
25
|
+
"""Print version and exit."""
|
|
26
|
+
if value:
|
|
27
|
+
version = metadata.version("tailwhip")
|
|
28
|
+
typer.echo(f"tailwhip {version}")
|
|
29
|
+
raise typer.Exit
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
app = typer.Typer(
|
|
33
|
+
help="Sort Tailwind CSS classes in HTML and CSS files.",
|
|
34
|
+
add_completion=False,
|
|
35
|
+
rich_markup_mode="rich",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> None:
|
|
40
|
+
"""Entrypoint for the CLI."""
|
|
41
|
+
app()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.command(context_settings={"help_option_names": ["-h", "--help"]})
|
|
45
|
+
def run( # noqa: PLR0913
|
|
46
|
+
paths: Annotated[
|
|
47
|
+
list[Path],
|
|
48
|
+
typer.Argument(
|
|
49
|
+
help="Files or directories to process.",
|
|
50
|
+
metavar="PATH",
|
|
51
|
+
),
|
|
52
|
+
],
|
|
53
|
+
version: Annotated[ # noqa: ARG001
|
|
54
|
+
bool,
|
|
55
|
+
typer.Option(
|
|
56
|
+
"--version",
|
|
57
|
+
"-V",
|
|
58
|
+
callback=version_callback,
|
|
59
|
+
is_eager=True,
|
|
60
|
+
help="Show version and exit.",
|
|
61
|
+
),
|
|
62
|
+
] = False,
|
|
63
|
+
write_mode: Annotated[
|
|
64
|
+
bool,
|
|
65
|
+
typer.Option(
|
|
66
|
+
"--write",
|
|
67
|
+
"-w",
|
|
68
|
+
help="Write changes to files (default: dry-run mode).",
|
|
69
|
+
),
|
|
70
|
+
] = False,
|
|
71
|
+
quiet: Annotated[
|
|
72
|
+
bool,
|
|
73
|
+
typer.Option(
|
|
74
|
+
"--quiet",
|
|
75
|
+
"-q",
|
|
76
|
+
help="Suppress output except errors and warnings.",
|
|
77
|
+
),
|
|
78
|
+
] = False,
|
|
79
|
+
verbosity: Annotated[
|
|
80
|
+
int,
|
|
81
|
+
typer.Option(
|
|
82
|
+
"--verbose",
|
|
83
|
+
"-v",
|
|
84
|
+
count=True,
|
|
85
|
+
help="Increase output verbosity (-v: changes, -vv: diff, -vvv: debug).",
|
|
86
|
+
),
|
|
87
|
+
] = 0,
|
|
88
|
+
custom_configuration_file: Annotated[
|
|
89
|
+
Path | None,
|
|
90
|
+
typer.Option(
|
|
91
|
+
"--configuration",
|
|
92
|
+
"-c",
|
|
93
|
+
help="Load custom configuration file (overrides pyproject.toml settings).",
|
|
94
|
+
metavar="FILE",
|
|
95
|
+
),
|
|
96
|
+
] = None,
|
|
97
|
+
) -> None:
|
|
98
|
+
"""
|
|
99
|
+
Sort Tailwind CSS classes in HTML and CSS files.
|
|
100
|
+
|
|
101
|
+
Automatically discovers and sorts Tailwind classes according to a consistent
|
|
102
|
+
ordering. Supports HTML, CSS, and template files with Tailwind @apply directives.
|
|
103
|
+
"""
|
|
104
|
+
# Check if the given configuration file exists
|
|
105
|
+
if custom_configuration_file and not custom_configuration_file.exists():
|
|
106
|
+
typer.echo(
|
|
107
|
+
f"Custom configuration file {custom_configuration_file} not found.",
|
|
108
|
+
err=True,
|
|
109
|
+
)
|
|
110
|
+
raise typer.Exit(1)
|
|
111
|
+
|
|
112
|
+
# Setup configuration values -------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
# 1. Overwrite config defaults with pyproject.toml settings if they exist
|
|
115
|
+
if pyproject_data := get_pyproject_toml_data(Path.cwd()):
|
|
116
|
+
update_configuration(pyproject_data)
|
|
117
|
+
|
|
118
|
+
# 2. Overwrite config defaults with custom settings if they exist
|
|
119
|
+
if custom_configuration_file:
|
|
120
|
+
update_configuration(custom_configuration_file)
|
|
121
|
+
|
|
122
|
+
# 3. Overwrite config defaults with CLI options
|
|
123
|
+
update_configuration(
|
|
124
|
+
{
|
|
125
|
+
"write_mode": write_mode,
|
|
126
|
+
"verbosity": 0 if quiet else verbosity + 1,
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Setup helper tools ---------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
config.console = Console(quiet=quiet, theme=CONSOLE_THEME)
|
|
133
|
+
|
|
134
|
+
# Start Tailwhipping ---------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
start_time = time.time()
|
|
137
|
+
found_any, skipped, changed = apply_changes(targets=find_files(paths=paths))
|
|
138
|
+
duration = time.time() - start_time
|
|
139
|
+
|
|
140
|
+
if not found_any:
|
|
141
|
+
config.console.print("[red]Error: No files found[/red]")
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
|
|
144
|
+
if config.verbosity == VerbosityLevel.NORMAL:
|
|
145
|
+
config.console.print(
|
|
146
|
+
"\nUse [important] -v [/important] (show unchanged files) or [important] -vv [/important] (show diff preview) for more detail."
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if not config.write_mode:
|
|
150
|
+
config.console.print(
|
|
151
|
+
"\n:warning: Dry Run. No files were actually written. "
|
|
152
|
+
"Use [important] --write [/important] to write changes."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
config.console.print(
|
|
156
|
+
f"⏱ Completed in [bold]{duration:.3f}s[/bold] for {changed} files. [dim]({skipped} skipped)[/dim]",
|
|
157
|
+
highlight=False,
|
|
158
|
+
)
|