simple-merge-class-names 4.0.0 → 5.0.0

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 CHANGED
@@ -1,31 +1,32 @@
1
1
  # simple-merge-class-names
2
2
 
3
- A straightforward utility for merging CSS class names in `React + Tailwind` and other JavaScript projects.
3
+ A straightforward utility for merging CSS class names in `React + Tailwind` and other _JavaScript_ projects.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
+ - [Production Considerations](#production-considerations)
7
8
  - [Installation](#installation)
8
9
  - [Install Prettier With VSCode (Most Recommended)](#install-prettier-with-vscode-most-recommended)
9
10
  - [Usage](#usage)
10
-
11
- - [Workflow To Minimize Typing Strain](#workflow-to-minimize-typing-strain)
12
-
13
- - [Argument Handling](#argument-handling)
14
-
15
- - [Breaking Changes From Version 1.X.X](#breaking-changes-from-version-1xx)
16
-
17
- - [Console Warning for Invalid Arguments](#console-warning-for-invalid-arguments)
18
-
11
+ - [Workflow To Minimize Typing Strain](#workflow-to-minimize-typing-strain)
12
+ - [Type Definitions (of Exported Functions)](#type-definitions-of-exported-functions)
13
+ - [Accepted Arguments](#accepted-arguments)
14
+ - [Console Warning](#console-warning)
15
+ - [Conditionally Include Class Names](#conditionally-include-class-names)
16
+ - [Using `mergeClassNamesDebugger` And The Built-in Browser Debugger To Find And Fix Warnings](#using-mergeclassnamesdebugger-and-the-built-in-browser-debugger-to-find-and-fix-warnings)
17
+ - [Strategies To Ensure Correct Arguments Are Sent to `mergeClassNames`](#strategies-to-ensure-correct-arguments-are-sent-to-mergeclassnames)
19
18
  - [Testing](#testing)
20
19
  - [Source Code (Partial)](#source-code-partial)
21
20
  - [Misc.](#misc)
22
-
21
+ - [Motivation](#motivation)
23
22
  - [Why the Mismatch Between Exported Function and Package Name?](#why-the-mismatch-between-exported-function-and-package-name)
24
23
  - [Where This Package Excels](#where-this-package-excels)
25
-
26
- - [Production Considerations](#production-considerations)
27
24
  - [License](#license)
28
25
 
26
+ ## Production Considerations
27
+
28
+ When developing this package I prioritized _code readability_, _strict input handling_, and _improved developer experience_, as such **_performance_** and **_features_** were not the guiding factor. Therefore if you are considering this package for production, you might also want to look into `clsx`: [https://www.npmjs.com/package/clsx](https://www.npmjs.com/package/clsx)
29
+
29
30
  ## Installation
30
31
 
31
32
  ```bash
@@ -42,23 +43,23 @@ npm install simple-merge-class-names
42
43
 
43
44
  ### Install `Prettier` With VSCode (Most Recommended)
44
45
 
45
- [https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
46
+ - [https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
46
47
 
47
- Or install an equivalent auto code formatter for your IDE.
48
+ - Or use an equivalent auto code formatter for your IDE.
48
49
 
49
50
  ## Usage
50
51
 
51
- Import `mergeClassNames(...args)` from the package, and use it in your JSX or JavaScript code.
52
+ Import `mergeClassNames(...args)` from the package, and use it in your _JSX_ or _JavaScript_ code.
52
53
 
53
54
  ```jsx
54
55
  import { mergeClassNames } from "simple-merge-class-names";
55
56
 
56
- function MyComponent() {
57
+ const MyComponent = () => {
57
58
  return (
58
59
  <div
59
60
  className={mergeClassNames(
60
61
  "app",
61
- "min-h-dvh",
62
+ condition ? "min-h-dvh" : false,
62
63
  "grid",
63
64
  "grid-rows-[auto_1fr_auto]",
64
65
  "outline"
@@ -67,24 +68,35 @@ function MyComponent() {
67
68
  Hello, world!
68
69
  </div>
69
70
  );
70
- }
71
+ };
71
72
  ```
72
73
 
73
- While using separate strings for each class name can be tedious, see [Workflow To Minimize Typing Strain](#workflow-to-minimize-typing-strain), it significantly enhances code readability and Developer Experience (DX). This is in contrast to hard-to-read strings like:
74
+ Or for debugging purposes to fix [console warnings](#console-warning):
75
+
76
+ - Open the _Browser's Developer Tools_ and
77
+ - Use `mergeClassNamesDebugger`
74
78
 
75
79
  ```jsx
76
- function MyComponent() {
80
+ import { mergeClassNamesDebugger } from "simple-merge-class-names";
81
+
82
+ const MyComponent = () => {
77
83
  return (
78
- <div className="app min-h-dvh grid grid-rows-[auto_1fr_auto] outline">
84
+ <div
85
+ className={mergeClassNamesDebugger(
86
+ "app",
87
+ condition ? "min-h-dvh" : false,
88
+ "grid",
89
+ "grid-rows-[auto_1fr_auto]",
90
+ "outline"
91
+ )}
92
+ >
79
93
  Hello, world!
80
94
  </div>
81
95
  );
82
- }
96
+ };
83
97
  ```
84
98
 
85
- ### Workflow To Minimize Typing Strain
86
-
87
- ![Screen recording of optimal DX in action: using this package with Prettier as it neatly arranges each class name on a new line](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/Reduce%20typing%20strain.gif)
99
+ ## Workflow To Minimize Typing Strain
88
100
 
89
101
  - Have `Prettier` installed
90
102
  - Have `Editor: Word Wrap` enabled in VS Code:
@@ -94,38 +106,192 @@ function MyComponent() {
94
106
 
95
107
  `"editor.wordWrap": "on"`
96
108
 
97
- - Use single quotes (<kbd>'</kbd>) for class names, often a single key press on many keyboards.
98
- - Save the file (<kbd>Ctrl+S</kbd>), and Prettier does the formatting heavy-lifting, it automatically:
99
- - Replaces single quotes with double quotes.
100
- - Neatly arranges each class name on a new line.
109
+ - **Use single quotes** (<kbd>'</kbd>) for class names, often a single key press on many keyboards.
110
+ - **Save the file** (<kbd>Ctrl+S</kbd>), which activates `Prettier` to auto-format the file, it will:
111
+
112
+ - Replace single quotes with double quotes.
113
+ - Neatly arrange each class name on a new line.
114
+
115
+ ### Result
101
116
 
102
- ## Argument Handling
117
+ #### Before
103
118
 
104
- `mergeClassNames` only accepts **_non-empty string values_**, everything else like empty strings (`""`), `null`, `undefined`, numbers, objects and arrays is _ignored_. This ensures stricter and predictable output.
119
+ ![Screenshot of code before Prettier neatly formats code](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/before.png)
105
120
 
106
- ### Breaking Changes From Version 1.X.X
121
+ #### After
107
122
 
108
- In pervious versions, arguments that were not strings were implicitly converted to strings by the JavaScript engine.
123
+ ![Screenshot of code after Prettier neatly formats code](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/after.png)
109
124
 
110
- ### Console Warning for Invalid Arguments
125
+ ## Type Definitions (of Exported Functions)
111
126
 
112
- Invalid arguments are not silently ignored, as they may indicate a deeper issue. A single warning is logged to the developer console whenever these arguments are passed and ignored.
127
+ ```jsx
128
+ mergeClassNames: (...args: (string | false)[]) => string;
129
+ ```
130
+
131
+ ```jsx
132
+ mergeClassNamesDebugger: (...args: (string | false)[]) => string;
133
+ ```
134
+
135
+ ```jsx
136
+ isEmptyString: (argument: string) => boolean;
137
+ ```
113
138
 
114
- Example output:
139
+ For both `mergeClassNames` and `mergeClassNamesDebugger` they always return a string.
140
+
141
+ - If no inputs were provided e.g. `mergeClassNames()` or were invalid `mergeClassNames(undefined, " ")` then an _empty string_ is returned `""`
142
+
143
+ ## Accepted Arguments
144
+
145
+ `mergeClassNames(...args)` and `mergeClassNamesDebugger(...args)` only accept the following arguments:
146
+
147
+ - **Strings that are not empty, and are not whitespace** (for example: `"app"`, `"min-h-dvh"`, `" grid "`)
148
+
149
+ - The boolean value **`false`**
150
+
151
+ Everything else is an _invalid argument_ that will be _ignored_, and cause a _warning_ to be logged, these argument types include:
152
+
153
+ - _empty strings_ (`""`),
154
+ - _whitespace combinations_ (e.g. `"\n"`, `" \n\t "`, etc...),
155
+ - `null`,
156
+ - `undefined`,
157
+ - _numbers_,
158
+ - _objects_,
159
+ - _arrays_
160
+
161
+ ## Console Warning
162
+
163
+ Whenever invalid arguments are passed to `mergeClassNames` they are _not silently ignored_ because this can cause a lot of subtle bugs in the future and compound technical debt. Therefore a `console.warn` is printed in the _Developer Console_ to alert of the potentially deeper issue that requires rectifying. Example:
115
164
 
116
165
  ```plaintext
117
- [mergeClassNames] Warning: invalid arguments were provided and were ignored:
118
- - Expected all arguments to be strings, but got 4 non-string values: [(1/4): (undefined) of type "undefined", (2/4): (test) of type "object", (3/4): ([object Object]) of type "object", (4/4): (null) of type "object"].
119
- - Expected 0 empty strings, but got 2.
166
+ [mergeClassNames] Warning: invalid arguments were provided and ignored:
167
+
168
+ * Replace "mergeClassNames" with "mergeClassNamesDebugger" without changing any arguments, and open the Developer Console, or attach Debugger (see README.md).
169
+
170
+ * Expected all arguments to be either strings or value `false`, but got 5 invalid value(s):
171
+ [
172
+ (1/5): >undefined< of type "undefined",
173
+ (2/5): > test < of type "object",
174
+ (3/5): >[object Object]< of type "object",
175
+ (4/5): >true< of type "boolean",
176
+ (5/5): >null< of type "object"
177
+ ]
178
+
179
+ * Expected 0 empty strings, but got 2 invalid value(s):
180
+ [
181
+ (1/2): ><,
182
+ (2/2): > <
183
+ ]
184
+ ```
185
+
186
+ ## Conditionally Include Class Names
187
+
188
+ To conditionally include a class name, use the
189
+
190
+ - _Conditional operator_ `condition ? "class-name" : false` with `false` as the fallback value to maintain a clear and warning-free code.
191
+
192
+ This is because `false` will never cause the function to print a warning.
193
+
194
+ **Important**: Avoid using the
195
+
196
+ - _Short-circuit implicit syntax_ `condition && "class-name"` because it can produce falsy values like `0`, `""`, `undefined`, `null`, which will cause warnings to be logged.
197
+
198
+ ```jsx
199
+ import { mergeClassNames } from "simple-merge-class-names";
200
+
201
+ const MyComponent = () => {
202
+ return (
203
+ <div
204
+ className={mergeClassNames(
205
+ "app",
206
+ condition ? "min-h-dvh" : false,
207
+ "grid",
208
+ "grid-rows-[auto_1fr_auto]",
209
+ "outline"
210
+ )}
211
+ >
212
+ Hello, world!
213
+ </div>
214
+ );
215
+ };
216
+ ```
217
+
218
+ ## Using `mergeClassNamesDebugger` And The Built-in Browser Debugger To Find And Fix Warnings
219
+
220
+ `mergeClassNamesDebugger` is a drop-in replacement for `mergeClassNames` but with the added _benefit_ that it will activate the built-in **_debugger_** inside browsers like _FireFox_, _Chrome_, _Safari_ and even _VS Code_ if configured properly.
221
+
222
+ This built-in JavaScript feature gained wide-spread support from major browsers around 2012, so you are getting this feature for free with minimal effort, all you have to do are 2 things:
223
+
224
+ 1. Simply **_Open the Browser's Developer Tools_**, this tells the JavaScript engine that the Debugger is _enabled_.
225
+ 2. **_Replace_** `mergeClassNames` with **`mergeClassNamesDebugger`** _without_ changing any of the argument provided.
226
+
227
+ _When the Debugger is enabled (i.e. *Browser's Developer Tools* is open) and an invalid argument like `undefined` or `" "` is passed to `mergeClassNamesDebugger`, then the JavaScript engine will automatically pause execution and highlight the invalid argument. You simply have to select the offending component (`Container.jsx` in this case) from the Call Stack._
228
+
229
+ When the Debugger is active, it should look like this screenshot (in _FireFox_):
230
+
231
+ ![Firefox Debugger automatically paused execution when undefined was passed to mergeClassNamesDebugger](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/debugger.png)
232
+
233
+ ## Strategies To Ensure Correct Arguments Are Sent to `mergeClassNames`
234
+
235
+ - Use the _conditional operator_ to conditionally including class names:
236
+
237
+ ```jsx
238
+ import { mergeClassNames } from "simple-merge-class-names";
239
+
240
+ const MyComponent = () => {
241
+ return (
242
+ <div
243
+ className={mergeClassNames(
244
+ "app",
245
+ condition ? "min-h-dvh" : false,
246
+ "grid",
247
+ "grid-rows-[auto_1fr_auto]",
248
+ "outline"
249
+ )}
250
+ >
251
+ Hello, world!
252
+ </div>
253
+ );
254
+ };
255
+ ```
256
+
257
+ - If chaining the result of another `mergeClassNames` then use `isEmptyString` to check if the result is not the _empty string_ (`""`):
258
+
259
+ ```jsx
260
+ import { mergeClassNames, isEmptyString } from "simple-merge-class-names";
261
+
262
+ const MyComponent = ({ condition, resultOfAnotherMergeClassNames }) => {
263
+ return (
264
+ <div
265
+ className={mergeClassNames(
266
+ "app",
267
+
268
+ condition ? "min-h-dvh" : false,
269
+
270
+ isEmptyString(resultOfAnotherMergeClassNames)
271
+ ? false
272
+ : resultOfAnotherMergeClassNames,
273
+
274
+ "grid",
275
+ "grid-rows-[auto_1fr_auto]",
276
+ "outline"
277
+ )}
278
+ >
279
+ Hello, world!
280
+ </div>
281
+ );
282
+ };
120
283
  ```
121
284
 
122
285
  ## Testing
123
286
 
124
- This project uses `Vitest` as the test runner for fast, modern testing.
287
+ This project uses `Vitest` as the test runner for fast and modern testing.
125
288
 
126
- #### Run All Testing Once
289
+ #### Run All Tests Once
127
290
 
128
291
  ```bash
292
+ git clone https://github.com/new-AF/simple-merge-class-names
293
+ cd simple-merge-class-names
294
+ pnpm install
129
295
  pnpm test
130
296
  ```
131
297
 
@@ -140,56 +306,58 @@ pnpm test:watch
140
306
  ```javascript
141
307
  /**
142
308
  * mergeClassNames - A straightforward utility for merging CSS class names in React + Tailwind, and other JavaScript projects.
143
- ...
309
+ *
310
+ * @license AGPL-3.0
311
+ * Copyright (C) 2025 Abdullah Fatota
312
+ *
313
+ * ...
144
314
  */
145
315
 
146
- const isTypeString = (val) => typeof val === "string";
147
-
148
- const isNonEmptyString = (val) => val !== "";
149
-
150
- const partition = (array, keepPredicate) => {
151
- const keep = [];
152
- const ignore = [];
153
- for (const element of array) {
154
- (keepPredicate(element) ? keep : ignore).push(element);
155
- }
156
- return [keep, ignore];
157
- };
158
-
159
- export const mergeClassNames = (...args) => {
316
+ const mergeClassNamesCore = ({ args, activateDebugger }) => {
160
317
  const space = "\x20"; // ASCII code for a single space character (" "), decimal 32
161
318
 
162
- const [strings, nonStrings] = partition(args, isTypeString);
319
+ const [_, nonFalseValues] = partition(args, isValueFalse); // ignore all false values used for conditional class inclusion
320
+ const [strings, nonStrings] = partition(nonFalseValues, isTypeString);
321
+ const [emptyStrings, nonEmptyStrings] = partition(strings, isEmptyString);
322
+ const trimmed = nonEmptyStrings.map((val) => val.trim());
323
+ const className = trimmed.join(space);
163
324
 
164
- const trimmed = strings.map((val) => val.trim());
325
+ /* Don't silently ignore invalid input, explicitly disclose them as it may indicate a bigger problem */
326
+ warnInvalidArguments({ nonStrings, emptyStrings, activateDebugger });
165
327
 
166
- const [nonEmptyStrings, emptyStrings] = partition(
167
- trimmed,
168
- isNonEmptyString
169
- );
328
+ return className;
329
+ };
170
330
 
171
- const className = nonEmptyStrings.join(space);
331
+ export const mergeClassNames = (...args) =>
332
+ mergeClassNamesCore({ args, activateDebugger: false });
172
333
 
173
- /* Don't silently ignore invalid input, explicitly disclose them as it may indicate a bigger problem */
174
- const warn = [];
175
- /* ... */
334
+ export const mergeClassNamesDebugger = (...args) =>
335
+ mergeClassNamesCore({ args, activateDebugger: true });
176
336
  ```
177
337
 
178
338
  ## Misc.
179
339
 
180
- ### Why the Mismatch Between Exported Function and Package Name?
340
+ ### Motivation
181
341
 
182
- I wanted to name the package as `mergeClassNames` to reflect the single exported function, but the NPM Package Registry does not allow capital letters, only lower case and dash characters.
342
+ This package aims to improve code readability and developer experience in `React & Tailwind` projects by enforcing strict input handling. It logs warnings for invalid arguments, helping developers catch and fix underlying issues _early_.
183
343
 
184
- In addition there was already a package named `merge-class-names` but it is no longer maintained (and the developer recommends `clsx` instead).
344
+ In addition while writing class names as separate strings may seem tedious, the [workflow](#workflow-to-minimize-typing-strain) reduces friction and the overall process results in more readable and maintainable code than using single long strings:
185
345
 
186
- ### Where This Package Excels
346
+ ```jsx
347
+ const MyComponent = () => {
348
+ return (
349
+ <div className="app min-h-dvh grid grid-rows-[auto_1fr_auto] outline">
350
+ Hello, world!
351
+ </div>
352
+ );
353
+ };
354
+ ```
187
355
 
188
- While similar packages exist (`clsx`) with better features and potentially improved performance, `simple-merge-class-names` focuses on being very straightforward and easy to reason about, as defined in its source code.
356
+ ### Why the Mismatch Between Exported Function and Package Name?
189
357
 
190
- ## Production Considerations
358
+ I wanted to name the package `mergeClassNames` to reflect the exported function, but the NPM Package Registry doesn't allow capital letters, only lower case and dash characters.
191
359
 
192
- If you are considering this package for production, you might also want to look into `clsx`: [https://www.npmjs.com/package/clsx](https://www.npmjs.com/package/clsx)
360
+ In addition there was already a package named `merge-class-names` but it is no longer maintained (and the developer recommends `clsx` instead).
193
361
 
194
362
  ## License
195
363
 
@@ -1 +1,5 @@
1
- export declare const mergeClassNames: (...args: (string | boolean)[]) => string;
1
+ export declare const mergeClassNames: (...args: (string | false)[]) => string;
2
+ export declare const mergeClassNamesDebugger: (
3
+ ...args: (string | false)[]
4
+ ) => string;
5
+ export declare const isEmptyString: (argument: string) => boolean;
@@ -41,7 +41,10 @@ const isValueFalse = (val) => val === false;
41
41
 
42
42
  const isTypeString = (val) => typeof val === "string";
43
43
 
44
- const isNonEmptyString = (val) => val !== "";
44
+ export const isEmptyString = (val) => {
45
+ const trimmed = val.trim();
46
+ return trimmed === "";
47
+ };
45
48
 
46
49
  const partition = (array, keepPredicate) => {
47
50
  const keep = [];
@@ -52,62 +55,135 @@ const partition = (array, keepPredicate) => {
52
55
  return [keep, ignore];
53
56
  };
54
57
 
55
- export const mergeClassNames = (...args) => {
56
- const space = "\x20"; // ASCII code for a single space character (" "), decimal 32
57
-
58
- const [_, nonFalseValues] = partition(args, isValueFalse); // ignore all false values used for conditional class inclusion
59
-
60
- const [strings, nonStrings] = partition(nonFalseValues, isTypeString);
61
-
62
- const trimmed = strings.map((val) => val.trim());
63
-
64
- const [nonEmptyStrings, emptyStrings] = partition(
65
- trimmed,
66
- isNonEmptyString
67
- );
68
-
69
- const className = nonEmptyStrings.join(space);
58
+ const warnInvalidArguments = ({
59
+ nonStrings,
60
+ emptyStrings,
61
+ activateDebugger = false,
62
+ }) => {
63
+ const hasInvalidTypes = nonStrings.length > 0;
64
+ const hasEmptyStrings = emptyStrings.length > 0;
65
+ const doPrint = hasInvalidTypes || hasEmptyStrings;
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
+ );
70
102
 
71
- /* Don't silently ignore invalid input, explicitly disclose them as it may indicate a bigger problem */
72
- const warn = [];
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
+ );
126
+ }
73
127
 
74
128
  /* "Expected all arguments to be either ..." */
75
- if (nonStrings.length > 0) {
76
- const join = ", ";
77
- const count = nonStrings.length;
78
- const maxPrint = 10;
79
- const formatGotArray = (element, index) =>
80
- `(${index + 1}/${count}): (${element}) of type "${typeof element}"`;
81
- const message = (
82
- count > maxPrint ? nonStrings.slice(0, maxPrint) : nonStrings
83
- )
84
- .map(formatGotArray)
85
- .join(join);
86
- const suffix = count > maxPrint ? ", ... ]" : "]";
87
-
88
- warn.push(
89
- `Expected all arguments to be either strings or value "false", but got ${count} invalid values: [${message}${suffix}.`
129
+ if (hasInvalidTypes) {
130
+ if (activateDebugger) {
131
+ debugger;
132
+ }
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
+ })
90
140
  );
91
141
  }
92
142
 
93
143
  /* "Expected 0 empty strings ..." */
94
- if (emptyStrings.length > 0) {
95
- const count = emptyStrings.length;
96
- warn.push(`Expected 0 empty strings, but got ${count}.`);
144
+ if (hasEmptyStrings) {
145
+ if (activateDebugger) {
146
+ debugger;
147
+ }
148
+ messages.push(
149
+ makeMessage({
150
+ array: emptyStrings,
151
+ expected: "* Expected 0 empty strings",
152
+ })
153
+ );
97
154
  }
98
155
 
99
- /* Full Warn Message */
100
- if (warn.length > 0) {
101
- const newline = "\n";
102
- const prefix = "\t" + "- ";
103
- const message = warn.map((text) => `${prefix}${text}`).join(newline);
156
+ /* Full Warn makeMessage */
157
+ const functionName =
158
+ activateDebugger === false
159
+ ? "mergeClassNames"
160
+ : "mergeClassNamesDebugger";
104
161
 
105
- console.warn(
106
- "[mergeClassNames] Warning: invalid arguments were provided and were ignored:",
107
- newline,
108
- message
109
- );
110
- }
162
+ const string = [
163
+ `[${functionName}] Warning: invalid arguments were provided and ignored:`,
164
+ ...messages,
165
+ ].join(doubleNewline);
166
+
167
+ console.warn(string);
168
+ };
169
+
170
+ const mergeClassNamesCore = ({ args, activateDebugger }) => {
171
+ const space = "\x20"; // ASCII code for a single space character (" "), decimal 32
172
+
173
+ const [_, nonFalseValues] = partition(args, isValueFalse); // ignore all false values used for conditional class inclusion
174
+ const [strings, nonStrings] = partition(nonFalseValues, isTypeString);
175
+ const [emptyStrings, nonEmptyStrings] = partition(strings, isEmptyString);
176
+ const trimmed = nonEmptyStrings.map((val) => val.trim());
177
+ const className = trimmed.join(space);
178
+
179
+ /* Don't silently ignore invalid input, explicitly disclose them as it may indicate a bigger problem */
180
+ warnInvalidArguments({ nonStrings, emptyStrings, activateDebugger });
111
181
 
112
182
  return className;
113
183
  };
184
+
185
+ export const mergeClassNames = (...args) =>
186
+ mergeClassNamesCore({ args, activateDebugger: false });
187
+
188
+ export const mergeClassNamesDebugger = (...args) =>
189
+ mergeClassNamesCore({ args, activateDebugger: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-merge-class-names",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "A straightforward utility for merging CSS class names in React + Tailwind and JavaScript projects.",
5
5
  "exports": {
6
6
  ".": {