@youthfulhps/prettier-plugin-tailwindcss-normalizer 0.3.10 → 0.5.2
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.
- package/README.md +117 -96
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -2
- package/dist/normalizer.d.ts +2 -0
- package/dist/normalizer.js +158 -11
- package/dist/types.d.ts +3 -0
- package/dist/utils/spacing-generator.d.ts +2 -0
- package/dist/utils/spacing-generator.js +116 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,31 +5,26 @@
|
|
|
5
5
|
|
|
6
6
|
A Prettier plugin that automatically normalizes Tailwind CSS arbitrary values into standard utility classes, helping maintain consistent and optimized CSS across your project.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
**Requires Prettier v3.0.0 or higher** (Prettier v2 is not supported)
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Features
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
12
|
+
- Converts arbitrary values like `p-[4px]` to standard classes like `p-1`
|
|
13
|
+
- Works with HTML, React/JSX, Vue.js, and Angular
|
|
14
|
+
- Only transforms class attributes, leaving comments, text, and other attributes untouched
|
|
15
|
+
- Supports padding, margin, sizing, typography, borders, shadows, and more
|
|
16
|
+
- Supports all Tailwind CSS variants (responsive, state, dark mode, group, peer, etc.)
|
|
17
17
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
**Prerequisites**: Make sure you have Prettier v3.0.0 or higher installed.
|
|
18
|
+
## Installation
|
|
21
19
|
|
|
22
20
|
```bash
|
|
23
|
-
# Install Prettier v3+ (if not already installed)
|
|
24
21
|
npm install --save-dev prettier@^3.0.0
|
|
25
|
-
|
|
26
|
-
# Install the plugin
|
|
27
22
|
npm install --save-dev @youthfulhps/prettier-plugin-tailwindcss-normalizer
|
|
28
23
|
```
|
|
29
24
|
|
|
30
|
-
##
|
|
25
|
+
## Usage
|
|
31
26
|
|
|
32
|
-
###
|
|
27
|
+
### Basic Configuration
|
|
33
28
|
|
|
34
29
|
Add the plugin to your Prettier configuration:
|
|
35
30
|
|
|
@@ -49,7 +44,7 @@ module.exports = {
|
|
|
49
44
|
};
|
|
50
45
|
```
|
|
51
46
|
|
|
52
|
-
###
|
|
47
|
+
### Running Prettier
|
|
53
48
|
|
|
54
49
|
```bash
|
|
55
50
|
# Format all files
|
|
@@ -59,35 +54,29 @@ npx prettier --write .
|
|
|
59
54
|
npx prettier --write src/**/*.{tsx,jsx,html,vue}
|
|
60
55
|
```
|
|
61
56
|
|
|
62
|
-
##
|
|
57
|
+
## Using with Other Prettier Tailwind Plugins
|
|
63
58
|
|
|
64
|
-
To use this plugin alongside other prettier tailwind plugins
|
|
65
|
-
|
|
66
|
-
### Installation
|
|
59
|
+
To use this plugin alongside other prettier tailwind plugins (like `prettier-plugin-tailwindcss`), install and configure `prettier-plugin-merge`:
|
|
67
60
|
|
|
68
61
|
```bash
|
|
69
62
|
npm install --save-dev prettier-plugin-merge
|
|
70
63
|
```
|
|
71
64
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
In your `.prettierrc.js` file, add `prettier-plugin-merge` as the last item in the plugins array:
|
|
65
|
+
**`.prettierrc.js`**
|
|
75
66
|
|
|
76
67
|
```javascript
|
|
77
68
|
module.exports = {
|
|
78
69
|
plugins: [
|
|
79
70
|
"@youthfulhps/prettier-plugin-tailwindcss-normalizer",
|
|
80
71
|
"prettier-plugin-tailwindcss",
|
|
81
|
-
"prettier-plugin-merge",
|
|
72
|
+
"prettier-plugin-merge", // Must be last
|
|
82
73
|
],
|
|
83
74
|
};
|
|
84
75
|
```
|
|
85
76
|
|
|
86
|
-
|
|
77
|
+
## Examples
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### Before
|
|
79
|
+
**Before:**
|
|
91
80
|
|
|
92
81
|
```jsx
|
|
93
82
|
<div className="p-[16px] m-[8px] bg-blue-500">
|
|
@@ -95,7 +84,7 @@ module.exports = {
|
|
|
95
84
|
</div>
|
|
96
85
|
```
|
|
97
86
|
|
|
98
|
-
|
|
87
|
+
**After:**
|
|
99
88
|
|
|
100
89
|
```jsx
|
|
101
90
|
<div className="p-4 m-2 bg-blue-500">
|
|
@@ -103,86 +92,131 @@ module.exports = {
|
|
|
103
92
|
</div>
|
|
104
93
|
```
|
|
105
94
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
The plugin is designed to be safe and only transforms class-related attributes:
|
|
109
|
-
|
|
110
|
-
### ✅ What Gets Transformed
|
|
111
|
-
|
|
112
|
-
- `className` attributes in JSX/TSX
|
|
113
|
-
- `class` attributes in HTML
|
|
114
|
-
- `:class` and `v-bind:class` in Vue
|
|
115
|
-
- `[class]` in Angular
|
|
116
|
-
- String literals in template literals
|
|
117
|
-
- Function calls like `clsx()`, `classnames()`, `cn()`
|
|
95
|
+
**With Variants:**
|
|
118
96
|
|
|
119
|
-
|
|
97
|
+
```jsx
|
|
98
|
+
// Before
|
|
99
|
+
<div className="md:p-[16px] hover:m-[8px] dark:bg-[#1f2937]">
|
|
100
|
+
Content
|
|
101
|
+
</div>
|
|
120
102
|
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
103
|
+
// After
|
|
104
|
+
<div className="md:p-4 hover:m-2 dark:bg-[#1f2937]">
|
|
105
|
+
Content
|
|
106
|
+
</div>
|
|
107
|
+
```
|
|
126
108
|
|
|
127
|
-
##
|
|
109
|
+
## Supported Mappings
|
|
128
110
|
|
|
129
111
|
### Spacing
|
|
130
112
|
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
113
|
+
- Padding: `p-[4px]` → `p-1`, `px-[16px]` → `px-4`
|
|
114
|
+
- Margin: `m-[8px]` → `m-2`, `my-[12px]` → `my-3`
|
|
115
|
+
- Gap: `gap-[8px]` → `gap-2`, `gap-x-[16px]` → `gap-x-4`
|
|
134
116
|
|
|
135
117
|
### Sizing
|
|
136
118
|
|
|
137
|
-
-
|
|
138
|
-
-
|
|
119
|
+
- Width: `w-[100px]` → `w-25`, `w-[200px]` → `w-50`
|
|
120
|
+
- Height: `h-[50px]` → `h-12.5`, `h-[100px]` → `h-25`
|
|
139
121
|
|
|
140
122
|
### Typography
|
|
141
123
|
|
|
142
|
-
-
|
|
124
|
+
- Font Size: `text-[14px]` → `text-sm`, `text-[18px]` → `text-lg`
|
|
125
|
+
- Letter Spacing: `tracking-[-0.05em]` → `tracking-tighter`
|
|
143
126
|
|
|
144
127
|
### Layout
|
|
145
128
|
|
|
146
|
-
-
|
|
147
|
-
-
|
|
129
|
+
- Border Radius: `rounded-[4px]` → `rounded`, `rounded-[6px]` → `rounded-md`
|
|
130
|
+
- Border Width: `border-[1px]` → `border`, `border-[2px]` → `border-2`
|
|
148
131
|
|
|
149
132
|
### Effects
|
|
150
133
|
|
|
151
|
-
-
|
|
152
|
-
-
|
|
134
|
+
- Box Shadow: `shadow-[0_1px_3px_rgba(0,0,0,0.1)]` → `shadow-sm`
|
|
135
|
+
- Opacity: `opacity-[0.5]` → `opacity-50`
|
|
153
136
|
|
|
154
|
-
|
|
137
|
+
### Transforms
|
|
155
138
|
|
|
156
|
-
|
|
139
|
+
- Rotate: `rotate-[-180deg]` → `-rotate-180`
|
|
140
|
+
- Translate: `translate-x-[-100%]` → `-translate-x-full`
|
|
157
141
|
|
|
158
|
-
|
|
142
|
+
## Configuration
|
|
159
143
|
|
|
160
|
-
|
|
144
|
+
### Custom Spacing Unit
|
|
161
145
|
|
|
162
|
-
|
|
146
|
+
By default, Tailwind uses **4px** as the base spacing unit (e.g., `p-1` = 4px, `p-2` = 8px). If you've customized your Tailwind spacing scale, configure the `customSpacingUnit` option:
|
|
163
147
|
|
|
164
|
-
|
|
165
|
-
- Edge cases and complex scenarios
|
|
166
|
-
- Safety features and non-transformation cases
|
|
167
|
-
- Different Tailwind CSS patterns
|
|
148
|
+
**`.prettierrc.js`**
|
|
168
149
|
|
|
169
|
-
|
|
150
|
+
```javascript
|
|
151
|
+
module.exports = {
|
|
152
|
+
plugins: ["@youthfulhps/prettier-plugin-tailwindcss-normalizer"],
|
|
153
|
+
customSpacingUnit: 8, // Change to match your Tailwind spacing scale
|
|
154
|
+
};
|
|
155
|
+
```
|
|
170
156
|
|
|
171
|
-
|
|
172
|
-
|
|
157
|
+
**Example with `customSpacingUnit: 8`:**
|
|
158
|
+
|
|
159
|
+
```jsx
|
|
160
|
+
// Before
|
|
161
|
+
<div className="p-[8px] m-[16px] gap-[24px]">Content</div>
|
|
162
|
+
|
|
163
|
+
// After
|
|
164
|
+
<div className="p-1 m-2 gap-3">Content</div>
|
|
173
165
|
```
|
|
174
166
|
|
|
175
|
-
|
|
167
|
+
See the [examples/custom-spacing](./examples/custom-spacing) directory for a complete working example.
|
|
168
|
+
|
|
169
|
+
## Supported Variants
|
|
170
|
+
|
|
171
|
+
The plugin supports all Tailwind CSS variants:
|
|
172
|
+
|
|
173
|
+
- **Responsive**: `sm:`, `md:`, `lg:`, `xl:`, `2xl:`
|
|
174
|
+
- **State**: `hover:`, `focus:`, `active:`, `disabled:`, etc.
|
|
175
|
+
- **Dark Mode**: `dark:`
|
|
176
|
+
- **Group/Peer**: `group-hover:`, `peer-checked:`, etc.
|
|
177
|
+
- **Pseudo-elements**: `before:`, `after:`, `placeholder:`, etc.
|
|
178
|
+
- **ARIA**: `aria-checked:`, `aria-disabled:`, etc.
|
|
179
|
+
- **Data Attributes**: `data-[status=active]:`
|
|
180
|
+
- **Arbitrary**: `[&:nth-child(3)]:`, `has-[input:focus]:`, etc.
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
## Safety Features
|
|
183
|
+
|
|
184
|
+
The plugin only transforms class-related attributes:
|
|
185
|
+
|
|
186
|
+
**Transformed:**
|
|
187
|
+
|
|
188
|
+
- `className` attributes in JSX/TSX
|
|
189
|
+
- `class` attributes in HTML
|
|
190
|
+
- `:class` and `v-bind:class` in Vue
|
|
191
|
+
- `[class]` in Angular
|
|
192
|
+
- String literals in template literals
|
|
193
|
+
- Function calls like `clsx()`, `classnames()`, `cn()`
|
|
184
194
|
|
|
185
|
-
|
|
195
|
+
**Untouched:**
|
|
196
|
+
|
|
197
|
+
- Comments and documentation
|
|
198
|
+
- Regular text content
|
|
199
|
+
- Other HTML attributes
|
|
200
|
+
- JavaScript strings and variables
|
|
201
|
+
- CSS in `<style>` tags
|
|
202
|
+
|
|
203
|
+
## File Support
|
|
204
|
+
|
|
205
|
+
| Format | Extension |
|
|
206
|
+
| --------- | ----------------- |
|
|
207
|
+
| HTML | `.html` |
|
|
208
|
+
| React JSX | `.jsx` |
|
|
209
|
+
| React TSX | `.tsx` |
|
|
210
|
+
| Vue | `.vue` |
|
|
211
|
+
| Angular | `.component.html` |
|
|
212
|
+
|
|
213
|
+
## Testing
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
npm test
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Contributing
|
|
186
220
|
|
|
187
221
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
188
222
|
|
|
@@ -192,17 +226,11 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
192
226
|
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
193
227
|
5. Open a Pull Request
|
|
194
228
|
|
|
195
|
-
##
|
|
229
|
+
## License
|
|
196
230
|
|
|
197
231
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
198
232
|
|
|
199
|
-
##
|
|
200
|
-
|
|
201
|
-
- Built for the Prettier ecosystem
|
|
202
|
-
- Inspired by Tailwind CSS best practices
|
|
203
|
-
- Thanks to all contributors and users
|
|
204
|
-
|
|
205
|
-
## 📞 Support
|
|
233
|
+
## Support
|
|
206
234
|
|
|
207
235
|
If you encounter any issues or have questions:
|
|
208
236
|
|
|
@@ -212,15 +240,8 @@ If you encounter any issues or have questions:
|
|
|
212
240
|
|
|
213
241
|
### Common Issues
|
|
214
242
|
|
|
215
|
-
**Q: The plugin doesn't work with Prettier v2**
|
|
243
|
+
**Q: The plugin doesn't work with Prettier v2**
|
|
216
244
|
A: This plugin requires Prettier v3.0.0 or higher. Please upgrade your Prettier version.
|
|
217
245
|
|
|
218
|
-
**Q: How do I check my Prettier version?**
|
|
246
|
+
**Q: How do I check my Prettier version?**
|
|
219
247
|
A: Run `npx prettier --version` to check your current Prettier version.
|
|
220
|
-
|
|
221
|
-
**Q: Can I use this with Prettier v2?**
|
|
222
|
-
A: No, this plugin is built specifically for Prettier v3+ and will not work with v2.
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
**Made with ❤️ for the developer community**
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ declare const parsers: Record<string, CompatParser>, languages: {
|
|
|
3
3
|
name: string;
|
|
4
4
|
parsers: string[];
|
|
5
5
|
extensions: string[];
|
|
6
|
-
}[] | undefined;
|
|
7
|
-
export { parsers, languages };
|
|
6
|
+
}[] | undefined, options: Record<string, any> | undefined;
|
|
7
|
+
export { parsers, languages, options };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.languages = exports.parsers = void 0;
|
|
3
|
+
exports.options = exports.languages = exports.parsers = void 0;
|
|
4
4
|
const ast_transformer_1 = require("./ast-transformer");
|
|
5
5
|
const version_utils_1 = require("./utils/version-utils");
|
|
6
|
+
const normalizer_1 = require("./normalizer");
|
|
6
7
|
function createParser(parserName, fileType) {
|
|
7
8
|
const baseParser = (0, version_utils_1.safeLoadParser)(parserName, fileType);
|
|
8
9
|
return {
|
|
9
10
|
...baseParser,
|
|
10
11
|
preprocess: (text, options) => {
|
|
12
|
+
const pluginOptions = {
|
|
13
|
+
customSpacingUnit: options.customSpacingUnit,
|
|
14
|
+
};
|
|
15
|
+
(0, normalizer_1.setPluginOptions)(pluginOptions);
|
|
11
16
|
return (0, ast_transformer_1.transformByFileType)(text, options.filepath || `file.${fileType}`);
|
|
12
17
|
},
|
|
13
18
|
};
|
|
@@ -62,7 +67,16 @@ const plugin = {
|
|
|
62
67
|
astFormat: "estree",
|
|
63
68
|
},
|
|
64
69
|
},
|
|
70
|
+
options: {
|
|
71
|
+
customSpacingUnit: {
|
|
72
|
+
type: "int",
|
|
73
|
+
category: "Tailwind",
|
|
74
|
+
default: 4,
|
|
75
|
+
description: "Custom spacing unit in pixels (default: 4). Tailwind uses 4px as the base unit (e.g., p-1 = 4px). Change this if you've customized Tailwind's spacing scale.",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
65
78
|
};
|
|
66
|
-
const { parsers, languages } = plugin;
|
|
79
|
+
const { parsers, languages, options } = plugin;
|
|
67
80
|
exports.parsers = parsers;
|
|
68
81
|
exports.languages = languages;
|
|
82
|
+
exports.options = options;
|
package/dist/normalizer.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PluginOptions } from "./types";
|
|
2
|
+
export declare function setPluginOptions(options?: PluginOptions): void;
|
|
1
3
|
export declare function normalizeClassAttribute(content: string): string;
|
|
2
4
|
export declare function normalizeClassNames(classNames: string): string;
|
|
3
5
|
export declare function normalizeClassName(className: string): string;
|
package/dist/normalizer.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setPluginOptions = setPluginOptions;
|
|
3
4
|
exports.normalizeClassAttribute = normalizeClassAttribute;
|
|
4
5
|
exports.normalizeClassNames = normalizeClassNames;
|
|
5
6
|
exports.normalizeClassName = normalizeClassName;
|
|
6
7
|
const mappings_1 = require("./mappings");
|
|
8
|
+
const spacing_generator_1 = require("./utils/spacing-generator");
|
|
9
|
+
let currentMappings = mappings_1.TAILWIND_MAPPINGS;
|
|
10
|
+
function setPluginOptions(options = {}) {
|
|
11
|
+
if (options.customSpacingUnit && options.customSpacingUnit !== 4) {
|
|
12
|
+
const customSpacingMappings = (0, spacing_generator_1.generateSpacingMappings)(options.customSpacingUnit);
|
|
13
|
+
currentMappings = {
|
|
14
|
+
...mappings_1.TAILWIND_MAPPINGS,
|
|
15
|
+
...customSpacingMappings,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
currentMappings = mappings_1.TAILWIND_MAPPINGS;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
7
22
|
function normalizeClassAttribute(content) {
|
|
8
23
|
let result = content;
|
|
9
24
|
const classAttributeRegex = /class\s*=\s*["']([^"']*?)["']/g;
|
|
@@ -62,18 +77,90 @@ function normalizeClassNames(classNames) {
|
|
|
62
77
|
return classes.map((className) => normalizeClassName(className)).join(" ");
|
|
63
78
|
}
|
|
64
79
|
function normalizeClassName(className) {
|
|
80
|
+
let variantPrefix = "";
|
|
81
|
+
let pos = 0;
|
|
82
|
+
while (pos < className.length) {
|
|
83
|
+
if (className[pos] === "[") {
|
|
84
|
+
const bracketEnd = className.indexOf("]:", pos);
|
|
85
|
+
if (bracketEnd === -1)
|
|
86
|
+
break;
|
|
87
|
+
variantPrefix += className.substring(pos, bracketEnd + 2);
|
|
88
|
+
pos = bracketEnd + 2;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const colonPos = className.indexOf(":", pos);
|
|
92
|
+
if (colonPos === -1)
|
|
93
|
+
break;
|
|
94
|
+
const beforeColon = className.substring(pos, colonPos);
|
|
95
|
+
if (beforeColon.includes("[")) {
|
|
96
|
+
const bracketEnd = className.indexOf("]:", pos);
|
|
97
|
+
if (bracketEnd === -1)
|
|
98
|
+
break;
|
|
99
|
+
variantPrefix += className.substring(pos, bracketEnd + 2);
|
|
100
|
+
pos = bracketEnd + 2;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
variantPrefix += className.substring(pos, colonPos + 1);
|
|
104
|
+
pos = colonPos + 1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (pos >= className.length)
|
|
108
|
+
break;
|
|
109
|
+
if (className[pos] === " ")
|
|
110
|
+
break;
|
|
111
|
+
const nextChar = className[pos];
|
|
112
|
+
if (!/[a-z0-9_\-\[\]]/.test(nextChar)) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const classWithoutVariant = className.substring(variantPrefix.length);
|
|
117
|
+
if (!variantPrefix) {
|
|
118
|
+
return normalizeClassNameWithoutVariant(className);
|
|
119
|
+
}
|
|
120
|
+
const normalizedClass = normalizeClassNameWithoutVariant(classWithoutVariant);
|
|
121
|
+
if (normalizedClass === classWithoutVariant) {
|
|
122
|
+
return className;
|
|
123
|
+
}
|
|
124
|
+
return variantPrefix + normalizedClass;
|
|
125
|
+
}
|
|
126
|
+
function normalizeClassNameWithoutVariant(className) {
|
|
65
127
|
const negativeArbitraryValueRegex = /^-([a-z:-]+)\[([^\]]+)\]$/;
|
|
66
128
|
const negativeArbitraryMatch = className.match(negativeArbitraryValueRegex);
|
|
67
129
|
if (negativeArbitraryMatch) {
|
|
68
130
|
const [, prefixWithDash, value] = negativeArbitraryMatch;
|
|
69
131
|
const prefix = prefixWithDash.replace(/-$/, "");
|
|
132
|
+
const negativeAllowedPrefixes = [
|
|
133
|
+
"m",
|
|
134
|
+
"mx",
|
|
135
|
+
"my",
|
|
136
|
+
"mt",
|
|
137
|
+
"mr",
|
|
138
|
+
"mb",
|
|
139
|
+
"ml",
|
|
140
|
+
"ms",
|
|
141
|
+
"me",
|
|
142
|
+
"top",
|
|
143
|
+
"right",
|
|
144
|
+
"bottom",
|
|
145
|
+
"left",
|
|
146
|
+
"inset",
|
|
147
|
+
"inset-x",
|
|
148
|
+
"inset-y",
|
|
149
|
+
"gap",
|
|
150
|
+
"gap-x",
|
|
151
|
+
"gap-y",
|
|
152
|
+
"space-x",
|
|
153
|
+
"space-y",
|
|
154
|
+
];
|
|
155
|
+
if (!negativeAllowedPrefixes.includes(prefix)) {
|
|
156
|
+
return className;
|
|
157
|
+
}
|
|
70
158
|
if (value === "0px" || value === "0") {
|
|
71
159
|
const mapping = findStandardMapping(prefix, value);
|
|
72
|
-
return mapping
|
|
160
|
+
return mapping ? `-${mapping}` : className;
|
|
73
161
|
}
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
return mapping || className;
|
|
162
|
+
const mapping = findStandardMapping(prefix, value);
|
|
163
|
+
return mapping ? `-${mapping}` : className;
|
|
77
164
|
}
|
|
78
165
|
const arbitraryValueRegex = /^([a-z:-]+)\[([^\]]+)\]$/;
|
|
79
166
|
const arbitraryMatch = className.match(arbitraryValueRegex);
|
|
@@ -86,8 +173,42 @@ function normalizeClassName(className) {
|
|
|
86
173
|
const mapping = findStandardMapping(prefix, positiveValue);
|
|
87
174
|
return mapping || className;
|
|
88
175
|
}
|
|
89
|
-
const
|
|
90
|
-
|
|
176
|
+
const negativeMapping = findStandardMapping(prefix, value);
|
|
177
|
+
if (negativeMapping) {
|
|
178
|
+
return negativeMapping;
|
|
179
|
+
}
|
|
180
|
+
const negativeAllowedPrefixes = [
|
|
181
|
+
"m",
|
|
182
|
+
"mx",
|
|
183
|
+
"my",
|
|
184
|
+
"mt",
|
|
185
|
+
"mr",
|
|
186
|
+
"mb",
|
|
187
|
+
"ml",
|
|
188
|
+
"ms",
|
|
189
|
+
"me",
|
|
190
|
+
"top",
|
|
191
|
+
"right",
|
|
192
|
+
"bottom",
|
|
193
|
+
"left",
|
|
194
|
+
"inset",
|
|
195
|
+
"inset-x",
|
|
196
|
+
"inset-y",
|
|
197
|
+
"gap",
|
|
198
|
+
"gap-x",
|
|
199
|
+
"gap-y",
|
|
200
|
+
"space-x",
|
|
201
|
+
"space-y",
|
|
202
|
+
"rotate",
|
|
203
|
+
"translate-x",
|
|
204
|
+
"translate-y",
|
|
205
|
+
];
|
|
206
|
+
if (negativeAllowedPrefixes.includes(prefix)) {
|
|
207
|
+
const positiveValue = value.substring(1);
|
|
208
|
+
const mapping = findStandardMapping(prefix, positiveValue);
|
|
209
|
+
return mapping ? `-${mapping}` : className;
|
|
210
|
+
}
|
|
211
|
+
return className;
|
|
91
212
|
}
|
|
92
213
|
const mapping = findStandardMapping(prefix, value);
|
|
93
214
|
return mapping || className;
|
|
@@ -96,13 +217,39 @@ function normalizeClassName(className) {
|
|
|
96
217
|
const negativePxMatch = className.match(negativePxSuffixRegex);
|
|
97
218
|
if (negativePxMatch) {
|
|
98
219
|
const [, prefix, numValue] = negativePxMatch;
|
|
220
|
+
const negativeAllowedPrefixes = [
|
|
221
|
+
"m",
|
|
222
|
+
"mx",
|
|
223
|
+
"my",
|
|
224
|
+
"mt",
|
|
225
|
+
"mr",
|
|
226
|
+
"mb",
|
|
227
|
+
"ml",
|
|
228
|
+
"ms",
|
|
229
|
+
"me",
|
|
230
|
+
"top",
|
|
231
|
+
"right",
|
|
232
|
+
"bottom",
|
|
233
|
+
"left",
|
|
234
|
+
"inset",
|
|
235
|
+
"inset-x",
|
|
236
|
+
"inset-y",
|
|
237
|
+
"gap",
|
|
238
|
+
"gap-x",
|
|
239
|
+
"gap-y",
|
|
240
|
+
"space-x",
|
|
241
|
+
"space-y",
|
|
242
|
+
];
|
|
243
|
+
if (!negativeAllowedPrefixes.includes(prefix)) {
|
|
244
|
+
return className;
|
|
245
|
+
}
|
|
99
246
|
if (numValue === "0") {
|
|
100
247
|
const mapping = findStandardMapping(prefix, "0px");
|
|
101
|
-
return mapping
|
|
248
|
+
return mapping ? `-${mapping}` : className;
|
|
102
249
|
}
|
|
103
|
-
const
|
|
104
|
-
const mapping = findStandardMapping(prefix,
|
|
105
|
-
return mapping
|
|
250
|
+
const pixelValue = `${numValue}px`;
|
|
251
|
+
const mapping = findStandardMapping(prefix, pixelValue);
|
|
252
|
+
return mapping ? `-${mapping}` : className;
|
|
106
253
|
}
|
|
107
254
|
const pxSuffixRegex = /^([a-z:-]+)-(\d+)px$/;
|
|
108
255
|
const pxMatch = className.match(pxSuffixRegex);
|
|
@@ -115,7 +262,7 @@ function normalizeClassName(className) {
|
|
|
115
262
|
return className;
|
|
116
263
|
}
|
|
117
264
|
function findStandardMapping(prefix, value) {
|
|
118
|
-
const mappings =
|
|
265
|
+
const mappings = currentMappings[prefix];
|
|
119
266
|
if (!mappings) {
|
|
120
267
|
return null;
|
|
121
268
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSpacingMappings = generateSpacingMappings;
|
|
4
|
+
function generateSpacingMappings(customUnit = 4) {
|
|
5
|
+
const spacingPrefixes = [
|
|
6
|
+
"p",
|
|
7
|
+
"px",
|
|
8
|
+
"py",
|
|
9
|
+
"pt",
|
|
10
|
+
"pr",
|
|
11
|
+
"pb",
|
|
12
|
+
"pl",
|
|
13
|
+
"ps",
|
|
14
|
+
"pe",
|
|
15
|
+
"m",
|
|
16
|
+
"mx",
|
|
17
|
+
"my",
|
|
18
|
+
"mt",
|
|
19
|
+
"mr",
|
|
20
|
+
"mb",
|
|
21
|
+
"ml",
|
|
22
|
+
"ms",
|
|
23
|
+
"me",
|
|
24
|
+
"gap",
|
|
25
|
+
"gap-x",
|
|
26
|
+
"gap-y",
|
|
27
|
+
"space-x",
|
|
28
|
+
"space-y",
|
|
29
|
+
"w",
|
|
30
|
+
"h",
|
|
31
|
+
"size",
|
|
32
|
+
"min-w",
|
|
33
|
+
"max-w",
|
|
34
|
+
"min-h",
|
|
35
|
+
"max-h",
|
|
36
|
+
"top",
|
|
37
|
+
"right",
|
|
38
|
+
"bottom",
|
|
39
|
+
"left",
|
|
40
|
+
"scroll-m",
|
|
41
|
+
"scroll-p",
|
|
42
|
+
];
|
|
43
|
+
const standardScales = [
|
|
44
|
+
{ scale: "0", multiplier: 0 },
|
|
45
|
+
{ scale: "0.5", multiplier: 0.5 },
|
|
46
|
+
{ scale: "1", multiplier: 1 },
|
|
47
|
+
{ scale: "1.5", multiplier: 1.5 },
|
|
48
|
+
{ scale: "2", multiplier: 2 },
|
|
49
|
+
{ scale: "2.5", multiplier: 2.5 },
|
|
50
|
+
{ scale: "3", multiplier: 3 },
|
|
51
|
+
{ scale: "3.5", multiplier: 3.5 },
|
|
52
|
+
{ scale: "4", multiplier: 4 },
|
|
53
|
+
{ scale: "5", multiplier: 5 },
|
|
54
|
+
{ scale: "6", multiplier: 6 },
|
|
55
|
+
{ scale: "7", multiplier: 7 },
|
|
56
|
+
{ scale: "8", multiplier: 8 },
|
|
57
|
+
{ scale: "9", multiplier: 9 },
|
|
58
|
+
{ scale: "10", multiplier: 10 },
|
|
59
|
+
{ scale: "11", multiplier: 11 },
|
|
60
|
+
{ scale: "12", multiplier: 12 },
|
|
61
|
+
{ scale: "14", multiplier: 14 },
|
|
62
|
+
{ scale: "16", multiplier: 16 },
|
|
63
|
+
{ scale: "20", multiplier: 20 },
|
|
64
|
+
{ scale: "24", multiplier: 24 },
|
|
65
|
+
{ scale: "28", multiplier: 28 },
|
|
66
|
+
{ scale: "32", multiplier: 32 },
|
|
67
|
+
{ scale: "36", multiplier: 36 },
|
|
68
|
+
{ scale: "40", multiplier: 40 },
|
|
69
|
+
{ scale: "44", multiplier: 44 },
|
|
70
|
+
{ scale: "48", multiplier: 48 },
|
|
71
|
+
{ scale: "52", multiplier: 52 },
|
|
72
|
+
{ scale: "56", multiplier: 56 },
|
|
73
|
+
{ scale: "60", multiplier: 60 },
|
|
74
|
+
{ scale: "64", multiplier: 64 },
|
|
75
|
+
{ scale: "72", multiplier: 72 },
|
|
76
|
+
{ scale: "80", multiplier: 80 },
|
|
77
|
+
{ scale: "96", multiplier: 96 },
|
|
78
|
+
];
|
|
79
|
+
const fractionalPrefixes = ["py", "my"];
|
|
80
|
+
const result = {};
|
|
81
|
+
spacingPrefixes.forEach((prefix) => {
|
|
82
|
+
const mapping = {};
|
|
83
|
+
const supportsFractional = fractionalPrefixes.includes(prefix);
|
|
84
|
+
const supportsNegative = [
|
|
85
|
+
"m",
|
|
86
|
+
"mx",
|
|
87
|
+
"my",
|
|
88
|
+
"mt",
|
|
89
|
+
"mr",
|
|
90
|
+
"mb",
|
|
91
|
+
"ml",
|
|
92
|
+
"ms",
|
|
93
|
+
"me",
|
|
94
|
+
"top",
|
|
95
|
+
"right",
|
|
96
|
+
"bottom",
|
|
97
|
+
"left",
|
|
98
|
+
].includes(prefix);
|
|
99
|
+
standardScales.forEach(({ scale, multiplier }) => {
|
|
100
|
+
if (!supportsFractional && scale.includes(".")) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const pixelValue = multiplier * customUnit;
|
|
104
|
+
const pixelKey = `${pixelValue}px`;
|
|
105
|
+
mapping[pixelKey] = `${prefix}-${scale}`;
|
|
106
|
+
if (supportsNegative && scale !== "0") {
|
|
107
|
+
const negativePixelValue = -pixelValue;
|
|
108
|
+
const negativePixelKey = `${negativePixelValue}px`;
|
|
109
|
+
mapping[negativePixelKey] = `-${prefix}-${scale}`;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
mapping["1px"] = `${prefix}-px`;
|
|
113
|
+
result[prefix] = mapping;
|
|
114
|
+
});
|
|
115
|
+
return result;
|
|
116
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youthfulhps/prettier-plugin-tailwindcss-normalizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "A Prettier plugin that normalizes Tailwind CSS arbitrary values into standard utility classes.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|