Skip to content

Commit 0b7caa1

Browse files
committed
Add prototype monolithic mode
1 parent c7a38b2 commit 0b7caa1

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

monolithic.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./dist/monolithic')

src/monolithic.js

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
let cache = {}
2+
let prefix = '_cxs'
3+
const cssRules = []
4+
let insert = rule => cssRules.push(rule)
5+
const hyph = s => s.replace(/[A-Z]|^ms/g, '-$&').toLowerCase()
6+
const mx = (rule, media) => media ? `${media}{${rule}}` : rule
7+
const rx = (cn, prop, val) => `.${cn}{${hyph(prop)}:${val}}`
8+
const noAnd = s => s.replace(/&/g, '')
9+
10+
const isMedia = key => /^@/.test(key)
11+
const createDeclaration = (key, value) => hyph(key) + ':' + value
12+
const createRule = ({
13+
className,
14+
child,
15+
media,
16+
declarations
17+
}) => mx(`.${className + child}{${declarations.join(';')}}`, media)
18+
19+
const parseRules = (obj, child = '', media) => {
20+
const rules = []
21+
const declarations = []
22+
23+
for (let key in obj) {
24+
const value = obj[key]
25+
26+
if (value === null) continue
27+
28+
if (typeof value === 'object') {
29+
const _media = isMedia(key) ? key : null
30+
const _child = _media ? child : child + noAnd(key)
31+
parseRules(value, _child, _media)
32+
.forEach(r => rules.push(r))
33+
continue
34+
}
35+
36+
const dec = createDeclaration(key, value)
37+
declarations.push(dec)
38+
}
39+
40+
rules.unshift({
41+
media,
42+
child,
43+
declarations
44+
})
45+
46+
return rules
47+
}
48+
49+
const parse = obj => {
50+
const rules = parseRules(obj)
51+
const classNames = []
52+
53+
rules.forEach(rule => {
54+
const cacheKey = JSON.stringify(rule)
55+
if (cache[cacheKey]) {
56+
classNames.push(cache[cacheKey])
57+
return
58+
}
59+
const className = prefix + cssRules.length.toString(36)
60+
classNames.push(className)
61+
const ruleset = createRule(Object.assign(rule, { className }))
62+
insert(ruleset)
63+
cache[cacheKey] = className
64+
})
65+
66+
return classNames.join(' ')
67+
}
68+
69+
module.exports = (...styles) =>
70+
styles.map(style => parse(style))
71+
.join(' ').trim()
72+
73+
module.exports.css = () => cssRules.sort().join('')
74+
75+
module.exports.reset = () => {
76+
cache = {}
77+
while (cssRules.length) cssRules.pop()
78+
}
79+
80+
module.exports.prefix = val => prefix = val
81+
82+
if (typeof document !== 'undefined') {
83+
const sheet = document.head.appendChild(
84+
document.createElement('style')
85+
).sheet
86+
insert = rule => {
87+
cssRules.push(rule)
88+
sheet.insertRule(rule, sheet.cssRules.length)
89+
}
90+
}

test/monolithic.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import test from 'ava'
2+
import cxs from '../src/monolithic'
3+
4+
test.afterEach.always(() => {
5+
cxs.reset()
6+
})
7+
8+
test('returns a monolithic className', t => {
9+
const cn = cxs({
10+
color: 'tomato',
11+
fontSize: '48px',
12+
'&:hover': {
13+
color: 'red',
14+
textDecoration: 'underline'
15+
},
16+
'@media screen': {
17+
fontSize: '64px',
18+
lineHeight: 1,
19+
'&:active': {
20+
outline: '4px solid lime',
21+
fontWeight: 'bold'
22+
}
23+
}
24+
})
25+
26+
const css = cxs.css()
27+
t.is(typeof cn, 'string')
28+
t.is(typeof css, 'string')
29+
})

0 commit comments

Comments
 (0)