Skip to content

Commit 6c4f2ef

Browse files
committed
Refactor to move implementation to lib/
1 parent c626444 commit 6c4f2ef

File tree

3 files changed

+128
-120
lines changed

3 files changed

+128
-120
lines changed

index.js

Lines changed: 4 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,7 @@
11
/**
2-
* @typedef {import('hast').Root} Root
3-
* @typedef {import('hast').Content} Content
2+
* @typedef {import('./lib/index.js').Conditional} Conditional
3+
* @typedef {import('./lib/index.js').ConditionalMap} ConditionalMap
4+
* @typedef {import('./lib/index.js').ConditionalPrimitive} ConditionalPrimitive
45
*/
56

6-
/**
7-
* @typedef {Root | Content} Node
8-
* @typedef {string | number} ConditionalPrimitive
9-
* Basic class names.
10-
* @typedef {Record<string, boolean>} ConditionalMap
11-
* Map of class names as keys, with whether they’re turned on or not as
12-
* values.
13-
* @typedef {null | undefined | ConditionalPrimitive | ConditionalMap | Array<ConditionalPrimitive | ConditionalMap | Array<ConditionalPrimitive | ConditionalMap>>} Conditional
14-
* Different ways to turn class names on or off.
15-
* @typedef {Record<string, boolean>} ClassMap
16-
* Map of class names as keys, with whether they’re turned on or not as
17-
* values.
18-
*/
19-
20-
import {parse} from 'space-separated-tokens'
21-
22-
const own = {}.hasOwnProperty
23-
24-
/**
25-
* Merge classes.
26-
*
27-
* @param node
28-
* Optionally, node whose classes to append to.
29-
* @param conditionals
30-
* Class configuration to merge.
31-
* @returns
32-
* The given node, if any, or a list of strings.
33-
*/
34-
export const classnames =
35-
/**
36-
* @type {(
37-
* (<T extends Node>(node: T, ...conditions: Array<Conditional>) => T) &
38-
* ((...conditions: Array<Conditional>) => Array<string>)
39-
* )}
40-
*/
41-
(
42-
/**
43-
* @param {Node | Conditional | null | undefined} [node]
44-
* @param {Array<Conditional>} conditionals
45-
*/
46-
function (node, ...conditionals) {
47-
let index = -1
48-
/** @type {ClassMap} */
49-
const map = Object.create(null)
50-
/** @type {Array<string>} */
51-
const list = []
52-
53-
if (isNode(node)) {
54-
if (node.type !== 'element') throw new Error('Expected element node')
55-
56-
if (!node.properties) node.properties = {}
57-
58-
if (node.properties.className) {
59-
// @ts-expect-error Assume `classname` is `Array<string>`
60-
add(map, node.properties.className)
61-
}
62-
63-
node.properties.className = list
64-
} else {
65-
conditionals.unshift(node)
66-
}
67-
68-
while (++index < conditionals.length) {
69-
add(map, conditionals[index])
70-
}
71-
72-
/** @type {string} */
73-
let key
74-
75-
for (key in map) {
76-
if (map[key]) list.push(key)
77-
}
78-
79-
return isNode(node) ? node : list
80-
}
81-
)
82-
83-
/**
84-
* @param {ClassMap} result
85-
* @param {Conditional} conditional
86-
*/
87-
function add(result, conditional) {
88-
let index = -1
89-
/** @type {string} */
90-
let key
91-
/** @type {Array<string>} */
92-
let list
93-
94-
if (typeof conditional === 'number') {
95-
result[conditional] = true
96-
} else if (typeof conditional === 'string') {
97-
list = parse(conditional)
98-
99-
while (++index < list.length) {
100-
result[list[index]] = true
101-
}
102-
} else if (conditional && typeof conditional === 'object') {
103-
if (Array.isArray(conditional)) {
104-
while (++index < conditional.length) {
105-
add(result, conditional[index])
106-
}
107-
} else {
108-
for (key in conditional) {
109-
if (own.call(conditional, key)) {
110-
result[key] = conditional[key]
111-
}
112-
}
113-
}
114-
}
115-
}
116-
117-
/**
118-
* @param {Node | Conditional} [value]
119-
* @returns {value is Node}
120-
*/
121-
function isNode(value) {
122-
return Boolean(value && typeof value === 'object' && 'type' in value)
123-
}
7+
export {classnames} from './lib/index.js'

