simple-merge-class-names 6.0.9 → 7.0.1
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 +206 -231
- package/mergeClassNames.d.ts +5 -2
- package/mergeClassNames.js +113 -127
- package/package.json +44 -44
package/README.md
CHANGED
|
@@ -1,40 +1,42 @@
|
|
|
1
1
|
# simple-merge-class-names
|
|
2
2
|
|
|
3
|
-
A straightforward utility for merging CSS class names in _React
|
|
3
|
+
A straightforward utility for merging CSS class names in _React (JSX)_ and other _JavaScript_ projects.
|
|
4
|
+
|
|
5
|
+
For Production look into [https://www.npmjs.com/package/clsx](https://www.npmjs.com/package/clsx)
|
|
4
6
|
|
|
5
7
|
## Table of Contents
|
|
6
8
|
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
9
|
-
- [
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- [License](#license)
|
|
26
|
-
|
|
27
|
-
##
|
|
9
|
+
- [simple-merge-class-names](#simple-merge-class-names)
|
|
10
|
+
- [Table of Contents](#table-of-contents)
|
|
11
|
+
- [The Genocidal Occupation Is Starving Gaza](#the-genocidal-occupation-is-starving-gaza)
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [_Recommended If Using VSCode: Install `Prettier` Extension_](#recommended-if-using-vscode-install-prettier-extension)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [TypeScript Definitions](#typescript-definitions)
|
|
16
|
+
- [Valid Arguments](#valid-arguments)
|
|
17
|
+
- [Invalid Arguments](#invalid-arguments)
|
|
18
|
+
- [Reason for warnings](#reason-for-warnings)
|
|
19
|
+
- [Conditional class inclusion](#conditional-class-inclusion)
|
|
20
|
+
- [Return Result](#return-result)
|
|
21
|
+
- [Chaining](#chaining)
|
|
22
|
+
- [Usage of Browser Debugger](#usage-of-browser-debugger)
|
|
23
|
+
- [VSCode Workflow To Minimize Typing Strain](#vscode-workflow-to-minimize-typing-strain)
|
|
24
|
+
- [Testing Source Code](#testing-source-code)
|
|
25
|
+
- [Run Once](#run-once)
|
|
26
|
+
- [Run Watch Mode](#run-watch-mode)
|
|
27
|
+
- [License](#license)
|
|
28
|
+
|
|
29
|
+
## The Genocidal Occupation Is Starving Gaza
|
|
28
30
|
|
|
29
31
|
- [Donate](https://gazafunds.com/)
|
|
30
32
|
|
|
31
|
-
- [
|
|
33
|
+
- [(US) Demand Immediate Opening of ALL Gaza Border Crossings](https://act.uscpr.org/a/letaidin)
|
|
32
34
|
|
|
33
|
-
- [
|
|
35
|
+
- [Boycott Brands Supporting Gaza Holocaust](https://www.uplift.ie/bds/)
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
- [Legal Action](https://www.hindrajabfoundation.org/perpetrators)
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
_Palestine is about fundamental non-negotiable human rights. End all financial and diplomatic ties with i\*rael_.
|
|
38
40
|
|
|
39
41
|
## Installation
|
|
40
42
|
|
|
@@ -50,20 +52,25 @@ yarn add simple-merge-class-names
|
|
|
50
52
|
npm install simple-merge-class-names
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
### Install `Prettier`
|
|
55
|
+
### _Recommended If Using VSCode: Install `Prettier` Extension_
|
|
56
|
+
|
|
57
|
+
- _[https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)_
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
_It will nicely format your code especially when you have lots of classes, and will significantly improve your visual experience._
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
_If you have a different IDE use an equivalent auto code formatter tool/extension_
|
|
58
62
|
|
|
59
63
|
## Usage
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
| Function | Prints console warnings | Activates debugger |
|
|
66
|
+
| ------------------------- | ----------------------- | ------------------ |
|
|
67
|
+
| `mergeClassNames` | ✅ | ❌ |
|
|
68
|
+
| `mergeClassNamesDebugger` | ✅ | ✅ |
|
|
62
69
|
|
|
63
70
|
```jsx
|
|
64
71
|
import { mergeClassNames } from "simple-merge-class-names";
|
|
65
72
|
|
|
66
|
-
const
|
|
73
|
+
const Component = ({ condition }) => {
|
|
67
74
|
return (
|
|
68
75
|
<div
|
|
69
76
|
className={mergeClassNames(
|
|
@@ -80,130 +87,110 @@ const MyComponent = () => {
|
|
|
80
87
|
};
|
|
81
88
|
```
|
|
82
89
|
|
|
83
|
-
|
|
90
|
+
### TypeScript Definitions
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
```ts
|
|
93
|
+
export declare const mergeClassNames: (
|
|
94
|
+
...args: (string | false)[]
|
|
95
|
+
) => string | false;
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const MyComponent = () => {
|
|
92
|
-
return (
|
|
93
|
-
<div
|
|
94
|
-
className={mergeClassNamesDebugger(
|
|
95
|
-
"app",
|
|
96
|
-
condition ? "min-h-dvh" : false,
|
|
97
|
-
"grid",
|
|
98
|
-
"grid-rows-[auto_1fr_auto]",
|
|
99
|
-
"outline"
|
|
100
|
-
)}
|
|
101
|
-
>
|
|
102
|
-
Hello, world!
|
|
103
|
-
</div>
|
|
104
|
-
);
|
|
105
|
-
};
|
|
97
|
+
export declare const mergeClassNamesDebugger: (
|
|
98
|
+
...args: (string | false)[]
|
|
99
|
+
) => string | false;
|
|
106
100
|
```
|
|
107
101
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- Have `Prettier` installed
|
|
111
|
-
- Have `Editor: Word Wrap` enabled in VS Code:
|
|
102
|
+
### Valid Arguments
|
|
112
103
|
|
|
113
|
-
|
|
114
|
-
- Or `Open User Settings (JSON)` and add this entry:
|
|
104
|
+
Only 2:
|
|
115
105
|
|
|
116
|
-
|
|
106
|
+
1. **Valid strings, not whitespace, of length >= 1**
|
|
117
107
|
|
|
118
|
-
|
|
119
|
-
- **Save the file** (<kbd>Ctrl+S</kbd>), which activates `Prettier` to auto-format the file, it will:
|
|
108
|
+
_(As long as you have content in the string you're OK)_
|
|
120
109
|
|
|
121
|
-
|
|
122
|
-
- Neatly arrange each class name on a new line.
|
|
110
|
+
2. **`false`**
|
|
123
111
|
|
|
124
|
-
|
|
112
|
+
_Example, This is OK:_
|
|
125
113
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
114
|
+
```js
|
|
115
|
+
mergeClassNames(
|
|
116
|
+
"mx-auto",
|
|
117
|
+
"min-dvh ",
|
|
118
|
+
" flex",
|
|
119
|
+
" grid ",
|
|
120
|
+
"italic font-bold ",
|
|
121
|
+
`
|
|
122
|
+
gap-y-4
|
|
123
|
+
`,
|
|
124
|
+
false,
|
|
125
|
+
condition ? "daisy-btn-active" : false
|
|
126
|
+
);
|
|
138
127
|
```
|
|
139
128
|
|
|
140
|
-
|
|
141
|
-
|
|
129
|
+
### Invalid Arguments
|
|
130
|
+
|
|
131
|
+
Each below argument will be **ignored**, and cause a Dev Console **warning to be printed** to alert you:
|
|
132
|
+
|
|
133
|
+
- **Empty strings**: _(e.g. `""`)_
|
|
134
|
+
- **Whitespace** any consecutive combination of the following:
|
|
135
|
+
- new lines,
|
|
136
|
+
- spaces,
|
|
137
|
+
- tabs
|
|
138
|
+
- _(e.g._ `" "`, `"\n "`, `" \t \n "`, _etc.)_
|
|
139
|
+
- **`true`**
|
|
140
|
+
- **`undefined`**
|
|
141
|
+
- **`null`**
|
|
142
|
+
- **Objects**
|
|
143
|
+
- **Numbers**
|
|
144
|
+
- **Big Int**
|
|
145
|
+
- **Symbols**
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
// Example: These arguments will be **ignored**, and a console.warn will be printed
|
|
149
|
+
|
|
150
|
+
const someVariable = "";
|
|
151
|
+
|
|
152
|
+
mergeClassNames(
|
|
153
|
+
someVariable, // empty string
|
|
154
|
+
" ", // whitespace
|
|
155
|
+
"\n ", // whitespace
|
|
156
|
+
" \t \n ", // whitespace
|
|
157
|
+
` // whitespace
|
|
158
|
+
\n
|
|
159
|
+
`,
|
|
160
|
+
true, // true
|
|
161
|
+
undefined, // undefined
|
|
162
|
+
null, // null
|
|
163
|
+
{
|
|
164
|
+
// object
|
|
165
|
+
name: "value",
|
|
166
|
+
email: "email@example.com",
|
|
167
|
+
},
|
|
168
|
+
123, // number
|
|
169
|
+
123.45 // number
|
|
170
|
+
);
|
|
142
171
|
```
|
|
143
172
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
- If no inputs are provided (e.g. `mergeClassNames()`) or if invalid inputs are given (e.g. `mergeClassNames(undefined, " ")`), an _empty string_ is returned: `""`.
|
|
147
|
-
|
|
148
|
-
## Acceptable Arguments
|
|
149
|
-
|
|
150
|
-
`mergeClassNames(...args)` and `mergeClassNamesDebugger(...args)` only accept the following arguments:
|
|
151
|
-
|
|
152
|
-
- **Strings that are not empty, and are not whitespace** (e.g. `"app"`, `"min-h-dvh"`, `" grid "`)
|
|
153
|
-
|
|
154
|
-
- The boolean value **`false`**
|
|
155
|
-
|
|
156
|
-
Any other input is considered an invalid argument and will be ignored, resulting in a matching warning being logged. Invalid argument types include:
|
|
157
|
-
|
|
158
|
-
- _Empty strings_ (`""`),
|
|
159
|
-
- _Whitespace combinations_ (e.g. `" "`, `"\n"`, `" \n\t "`, etc...),,
|
|
160
|
-
- `null`,
|
|
161
|
-
- `undefined`,
|
|
162
|
-
- _Numbers_,
|
|
163
|
-
- _Objects_,
|
|
164
|
-
- _Arrays_
|
|
165
|
-
|
|
166
|
-
## Console Warning
|
|
167
|
-
|
|
168
|
-
Whenever invalid arguments are passed to `mergeClassNames`, they are not silently ignored, as this can lead to subtle bugs and increase technical debt. Instead, a `console.warn` is shown in the _Developer Console_ to notify the developer of a potentially deeper issue that requires attention. For example:
|
|
169
|
-
|
|
170
|
-
```plaintext
|
|
171
|
-
[mergeClassNames] Warning: invalid arguments were provided and ignored:
|
|
172
|
-
|
|
173
|
-
* Replace "mergeClassNames" with "mergeClassNamesDebugger" without changing any arguments, and open the Developer Console, or attach Debugger (see README.md).
|
|
174
|
-
|
|
175
|
-
* Expected all arguments to be either strings or value `false`, but got 5 invalid value(s):
|
|
176
|
-
[
|
|
177
|
-
(1/5): >undefined< of type "undefined",
|
|
178
|
-
(2/5): > test < of type "object",
|
|
179
|
-
(3/5): >[object Object]< of type "object",
|
|
180
|
-
(4/5): >true< of type "boolean",
|
|
181
|
-
(5/5): >null< of type "object"
|
|
182
|
-
]
|
|
173
|
+

|
|
183
174
|
|
|
184
|
-
|
|
185
|
-
[
|
|
186
|
-
(1/2): ><,
|
|
187
|
-
(2/2): > <
|
|
188
|
-
]
|
|
189
|
-
```
|
|
175
|
+
### Reason for warnings
|
|
190
176
|
|
|
191
|
-
|
|
177
|
+
- To avoid silent failures, because you will be pulling your hair asking why a Tailwind class isn't working only to figure out you passed an _object_, _array_ or an _empty string_ instead of a valid string. _(It could also be because of an unsupported class name or typo but this is beyond the scope of this package)_
|
|
192
178
|
|
|
193
|
-
|
|
179
|
+
### Conditional class inclusion
|
|
194
180
|
|
|
195
|
-
|
|
181
|
+
Use this pattern:
|
|
196
182
|
|
|
197
|
-
|
|
183
|
+
- `condition ? "class-name" : false` with `false` serving as the valid fallback.
|
|
184
|
+
- _or_ `condition === true ? "class-name" : false` if you want to be specific.
|
|
198
185
|
|
|
199
|
-
|
|
186
|
+
_Note:_ Avoid using the _short-circuit implicit syntax_ like this:
|
|
200
187
|
|
|
201
|
-
- `condition && "class-name"`,
|
|
188
|
+
- `condition && "class-name"`, beside less readable code, it can produce _falsy_ values which will be **_ignored_** _(e.g. `0`, `""`, `undefined`, and `null`)_.
|
|
202
189
|
|
|
203
190
|
```jsx
|
|
204
191
|
import { mergeClassNames } from "simple-merge-class-names";
|
|
205
192
|
|
|
206
|
-
const
|
|
193
|
+
const Component = () => {
|
|
207
194
|
return (
|
|
208
195
|
<div
|
|
209
196
|
className={mergeClassNames(
|
|
@@ -220,141 +207,129 @@ const MyComponent = () => {
|
|
|
220
207
|
};
|
|
221
208
|
```
|
|
222
209
|
|
|
223
|
-
|
|
210
|
+
### Return Result
|
|
224
211
|
|
|
225
|
-
|
|
226
|
-
import { mergeClassNamesDebugger } from "simple-merge-class-names";
|
|
212
|
+
Either:
|
|
227
213
|
|
|
228
|
-
|
|
229
|
-
return (
|
|
230
|
-
<div
|
|
231
|
-
className={mergeClassNamesDebugger(
|
|
232
|
-
"app",
|
|
233
|
-
condition ? "min-h-dvh" : false,
|
|
234
|
-
"grid",
|
|
235
|
-
"grid-rows-[auto_1fr_auto]",
|
|
236
|
-
"outline"
|
|
237
|
-
)}
|
|
238
|
-
>
|
|
239
|
-
Hello, world!
|
|
240
|
-
</div>
|
|
241
|
-
);
|
|
242
|
-
};
|
|
243
|
-
```
|
|
214
|
+
1. **Valid `string`, never whitespace, always length >= 1**
|
|
244
215
|
|
|
245
|
-
|
|
216
|
+
2. _or_ **`false`** _(if all input arguments were invalid)_
|
|
246
217
|
|
|
247
|
-
|
|
218
|
+
### Chaining
|
|
248
219
|
|
|
249
|
-
|
|
250
|
-
2. **_Replace_** `mergeClassNames` with **`mergeClassNamesDebugger`** _without_ altering any of the provided arguments.
|
|
220
|
+
Because of safe return types you can chain calls safely without worrying about warnings or arguments being ignored.
|
|
251
221
|
|
|
252
|
-
|
|
222
|
+
_So this is OK:_
|
|
253
223
|
|
|
254
|
-
|
|
224
|
+
```js
|
|
225
|
+
mergeClassNames(condition ? "disabled" : mergeClassNames(...) )
|
|
226
|
+
```
|
|
255
227
|
|
|
256
|
-
|
|
228
|
+
## Usage of Browser Debugger
|
|
257
229
|
|
|
258
|
-
|
|
230
|
+
**Once you see warnings in the console, the next step is to use `mergeClassNamesDebugger`**
|
|
259
231
|
|
|
260
|
-
|
|
232
|
+
1. Enable Debugger
|
|
233
|
+
- _For chromium-based browsers it's On by default and you don't need to do anything AFAIK._
|
|
234
|
+
- _For Firefox:_ Open **_Developer Tools_:**
|
|
235
|
+
- _Make Sure_ **_Debugger_** _(tab)_ -> **`Pause on debugger statement`** is ticked.
|
|
236
|
+
- Keep Dev Tools open.
|
|
237
|
+

|
|
261
238
|
|
|
262
|
-
|
|
263
|
-
import { mergeClassNames } from "simple-merge-class-names";
|
|
239
|
+
- Use **`import {mergeClassNamesDebugger as mergeClassNames}`** to debug the entire file, and keep the rest intact.
|
|
264
240
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
<div
|
|
268
|
-
className={mergeClassNames(
|
|
269
|
-
"app",
|
|
270
|
-
condition ? "min-h-dvh" : false,
|
|
271
|
-
"grid",
|
|
272
|
-
"grid-rows-[auto_1fr_auto]",
|
|
273
|
-
"outline"
|
|
274
|
-
)}
|
|
275
|
-
>
|
|
276
|
-
Hello, world!
|
|
277
|
-
</div>
|
|
278
|
-
);
|
|
279
|
-
};
|
|
280
|
-
```
|
|
241
|
+
```jsx
|
|
242
|
+
import { mergeClassNamesDebugger as mergeClassNames } from "simple-merge-class-names";
|
|
281
243
|
|
|
282
|
-
|
|
244
|
+
const Component = ({ condition }) => {
|
|
245
|
+
return (
|
|
246
|
+
<div
|
|
247
|
+
className={mergeClassNames(
|
|
248
|
+
"app",
|
|
249
|
+
condition ? "min-h-dvh" : false,
|
|
250
|
+
"grid",
|
|
251
|
+
"grid-rows-[auto_1fr_auto]",
|
|
252
|
+
"outline"
|
|
253
|
+
)}
|
|
254
|
+
>
|
|
255
|
+
Hello, world!
|
|
256
|
+
</div>
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
```
|
|
283
260
|
|
|
284
|
-
|
|
261
|
+
- or call `mergeClassNamesDebugger` directly.
|
|
285
262
|
|
|
286
|
-
|
|
263
|
+
```jsx
|
|
264
|
+
import { mergeClassNamesDebugger } from "simple-merge-class-names";
|
|
287
265
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
266
|
+
const Component = ({ condition }) => {
|
|
267
|
+
return (
|
|
268
|
+
<div
|
|
269
|
+
className={mergeClassNamesDebugger(
|
|
270
|
+
"app",
|
|
271
|
+
condition ? "min-h-dvh" : false,
|
|
272
|
+
"grid",
|
|
273
|
+
"grid-rows-[auto_1fr_auto]",
|
|
274
|
+
"outline"
|
|
275
|
+
)}
|
|
276
|
+
>
|
|
277
|
+
Hello, world!
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
```
|
|
294
282
|
|
|
295
|
-
|
|
283
|
+
- Refresh the page, the debugger should connect:
|
|
296
284
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
```
|
|
285
|
+
- Navigate to the **_Call stack_**
|
|
286
|
+
- Click the function/component right before _`mergeClassNamesDebugger`_
|
|
300
287
|
|
|
301
|
-
|
|
288
|
+

|
|
302
289
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
* mergeClassNames - A straightforward utility for merging CSS class names in React + Tailwind, and other JavaScript projects.
|
|
306
|
-
*
|
|
307
|
-
* @license AGPL-3.0
|
|
308
|
-
* Copyright (C) 2025 Abdullah Fatota
|
|
309
|
-
*
|
|
310
|
-
* ...
|
|
311
|
-
*/
|
|
290
|
+
- Hover over the arguments, one or several should be invalid:
|
|
291
|
+

|
|
312
292
|
|
|
313
|
-
|
|
314
|
-
const space = "\x20"; // ASCII code for a single space character (" "), decimal 32
|
|
293
|
+
## VSCode Workflow To Minimize Typing Strain
|
|
315
294
|
|
|
316
|
-
|
|
317
|
-
const [strings, nonStrings] = partition(nonFalseValues, isTypeString);
|
|
318
|
-
const [emptyStrings, nonEmptyStrings] = partition(strings, isEmptyString);
|
|
319
|
-
const trimmed = nonEmptyStrings.map((val) => val.trim());
|
|
320
|
-
const className = trimmed.join(space);
|
|
295
|
+
Use single quotes around class names, and activate `Prettier` which will neatly format and arrange the classes.
|
|
321
296
|
|
|
322
|
-
|
|
323
|
-
|
|
297
|
+
- Install `Prettier`
|
|
298
|
+
- Enable `Editor: Word Wrap`:
|
|
324
299
|
|
|
325
|
-
|
|
326
|
-
|
|
300
|
+
- Open `Settings (UI)` → `Editor: Word Wrap` → `on`
|
|
301
|
+
- _or_ `User Settings (JSON)` and add this entry `"editor.wordWrap": "on"`
|
|
327
302
|
|
|
328
|
-
|
|
329
|
-
|
|
303
|
+
- Use single quotes (<kbd>'</kbd>) around class names
|
|
304
|
+
- Save the file
|
|
330
305
|
|
|
331
|
-
|
|
332
|
-
mergeClassNamesCore({ args, activateDebugger: true });
|
|
333
|
-
```
|
|
306
|
+
Example:
|
|
334
307
|
|
|
335
|
-
|
|
308
|
+
- Before
|
|
309
|
+

|
|
336
310
|
|
|
337
|
-
|
|
311
|
+
- After
|
|
338
312
|
|
|
339
|
-
|
|
313
|
+

|
|
340
314
|
|
|
341
|
-
|
|
315
|
+
## Testing Source Code
|
|
342
316
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
<div className="app min-h-dvh grid grid-rows-[auto_1fr_auto] outline">
|
|
347
|
-
Hello, world!
|
|
348
|
-
</div>
|
|
349
|
-
);
|
|
350
|
-
};
|
|
351
|
-
```
|
|
317
|
+
This project uses `Vitest` as the test runner for fast and modern testing.
|
|
318
|
+
|
|
319
|
+
### Run Once
|
|
352
320
|
|
|
353
|
-
|
|
321
|
+
```bash
|
|
322
|
+
git clone https://github.com/new-AF/simple-merge-class-names
|
|
323
|
+
cd simple-merge-class-names
|
|
324
|
+
pnpm install
|
|
325
|
+
pnpm test
|
|
326
|
+
```
|
|
354
327
|
|
|
355
|
-
|
|
328
|
+
### Run Watch Mode
|
|
356
329
|
|
|
357
|
-
|
|
330
|
+
```bash
|
|
331
|
+
pnpm test:watch
|
|
332
|
+
```
|
|
358
333
|
|
|
359
334
|
## License
|
|
360
335
|
|
package/mergeClassNames.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export declare const mergeClassNames: (
|
|
1
|
+
export declare const mergeClassNames: (
|
|
2
|
+
...args: (string | false)[]
|
|
3
|
+
) => string | false;
|
|
4
|
+
|
|
2
5
|
export declare const mergeClassNamesDebugger: (
|
|
3
6
|
...args: (string | false)[]
|
|
4
|
-
) => string;
|
|
7
|
+
) => string | false;
|
package/mergeClassNames.js
CHANGED
|
@@ -37,153 +37,139 @@
|
|
|
37
37
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
-
const isValueFalse = (val) => val === false;
|
|
41
|
-
|
|
42
|
-
const isTypeString = (val) => typeof val === "string";
|
|
43
|
-
|
|
44
|
-
const isEmptyString = (val) => {
|
|
45
|
-
const trimmed = val.trim();
|
|
46
|
-
return trimmed === "";
|
|
47
|
-
};
|
|
48
|
-
|
|
49
40
|
const partition = (array, keepPredicate) => {
|
|
50
41
|
const keep = [];
|
|
51
42
|
const ignore = [];
|
|
52
43
|
for (const element of array) {
|
|
53
|
-
|
|
44
|
+
const arrayRef = keepPredicate(element) ? keep : ignore;
|
|
45
|
+
arrayRef.push(element);
|
|
54
46
|
}
|
|
55
47
|
return [keep, ignore];
|
|
56
48
|
};
|
|
57
49
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (doPrint === false) return;
|
|
68
|
-
|
|
69
|
-
const messages = [];
|
|
70
|
-
const newline = "\n";
|
|
71
|
-
const doubleNewline = newline.repeat(2);
|
|
72
|
-
|
|
73
|
-
const tabString = (string, tabCount = 0) => {
|
|
74
|
-
const tab = "\t".repeat(tabCount);
|
|
75
|
-
return `${tab}${string}`;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const tabArray = (array, tabCount) =>
|
|
79
|
-
array.map((element) => tabString(element, tabCount));
|
|
80
|
-
|
|
81
|
-
/* convert array to nice string because console.warn(array) squashes it on 1 line */
|
|
82
|
-
const makeMessage = ({
|
|
83
|
-
array,
|
|
84
|
-
expected,
|
|
85
|
-
tabCount = 1,
|
|
86
|
-
includeElementType = false,
|
|
87
|
-
arrayMaxLength = 5,
|
|
88
|
-
}) => {
|
|
89
|
-
const count = array.length;
|
|
90
|
-
const addEllipsis = count > arrayMaxLength;
|
|
91
|
-
const slice = addEllipsis ? array.slice(0, arrayMaxLength) : array;
|
|
92
|
-
|
|
93
|
-
const formatElementWithType = (element, index) =>
|
|
94
|
-
`(${index + 1}/${count}): >${element}< of type "${typeof element}"`;
|
|
95
|
-
|
|
96
|
-
const formatElement = (element, index) =>
|
|
97
|
-
`(${index + 1}/${count}): >${element}<`;
|
|
98
|
-
|
|
99
|
-
const initialMapped = slice.map(
|
|
100
|
-
includeElementType ? formatElementWithType : formatElement
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const mapped = addEllipsis ? [...initialMapped, "..."] : initialMapped;
|
|
104
|
-
|
|
105
|
-
const string = [
|
|
106
|
-
tabString(
|
|
107
|
-
`${expected}, but got ${count} invalid value(s):`,
|
|
108
|
-
tabCount
|
|
109
|
-
),
|
|
110
|
-
tabString("[", tabCount),
|
|
111
|
-
tabArray(mapped, tabCount).join("," + newline),
|
|
112
|
-
tabString("]", tabCount),
|
|
113
|
-
].join(newline);
|
|
114
|
-
|
|
115
|
-
return string;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
/* "Replace "mergeClassNames" with "mergeClassNamesDebugger" ... " */
|
|
119
|
-
if (doPrint && activateDebugger === false) {
|
|
120
|
-
messages.push(
|
|
121
|
-
tabString(
|
|
122
|
-
'* Replace "mergeClassNames" with "mergeClassNamesDebugger" without changing any arguments, and open the Developer Console, or attach Debugger (see README.md).',
|
|
123
|
-
1
|
|
124
|
-
)
|
|
125
|
-
);
|
|
50
|
+
const filterArguments = (argumentsArray) => {
|
|
51
|
+
/*
|
|
52
|
+
returns {
|
|
53
|
+
valid: bool,
|
|
54
|
+
value: any
|
|
55
|
+
(optional) isString: bool,
|
|
56
|
+
(optional) valueType: any
|
|
57
|
+
(optional) isValueFalse
|
|
58
|
+
(optional) isValidString: bool
|
|
126
59
|
}
|
|
60
|
+
*/
|
|
61
|
+
const isValidArgument = (value) => {
|
|
62
|
+
const valueType = typeof value;
|
|
127
63
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (activateDebugger) {
|
|
131
|
-
debugger;
|
|
64
|
+
if (value === false) {
|
|
65
|
+
return { valid: true, value };
|
|
132
66
|
}
|
|
133
|
-
messages.push(
|
|
134
|
-
makeMessage({
|
|
135
|
-
array: nonStrings,
|
|
136
|
-
expected:
|
|
137
|
-
"* Expected all arguments to be either strings or value `false`",
|
|
138
|
-
includeElementType: true,
|
|
139
|
-
})
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
67
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
68
|
+
if (valueType === "string") {
|
|
69
|
+
const trimmed = value.trim();
|
|
70
|
+
|
|
71
|
+
// invalid string
|
|
72
|
+
if (trimmed === "") {
|
|
73
|
+
return {
|
|
74
|
+
valid: false,
|
|
75
|
+
value,
|
|
76
|
+
isString: true,
|
|
77
|
+
isValidString: false,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// valid string
|
|
81
|
+
return {
|
|
82
|
+
valid: true,
|
|
83
|
+
value: trimmed,
|
|
84
|
+
isString: true,
|
|
85
|
+
isValidString: true,
|
|
86
|
+
};
|
|
147
87
|
}
|
|
148
|
-
messages.push(
|
|
149
|
-
makeMessage({
|
|
150
|
-
array: emptyStrings,
|
|
151
|
-
expected: "* Expected 0 empty strings",
|
|
152
|
-
})
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
88
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
? "mergeClassNames"
|
|
160
|
-
: "mergeClassNamesDebugger";
|
|
89
|
+
// everything else
|
|
90
|
+
return { valid: false, value, isString: false, valueType };
|
|
91
|
+
};
|
|
161
92
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
93
|
+
// array of objects
|
|
94
|
+
const newArguments = argumentsArray.map((element) =>
|
|
95
|
+
isValidArgument(element)
|
|
96
|
+
);
|
|
166
97
|
|
|
167
|
-
|
|
168
|
-
|
|
98
|
+
// includes `false`
|
|
99
|
+
const [validArgumentsWithFalse, invalidArguments] = partition(
|
|
100
|
+
newArguments,
|
|
101
|
+
({ valid }) => valid === true
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// ignore `false`
|
|
105
|
+
const [validArguments, _] = partition(
|
|
106
|
+
validArgumentsWithFalse,
|
|
107
|
+
({ value }) => value !== false
|
|
108
|
+
);
|
|
169
109
|
|
|
170
|
-
|
|
171
|
-
const space = "\x20"; // ASCII code for a single space character (" "), decimal 32
|
|
110
|
+
// console.log({ validArgumentsWithFalse, validArguments, invalidArguments });
|
|
172
111
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const [emptyStrings, nonEmptyStrings] = partition(strings, isEmptyString);
|
|
176
|
-
const trimmed = nonEmptyStrings.map((val) => val.trim());
|
|
177
|
-
const className = trimmed.join(space);
|
|
112
|
+
return { validArguments, invalidArguments };
|
|
113
|
+
};
|
|
178
114
|
|
|
179
|
-
|
|
180
|
-
|
|
115
|
+
const warnMessage = (
|
|
116
|
+
{ value, isString, valueType, isValidString },
|
|
117
|
+
callerFunctionName
|
|
118
|
+
) => {
|
|
119
|
+
if (isString === false) {
|
|
120
|
+
return `[${callerFunctionName}] Ignored non-string >${value}< (${valueType})`;
|
|
121
|
+
} else if (isValidString === false) {
|
|
122
|
+
return `[${callerFunctionName}] Ignored empty string "${value}"`;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
181
125
|
|
|
182
|
-
|
|
126
|
+
const warnOnly = (
|
|
127
|
+
invalidArgumentsArray,
|
|
128
|
+
callerFunctionName,
|
|
129
|
+
debuggerVariantName
|
|
130
|
+
) =>
|
|
131
|
+
invalidArgumentsArray.forEach((obj) => {
|
|
132
|
+
const message = warnMessage(obj, callerFunctionName);
|
|
133
|
+
console.warn(`${message}`);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const warnDebug = (invalidArgumentsArray, callerFunctionName) =>
|
|
137
|
+
invalidArgumentsArray.forEach((obj) => {
|
|
138
|
+
console.warn(warnMessage(obj, callerFunctionName));
|
|
139
|
+
debugger;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const join = (argumentsArray) => {
|
|
143
|
+
const space = "\x20"; // ASCII for single space (" "), decimal 32
|
|
144
|
+
const classNames = argumentsArray.map(({ value }) => value).join(space);
|
|
145
|
+
return classNames;
|
|
183
146
|
};
|
|
184
147
|
|
|
185
|
-
|
|
186
|
-
|
|
148
|
+
const finalResult = (joinedClassNames) =>
|
|
149
|
+
joinedClassNames === "" ? false : joinedClassNames;
|
|
150
|
+
|
|
151
|
+
// exported
|
|
152
|
+
// export const mergeClassNamesNoWarning = (...argumentsArray) => {
|
|
153
|
+
// const { validArguments, _ } = filterArguments(argumentsArray);
|
|
154
|
+
// const classNames = join(validArguments);
|
|
155
|
+
// const result = finalResult(classNames);
|
|
156
|
+
// return result;
|
|
157
|
+
// };
|
|
158
|
+
|
|
159
|
+
export const mergeClassNames = (...argumentsArray) => {
|
|
160
|
+
const { validArguments, invalidArguments } =
|
|
161
|
+
filterArguments(argumentsArray);
|
|
162
|
+
warnOnly(invalidArguments, "mergeClassNames", "mergeClassNamesDebugger");
|
|
163
|
+
const classNames = join(validArguments);
|
|
164
|
+
const result = finalResult(classNames);
|
|
165
|
+
return result;
|
|
166
|
+
};
|
|
187
167
|
|
|
188
|
-
export const mergeClassNamesDebugger = (...
|
|
189
|
-
|
|
168
|
+
export const mergeClassNamesDebugger = (...argumentsArray) => {
|
|
169
|
+
const { validArguments, invalidArguments } =
|
|
170
|
+
filterArguments(argumentsArray);
|
|
171
|
+
warnDebug(invalidArguments, "mergeClassNamesDebugger");
|
|
172
|
+
const classNames = join(validArguments);
|
|
173
|
+
const result = finalResult(classNames);
|
|
174
|
+
return result;
|
|
175
|
+
};
|
package/package.json
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "simple-merge-class-names",
|
|
3
|
+
"version": "7.0.1",
|
|
4
|
+
"description": "A straightforward utility for merging CSS class names in React + Tailwind and JavaScript projects.",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./mergeClassNames.js",
|
|
8
|
+
"types": "./mergeClassNames.d.ts"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"mergeClassNames.js",
|
|
13
|
+
"mergeClassNames.d.ts",
|
|
14
|
+
"LICENSE.txt",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mergeClassNames",
|
|
20
|
+
"merge",
|
|
21
|
+
"class-names-merger",
|
|
22
|
+
"utility",
|
|
23
|
+
"react",
|
|
24
|
+
"tailwindcss"
|
|
25
|
+
],
|
|
26
|
+
"author": "Abdullah Fatota",
|
|
27
|
+
"license": "AGPL-3.0",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/new-AF/simple-merge-class-names.git"
|
|
31
|
+
},
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/new-AF/simple-merge-class-names/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/new-AF/simple-merge-class-names",
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"vitest": "^3.2.3"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "vitest",
|
|
41
|
+
"test:watch": "vitest --watch",
|
|
42
|
+
"test:ui": "vitest --ui"
|
|
43
|
+
}
|
|
44
|
+
}
|