simple-merge-class-names 9.0.0 → 9.2.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,84 +1,38 @@
1
- # simple-merge-class-names
1
+ - [Why this package exists](#why-this-package-exists)
2
+ - [Why not `clsx`](#why-not-clsx)
3
+ - [Exported functions](#exported-functions)
4
+ - [Quick start](#quick-start)
5
+ - [Valid arguments](#valid-arguments)
6
+ - [Invalid arguments](#invalid-arguments)
7
+ - [Github](#github)
8
+ - [License](#license)
2
9
 
3
- A class names merger for TypeScript, JavaScript, TSX / JSX (React).
10
+ # Why this package exists
4
11
 
5
- > For production purposes there is also [https://www.npmjs.com/package/clsx](https://www.npmjs.com/package/clsx)
12
+ I got tired of squinting at the single line TailwindCSS `className = "..." ` string cramming a dozen utility classes with no delimiter between them, so I organically created a function that would split each string on a new line, and merge them back.
6
13
 
7
- ## Table of Contents
14
+ # Why not `clsx`
8
15
 
9
- - [simple-merge-class-names](#simple-merge-class-names)
10
- - [Table of Contents](#table-of-contents)
11
- - [Stop Starving Gaza](#stop-starving-gaza)
12
- - [Install](#install)
13
- - [Install `Prettier` too](#install-prettier-too)
14
- - [Exported Functions](#exported-functions)
15
- - [Non-scale Usage](#non-scale-usage)
16
- - [Example](#example)
17
- - [Scale Usage](#scale-usage)
18
- - [Create intermediary `@/mergeClassNames.js`](#create-intermediary-mergeclassnamesjs)
19
- - [Project Structure](#project-structure)
20
- - [Enable `@`-style Imports in Vite](#enable--style-imports-in-vite)
21
- - [Example](#example-1)
22
- - [During Dev, Debug Entire Project](#during-dev-debug-entire-project)
23
- - [Valid Arguments](#valid-arguments)
24
- - [Example](#example-2)
25
- - [Invalid Arguments](#invalid-arguments)
26
- - [Example](#example-3)
27
- - [Developer Console Warnings](#developer-console-warnings)
28
- - [Conditional Class Inclusion](#conditional-class-inclusion)
29
- - [Avoid Short-circuit Syntax](#avoid-short-circuit-syntax)
30
- - [Return Result](#return-result)
31
- - [Example](#example-4)
32
- - [Side Effect](#side-effect)
33
- - [Example](#example-5)
34
- - [Chaining](#chaining)
35
- - [Usage of Browser Debugger](#usage-of-browser-debugger)
36
- - [Testing Source Code](#testing-source-code)
37
- - [Run Once](#run-once)
38
- - [Run Watch Mode](#run-watch-mode)
39
- - [License](#license)
16
+ I didn't like the unreadable [code](https://github.com/lukeed/clsx/blob/master/src/index.js) and the fact it accepts all types of arguments. I thought my engineering approach is better both in terms of code readability and strictness.
40
17
 
41
- ## Stop Starving Gaza
18
+ I used TypeScript and a functional programming approach to develop it.
42
19
 
43
- - [Donate Direct Aid to Gazan Families](https://gazafunds.com/)
20
+ # Exported functions
44
21
 
45
- - [Call US Congress and Demand Immediate Opening of ALL Gaza Border Crossings](https://act.uscpr.org/a/letaidin)
22
+ | | Warns | Invokes debugger |
23
+ | ----------------------------- | ------ | ---------------- |
24
+ | `mergeClassNames` | ✅ | ❌ |
25
+ | `mergeClassNamesDebugger` | ✅ | ✅ |
26
+ | `createCustomMergeClassNames` | Custom | Custom |
46
27
 
47
- - [Boycott Brands Supporting Gaza Holocaust](https://www.uplift.ie/bds/)
48
-
49
- - [Legal Action](https://www.hindrajabfoundation.org/perpetrators)
50
-
51
- ## Install
52
-
53
- ```bash
54
- pnpm add simple-merge-class-names
55
-
56
- # or
57
- yarn add simple-merge-class-names
58
-
59
- # or
60
- npm install simple-merge-class-names
61
- ```
62
-
63
- ### Install `Prettier` too
64
-
65
- [https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
66
-
67
- It will nicely format and split your classes across new lines, and alleviate wrist strain because you will use single quotes (single key) instead of double (2 keys).
68
-
69
- ## Exported Functions
70
-
71
- | | `console.warn`s | Invokes JS `debugger;` statement on invalid arguments, which pauses execution when Debugger is attached |
72
- | ------------------------- | --------------- | ------------------------------------------------------------------------------------------------------- |
73
- | `mergeClassNames` | ✅ | ❌ |
74
- | `mergeClassNamesDebugger` | ✅ | ✅ |
75
-
76
- ## Non-scale Usage
77
-
78
- ### Example
28
+ # Quick start
79
29
 
80
30
  ```jsx
81
- // src/App.jsx
31
+ /*
32
+ {
33
+ "fileName": "App.jsx"
34
+ }
35
+ */
82
36
  import { mergeClassNames } from "simple-merge-class-names";
83
37
 
84
38
  const Component = ({ condition }) => {
@@ -98,301 +52,34 @@ const Component = ({ condition }) => {
98
52
  };
99
53
  ```
100
54
 
101
- ## Scale Usage
102
-
103
- ![diagram explanation of best way to use package](./.github/images/root-file.png)
104
-
105
- ### Create intermediary `@/mergeClassNames.js`
106
-
107
- #### Project Structure
108
-
109
- ```
110
- my-react-app/
111
- ├─ src/
112
- │ ├─ components/
113
- │ ├─ mergeClassNames.js // <- create it here
114
- │ ├─ App.jsx
115
- │ └─ index.js
116
- ├─ package.json
117
- ├─ vite.config.js
118
- └─ README.md
119
- ```
120
-
121
- #### Enable `@`-style Imports in Vite
122
-
123
- `@` points to `my-react-app/src`
124
-
125
- ```js
126
- // vite.config.js
127
-
128
- import { defineConfig } from "vite";
129
- import react from "@vitejs/plugin-react";
130
- import tailwindcss from "@tailwindcss/vite";
131
- import path from "path";
132
-
133
- // https://vitejs.dev/config/
134
- export default defineConfig({
135
- plugins: [react(), tailwindcss()],
136
-
137
- resolve: {
138
- alias: {
139
- "@": path.resolve(__dirname, "src"), // @ now points to "my-react-app/src"
140
- },
141
- },
142
- });
143
- ```
144
-
145
- ```js
146
- // src/mergeClassNames.js
147
-
148
- export { mergeClassNames } from "simple-merge-class-names";
149
-
150
- // export { mergeClassNames as mergeClassNamesDebugger } from "simple-merge-class-names";
151
- ```
152
-
153
- And toggle-comment between the first and second lines as needed.
154
-
155
- ### Example
156
-
157
- #### During Dev, Debug Entire Project
158
-
159
- ```js
160
- // @/mergeClassNames.js
161
-
162
- // export { mergeClassNames } from "simple-merge-class-names";
163
-
164
- export { mergeClassNames as mergeClassNamesDebugger } from "simple-merge-class-names";
165
- ```
166
-
167
- ```jsx
168
- // @/App.jsx
169
-
170
- import { mergeClassNames } from "@/mergeClassNames.js";
171
-
172
- const Component = ({ condition }) => {
173
- return (
174
- <div
175
- className={mergeClassNames(
176
- "app",
177
- condition ? "min-h-dvh" : false,
178
- "grid",
179
- "grid-rows-[auto_1fr_auto]",
180
- "outline",
181
- )}
182
- >
183
- Hello, world!
184
- </div>
185
- );
186
- };
187
- ```
188
-
189
- ## Valid Arguments
55
+ # Valid arguments
190
56
 
191
- Only 2:
57
+ Valid arguments are:
192
58
 
193
- 1. A valid string (not empty, not fully whitespace)
194
- 2. Value `false`
59
+ 1. `string`s, that are not fully whitespace, these will be the class names you pass.
60
+ 2. Empty string `""`
61
+ 3. `null`
62
+ 4. `undefined`
63
+ 5. `false`
195
64
 
196
- ### Example
65
+ The latter four are valid but will be ignored as they're commonly used in conditional expressions.
197
66
 
198
- ```js
199
- mergeClassNames(
200
- condition ? "daisy-btn-active" : false
201
- "mx-auto",
202
- "min-dvh ",
203
- " flex",
204
- " grid ",
205
- "italic font-bold ",
206
- `
207
- gap-y-4
208
- `,
209
- );
210
- ```
211
-
212
- ## Invalid Arguments
67
+ # Invalid arguments
213
68
 
214
- Anything that is not a valid argument, this includes:
215
-
216
- - Invalid strings:
217
- - Empty strings: _(`""`)_
218
- - Fully whitespace strings: any consecutive combination of the following:
219
- - new lines,
220
- - spaces,
221
- - tabs
222
- - _(e.g. `" "`, `"\n "`, `" \t \n "`, etc.)_
223
- - `true`
224
- - `undefined`
225
- - `null`
69
+ - Fully whitespace strings
226
70
  - Objects
227
- - Numbers
228
- - Big Int
71
+ - Arrays
72
+ - `true`
229
73
  - Symbols
230
74
 
231
- _All of above will be **ignored**, and cause a `console.warn` to be printed_
232
-
233
- ### Example
75
+ These will cause a console warning to be printed to alert you something might be off.
234
76
 
235
- ```js
236
- const someVariable = "";
237
-
238
- mergeClassNames(
239
- someVariable,
240
- " ",
241
- "\n ",
242
- " \t \n ",
243
- `
244
- \n
245
- `,
246
- true,
247
- undefined,
248
- null,
249
- {
250
- name: "name",
251
- email: "name@example.com",
252
- },
253
- 123,
254
- 123.45,
255
- );
256
- ```
77
+ # Github
257
78
 
258
- #### Developer Console Warnings
79
+ [https://github.com/new-AF/simple-merge-class-names](https://github.com/new-AF/simple-merge-class-names)
259
80
 
260
- ![screenshot of console.warn warnings because invalid arguments were provided and ignored, so no silent failing](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/console-warnings.png)
261
-
262
- ## Conditional Class Inclusion
263
-
264
- ```jsx
265
- mergeClassNames(condition ? "min-h-dvh" : false);
266
-
267
- // or if you want preciseness.
268
- mergeClassNames(condition === true ? "min-h-dvh" : false);
269
- ```
270
-
271
- ### Avoid Short-circuit Syntax
272
-
273
- Avoid the syntax (`condition && "class-name"`) because it can produce falsy values (e.g. `0`, `""`, `undefined`, `null`) which will be ignored and warned about.
274
-
275
- ## Return Result
276
-
277
- String of all merged valid classes. Invalid arguments are ignored and warned about.
278
-
279
- ### Example
280
-
281
- ```jsx
282
- import { mergeClassNames } from "simple-merge-class-names";
283
-
284
- // "app min-h-dvh grid grid-rows-[auto_1fr_auto] outline"
285
- mergeClassNames(
286
- " app ",
287
- undefined,
288
- [" test "],
289
- { key: "value" },
290
- "",
291
- "min-h-dvh",
292
- "grid ",
293
- true,
294
- null,
295
- "grid-rows-[auto_1fr_auto]",
296
- "outline",
297
- " ",
298
- );
299
- ```
300
-
301
- ## Side Effect
302
-
303
- `console.warn`s if arguments contain invalid arguments.
304
-
305
- ### Example
306
-
307
- ```
308
- Ignored invalid argument: >undefined< (undefined)
309
- Ignored invalid argument: > test < (object)
310
- Ignored invalid argument: >[object Object]< (object)
311
- Ignored empty string: ""
312
- Ignored invalid argument: >true< (boolean)
313
- Ignored invalid argument: >null< (object)
314
- Ignored whitespace string:
315
- ```
316
-
317
- > _It can be empy_
318
-
319
- 1. **Valid `string`, never whitespace, always length >= 1**
320
-
321
- ## Chaining
322
-
323
- Because of safe return types you can chain calls safely without worrying about warnings or arguments being ignored.
324
-
325
- _So this is OK:_
326
-
327
- ```js
328
- mergeClassNames(condition ? "disabled" : mergeClassNames(...) )
329
- ```
330
-
331
- ## Usage of Browser Debugger
332
-
333
- 1. Use **`import {mergeClassNamesDebugger as mergeClassNames}`** to debug the entire file.
334
-
335
- ```jsx
336
- import { mergeClassNamesDebugger as mergeClassNames } from "simple-merge-class-names";
337
-
338
- const Component = ({ condition }) => {
339
- return (
340
- <div
341
- className={mergeClassNames(
342
- "app",
343
- condition ? "min-h-dvh" : false,
344
- "grid",
345
- "grid-rows-[auto_1fr_auto]",
346
- "outline",
347
- )}
348
- >
349
- Hello, world!
350
- </div>
351
- );
352
- };
353
- ```
354
-
355
- 2. Enable Debugger
356
- - _For chromium-based browsers it's On by default and you don't need to do anything AFAIK._
357
- - _For Firefox:_ Open **_Developer Tools_:**
358
- - _Make Sure_ **_Debugger_** _(tab)_ -> **`Pause on debugger statement`** is ticked.
359
- - Keep Dev Tools open.
360
-
361
- ![screenshot of Firefox Debugger section with Pause on debugger statement ticked on](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/debugger-check.png)
362
-
363
- 3. Refresh the page, the debugger should connect:
364
- - Navigate to the **_Call stack_**
365
- - Click the function/component right before _`mergeClassNamesDebugger`_
366
-
367
- ![screenshot of Firefox debugger active because of `undefined` invalid class name argument](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/debugger-active.png)
368
-
369
- 4. Hover over the arguments, one or several should be invalid:
370
-
371
- ![screenshot of Firefox debugger active because of `undefined` invalid class name argument](https://raw.githubusercontent.com/new-AF/simple-merge-class-names/main/.github/images/debugger-3.png)
372
-
373
- ## Testing Source Code
374
-
375
- This project uses `Vitest` as the test runner for fast and modern testing.
376
-
377
- ### Run Once
378
-
379
- ```bash
380
- git clone https://github.com/new-AF/simple-merge-class-names
381
- cd simple-merge-class-names
382
- pnpm install
383
- pnpm test
384
- ```
385
-
386
- ### Run Watch Mode
387
-
388
- ```bash
389
- pnpm test:watch
390
- ```
391
-
392
- ## License
81
+ # License
393
82
 
394
83
  This project is licensed under the AGPL-3.0 License. See `LICENSE.txt` for full details.
395
84
 
396
- ---
397
-
398
85
  Enjoy 😉
package/dist/index.d.mts CHANGED
@@ -1,3 +1,9 @@
1
+ type CustomOptions = {
2
+ warnings?: boolean;
3
+ "activate-debugger"?: boolean;
4
+ name?: string;
5
+ };
6
+
1
7
  /**
2
8
  * mergeClassNames - A class names merger for TypeScript, JavaScript, TSX / JSX (React).
3
9
  *
@@ -33,7 +39,8 @@ Invalid arguments: anything else e.g.
33
39
  - etc.
34
40
  */
35
41
 
42
+ declare const createCustomMergeClassNames: (options: CustomOptions) => (...input: (string | false)[]) => string;
36
43
  declare const mergeClassNames: (...input: (string | false)[]) => string;
37
44
  declare const mergeClassNamesDebugger: (...input: (string | false)[]) => string;
38
45
 
39
- export { mergeClassNames, mergeClassNamesDebugger };
46
+ export { createCustomMergeClassNames, mergeClassNames, mergeClassNamesDebugger };
package/dist/index.mjs CHANGED
@@ -140,6 +140,6 @@ Invalid arguments: anything else e.g.
140
140
  - etc.
141
141
  */
142
142
 
143
- export { mergeClassNames, mergeClassNamesDebugger };
143
+ export { createCustomMergeClassNames, mergeClassNames, mergeClassNamesDebugger };
144
144
  //# sourceMappingURL=index.mjs.map
145
145
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/mergeClassNames.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import {\r\n Classified,\r\n ClassifiedInvalidWarn,\r\n ClassifiedInvalidReason,\r\n ClassifiedClassName,\r\n} from \"./types\";\r\n\r\n// classifies input arguments\r\nexport const classify = (\r\n value: string | undefined | null | false,\r\n): Classified => {\r\n // FP pattern of mapping values with extra information.\r\n // The core computes data.\r\n // because TS types disappear in JS runtime\r\n\r\n // valid but ignored\r\n if (\r\n value === false ||\r\n value === undefined ||\r\n value === null ||\r\n value === \"\"\r\n ) {\r\n return {\r\n status: \"ignore\",\r\n value,\r\n };\r\n }\r\n\r\n // invalid\r\n if (typeof value !== \"string\") {\r\n return {\r\n status: \"invalid\",\r\n value,\r\n reason: ClassifiedInvalidReason.NotAString,\r\n };\r\n }\r\n\r\n // it's a string\r\n\r\n const trimmed = value.trim();\r\n\r\n // invalid\r\n if (trimmed === \"\") {\r\n return {\r\n status: \"invalid\",\r\n value,\r\n reason: ClassifiedInvalidReason.Whitespace,\r\n };\r\n }\r\n\r\n // valid\r\n return {\r\n status: \"class-name\",\r\n value: trimmed,\r\n };\r\n};\r\n\r\n// get only valid objects\r\nexport const getClassNames = (values: Classified[]): ClassifiedClassName[] => {\r\n return values.filter((obj) => obj.status === \"class-name\");\r\n};\r\n\r\n// get only valid objects\r\nexport const getInvalid = (values: Classified[]): ClassifiedInvalidWarn[] => {\r\n return values.filter((obj) => obj.status === \"invalid\");\r\n};\r\n\r\nexport const warningMessage = (\r\n { value, reason }: ClassifiedInvalidWarn,\r\n functionName: string,\r\n): string => {\r\n const format = (message: string) => `[${functionName}] ${message}`;\r\n\r\n if (reason === ClassifiedInvalidReason.NotAString) {\r\n // null, undefined\r\n if (value === null || value === undefined) {\r\n return format(`Ignored non-string argument: ${value}`);\r\n }\r\n\r\n // true (because false was filtered out)\r\n if (value === true) {\r\n return format(`Ignored non-string argument: ${value} (boolean)`);\r\n }\r\n\r\n // array\r\n if (Array.isArray(value)) {\r\n const sub = value.slice(0, 3);\r\n const string = JSON.stringify(sub).slice(1, -1);\r\n const ellipsisPart = value.length > sub.length ? \", ...\" : \"\";\r\n\r\n return format(\r\n `Ignored non-string argument: array ([${string}${ellipsisPart}])`,\r\n );\r\n }\r\n }\r\n\r\n // empty string\r\n if (reason === ClassifiedInvalidReason.EmptyString) {\r\n return format(`Ignored empty string argument.`);\r\n }\r\n\r\n // whitespace\r\n if (reason === ClassifiedInvalidReason.Whitespace) {\r\n return format(`Ignored whitespace string argument: \"${value}\"`);\r\n }\r\n\r\n // object, symbol, etc.\r\n return format(`Ignored non-string argument: ${value} (${typeof value})`);\r\n};\r\n\r\n// console.warn\r\nexport const warn = (invalid: ClassifiedInvalidWarn, functionName: string) => {\r\n const warning = warningMessage(invalid, functionName);\r\n\r\n console.warn(warning);\r\n};\r\n\r\n// activates debugger\r\nexport const activateDebugger = (_invalid: ClassifiedInvalidWarn) => {\r\n debugger;\r\n};\r\n","/**\r\n * mergeClassNames - A class names merger for TypeScript, JavaScript, TSX / JSX (React).\r\n *\r\n * @license AGPL-3.0\r\n * Copyright (C) 2026 Abdullah Fatota\r\n *\r\n *\r\n * This program is free software: you can redistribute it and/or modify\r\n * it under the terms of the GNU Affero General Public License as published by\r\n * the Free Software Foundation, either version 3 of the License, or\r\n * (at your option) any later version.\r\n *\r\n * This program is distributed in the hope that it will be useful,\r\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n * GNU Affero General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU Affero General Public License\r\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\r\n \r\n\r\nOnly Valid Arguments:\r\n 1) valid strings: non-empty, non-whitespace\r\n 2) value `false`\r\n\r\nInvalid arguments: anything else e.g.\r\n - empty strings\r\n - whitespace strings\r\n - arrays\r\n - numbers\r\n - objects\r\n - value true\r\n - etc.\r\n*/\r\n\r\nimport {\r\n ClassifiedInvalidFunction,\r\n ClassifiedInvalidWarn,\r\n CustomOptions,\r\n} from \"./types\";\r\n\r\nimport {\r\n classify,\r\n getInvalid,\r\n warn,\r\n activateDebugger,\r\n getClassNames,\r\n} from \"./utils\";\r\n\r\n// joins valid strings into final className\r\nconst mergeClassNamesCore = (\r\n values: (string | false)[],\r\n onClassifiedInvalid?: ClassifiedInvalidFunction,\r\n) => {\r\n // classify arguments\r\n const classified = values.map(classify);\r\n\r\n // optional call invalid arguments handlers: warn and/or activate debugger\r\n if (onClassifiedInvalid) {\r\n getInvalid(classified).forEach(onClassifiedInvalid);\r\n }\r\n\r\n // valid strings only\r\n const classNames: string[] = getClassNames(classified).map(\r\n ({ value }) => value,\r\n );\r\n\r\n const finalClassName = classNames.join(\" \");\r\n return finalClassName;\r\n};\r\n\r\n// creates custom mergeClassNames e.g. warn = false, activate debugger = true\r\nexport const createCustomMergeClassNames = (options: CustomOptions) => {\r\n const invalidHandlers: ClassifiedInvalidFunction[] = [];\r\n\r\n // default: Custom mergeClassNames. will be logged in console\r\n const functionName = options.name ?? \"Custom mergeClassNames\";\r\n\r\n // default: falsey\r\n if (options[\"warnings\"]) {\r\n invalidHandlers.push((arg) => warn(arg, functionName));\r\n }\r\n\r\n // default: falsy\r\n if (options[\"activate-debugger\"]) {\r\n invalidHandlers.push(activateDebugger);\r\n }\r\n\r\n const combinedInvalidHandler = (value: ClassifiedInvalidWarn) => {\r\n invalidHandlers.forEach((func) => func(value));\r\n };\r\n\r\n // construct the mergeClassNames function\r\n return (...input: (string | false)[]) =>\r\n mergeClassNamesCore(\r\n input,\r\n invalidHandlers.length > 0 ? combinedInvalidHandler : undefined,\r\n );\r\n};\r\n\r\nexport const mergeClassNames = createCustomMergeClassNames({\r\n warnings: true,\r\n \"activate-debugger\": false,\r\n name: \"mergeClassNames\",\r\n});\r\n\r\nexport const mergeClassNamesDebugger = createCustomMergeClassNames({\r\n warnings: true,\r\n \"activate-debugger\": true,\r\n name: \"mergeClassNamesDebugger\",\r\n});\r\n"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/mergeClassNames.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import {\r\n Classified,\r\n ClassifiedInvalidWarn,\r\n ClassifiedInvalidReason,\r\n ClassifiedClassName,\r\n} from \"./types\";\r\n\r\n// classifies input arguments\r\nexport const classify = (\r\n value: string | undefined | null | false,\r\n): Classified => {\r\n // FP pattern of mapping values with extra information.\r\n // The core computes data.\r\n // because TS types disappear in JS runtime\r\n\r\n // valid but ignored\r\n if (\r\n value === false ||\r\n value === undefined ||\r\n value === null ||\r\n value === \"\"\r\n ) {\r\n return {\r\n status: \"ignore\",\r\n value,\r\n };\r\n }\r\n\r\n // invalid\r\n if (typeof value !== \"string\") {\r\n return {\r\n status: \"invalid\",\r\n value,\r\n reason: ClassifiedInvalidReason.NotAString,\r\n };\r\n }\r\n\r\n // it's a string\r\n\r\n const trimmed = value.trim();\r\n\r\n // invalid\r\n if (trimmed === \"\") {\r\n return {\r\n status: \"invalid\",\r\n value,\r\n reason: ClassifiedInvalidReason.Whitespace,\r\n };\r\n }\r\n\r\n // valid\r\n return {\r\n status: \"class-name\",\r\n value: trimmed,\r\n };\r\n};\r\n\r\n// get only valid objects\r\nexport const getClassNames = (values: Classified[]): ClassifiedClassName[] => {\r\n return values.filter((obj) => obj.status === \"class-name\");\r\n};\r\n\r\n// get only valid objects\r\nexport const getInvalid = (values: Classified[]): ClassifiedInvalidWarn[] => {\r\n return values.filter((obj) => obj.status === \"invalid\");\r\n};\r\n\r\nexport const warningMessage = (\r\n { value, reason }: ClassifiedInvalidWarn,\r\n functionName: string,\r\n): string => {\r\n const format = (message: string) => `[${functionName}] ${message}`;\r\n\r\n if (reason === ClassifiedInvalidReason.NotAString) {\r\n // null, undefined\r\n if (value === null || value === undefined) {\r\n return format(`Ignored non-string argument: ${value}`);\r\n }\r\n\r\n // true (because false was filtered out)\r\n if (value === true) {\r\n return format(`Ignored non-string argument: ${value} (boolean)`);\r\n }\r\n\r\n // array\r\n if (Array.isArray(value)) {\r\n const sub = value.slice(0, 3);\r\n const string = JSON.stringify(sub).slice(1, -1);\r\n const ellipsisPart = value.length > sub.length ? \", ...\" : \"\";\r\n\r\n return format(\r\n `Ignored non-string argument: array ([${string}${ellipsisPart}])`,\r\n );\r\n }\r\n }\r\n\r\n // empty string\r\n if (reason === ClassifiedInvalidReason.EmptyString) {\r\n return format(`Ignored empty string argument.`);\r\n }\r\n\r\n // whitespace\r\n if (reason === ClassifiedInvalidReason.Whitespace) {\r\n return format(`Ignored whitespace string argument: \"${value}\"`);\r\n }\r\n\r\n // object, symbol, etc.\r\n return format(`Ignored non-string argument: ${value} (${typeof value})`);\r\n};\r\n\r\n// console.warn\r\nexport const warn = (invalid: ClassifiedInvalidWarn, functionName: string) => {\r\n const warning = warningMessage(invalid, functionName);\r\n\r\n console.warn(warning);\r\n};\r\n\r\n// activates debugger\r\nexport const activateDebugger = (_invalid: ClassifiedInvalidWarn) => {\r\n debugger;\r\n};\r\n","/**\r\n * mergeClassNames - A class names merger for TypeScript, JavaScript, TSX / JSX (React).\r\n *\r\n * @license AGPL-3.0\r\n * Copyright (C) 2026 Abdullah Fatota\r\n *\r\n *\r\n * This program is free software: you can redistribute it and/or modify\r\n * it under the terms of the GNU Affero General Public License as published by\r\n * the Free Software Foundation, either version 3 of the License, or\r\n * (at your option) any later version.\r\n *\r\n * This program is distributed in the hope that it will be useful,\r\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n * GNU Affero General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU Affero General Public License\r\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\r\n \r\n\r\nOnly Valid Arguments:\r\n 1) valid strings: non-empty, non-whitespace\r\n 2) value `false`\r\n\r\nInvalid arguments: anything else e.g.\r\n - empty strings\r\n - whitespace strings\r\n - arrays\r\n - numbers\r\n - objects\r\n - value true\r\n - etc.\r\n*/\r\n\r\nimport {\r\n ClassifiedInvalidFunction,\r\n ClassifiedInvalidWarn,\r\n CustomOptions,\r\n} from \"./types\";\r\n\r\nimport {\r\n classify,\r\n getInvalid,\r\n warn,\r\n activateDebugger,\r\n getClassNames,\r\n} from \"./utils\";\r\n\r\n// joins valid strings into final className\r\nconst mergeClassNamesCore = (\r\n values: (string | false)[],\r\n onClassifiedInvalid?: ClassifiedInvalidFunction,\r\n) => {\r\n // classify arguments\r\n const classified = values.map(classify);\r\n\r\n // optional call invalid arguments handlers: warn and/or activate debugger\r\n if (onClassifiedInvalid) {\r\n getInvalid(classified).forEach(onClassifiedInvalid);\r\n }\r\n\r\n // valid strings only\r\n const classNames: string[] = getClassNames(classified).map(\r\n ({ value }) => value,\r\n );\r\n\r\n const finalClassName = classNames.join(\" \");\r\n return finalClassName;\r\n};\r\n\r\n// creates custom mergeClassNames e.g. warn = false, activate debugger = true\r\nexport const createCustomMergeClassNames = (options: CustomOptions) => {\r\n const invalidHandlers: ClassifiedInvalidFunction[] = [];\r\n\r\n // default: Custom mergeClassNames. will be logged in console\r\n const functionName = options.name ?? \"Custom mergeClassNames\";\r\n\r\n // default: falsey\r\n if (options[\"warnings\"]) {\r\n invalidHandlers.push((arg) => warn(arg, functionName));\r\n }\r\n\r\n // default: falsy\r\n if (options[\"activate-debugger\"]) {\r\n invalidHandlers.push(activateDebugger);\r\n }\r\n\r\n const combinedInvalidHandler = (value: ClassifiedInvalidWarn) => {\r\n invalidHandlers.forEach((func) => func(value));\r\n };\r\n\r\n // construct the mergeClassNames function\r\n return (...input: (string | false)[]) =>\r\n mergeClassNamesCore(\r\n input,\r\n invalidHandlers.length > 0 ? combinedInvalidHandler : undefined,\r\n );\r\n};\r\n\r\nexport const mergeClassNames = createCustomMergeClassNames({\r\n warnings: true,\r\n \"activate-debugger\": false,\r\n name: \"mergeClassNames\",\r\n});\r\n\r\nexport const mergeClassNamesDebugger = createCustomMergeClassNames({\r\n warnings: true,\r\n \"activate-debugger\": true,\r\n name: \"mergeClassNamesDebugger\",\r\n});\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-merge-class-names",
3
- "version": "9.0.0",
3
+ "version": "9.2.0",
4
4
  "description": " A class names merger for TypeScript, JavaScript, TSX, and JSX (React)",
5
5
  "exports": {
6
6
  ".": {