lib/index.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* @typedef {import('hast').Root} Root
3+
* @typedef {import('hast').Content} Content
4+
*/
5+
6+
/**
7+
* @typedef {Root | Content} Node
8+
* @typedef {string | number} ConditionalPrimitive
9+
* Basic class names.
10+
* @typedef {Record<string, boolean>} ConditionalMap
11+
* Map of class names as keys, with whether they’re turned on or not as
12+
* values.
13+
* @typedef {null | undefined | ConditionalPrimitive | ConditionalMap | Array<ConditionalPrimitive | ConditionalMap | Array<ConditionalPrimitive | ConditionalMap>>} Conditional
14+
* Different ways to turn class names on or off.
15+
* @typedef {Record<string, boolean>} ClassMap
16+
* Map of class names as keys, with whether they’re turned on or not as
17+
* values.
18+
*/
19+
20+
import {parse} from 'space-separated-tokens'
21+
22+
const own = {}.hasOwnProperty
23+
24+
/**
25+
* Merge classes.
26+
*
27+
* @param node
28+
* Optionally, node whose classes to append to.
29+
* @param conditionals
30+
* Class configuration to merge.
31+
* @returns
32+
* The given node, if any, or a list of strings.
33+
*/
34+
export const classnames =
35+
/**
36+
* @type {(
37+
* (<T extends Node>(node: T, ...conditions: Array<Conditional>) => T) &
38+
* ((...conditions: Array<Conditional>) => Array<string>)
39+
* )}
40+
*/
41+
(
42+
/**
43+
* @param {Node | Conditional | null | undefined} [node]
44+
* @param {Array<Conditional>} conditionals
45+
*/
46+
function (node, ...conditionals) {
47+
let index = -1
48+
/** @type {ClassMap} */
49+
const map = Object.create(null)
50+
/** @type {Array<string>} */
51+
const list = []
52+
53+
if (isNode(node)) {
54+
if (node.type !== 'element') throw new Error('Expected element node')
55+
56+
if (!node.properties) node.properties = {}
57+
58+
if (node.properties.className) {
59+
// @ts-expect-error Assume `classname` is `Array<string>`
60+
add(map, node.properties.className)
61+
}
62+
63+
node.properties.className = list
64+
} else {
65+
conditionals.unshift(node)
66+
}
67+
68+
while (++index < conditionals.length) {
69+
add(map, conditionals[index])
70+
}
71+
72+
/** @type {string} */
73+
let key
74+
75+
for (key in map) {
76+
if (map[key]) list.push(key)
77+
}
78+
79+
return isNode(node) ? node : list
80+
}
81+
)
82+
83+
/**
84+
* @param {ClassMap} result
85+
* @param {Conditional} conditional
86+
*/
87+
function add(result, conditional) {
88+
let index = -1
89+
/** @type {string} */
90+
let key
91+
/** @type {Array<string>} */
92+
let list
93+
94+
if (typeof conditional === 'number') {
95+
result[conditional] = true
96+
} else if (typeof conditional === 'string') {
97+
list = parse(conditional)
98+
99+
while (++index < list.length) {
100+
result[list[index]] = true
101+
}
102+
} else if (conditional && typeof conditional === 'object') {
103+
if (Array.isArray(conditional)) {
104+
while (++index < conditional.length) {
105+
add(result, conditional[index])
106+
}
107+
} else {
108+
for (key in conditional) {
109+
if (own.call(conditional, key)) {
110+
result[key] = conditional[key]
111+
}
112+
}
113+
}
114+
}
115+
}
116+
117+
/**
118+
* @param {Node | Conditional} [value]
119+
* @returns {value is Node}
120+
*/
121+
function isNode(value) {
122+
return Boolean(value && typeof value === 'object' && 'type' in value)
123+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"main": "index.js",
3030
"types": "index.d.ts",
3131
"files": [
32+
"lib/",
3233
"index.d.ts",
3334
"index.js"
3435
],

0 commit comments

Comments
 (0)