Skip to content

Commit

Permalink
More tests, use fns from utils and rework core (#191)
Browse files Browse the repository at this point in the history
* Add more tests and use functions from utils

* Update snapshots

* Add test for using css variables

* Remove test for sheet.rules()

* Remove getEmotionStylesFromClassName

* Prettier

* Use a smaller style hyphenator

* Don't filter toString since it's not enumerable

* Hoist regex

* Use parentSelectorRegex in another place

* Inline process.env.NODE_ENV === 'development' check so it's removed in production

* Convert StyleSheet to ES class

* Update jest-glamor-react

* Add assign util fn

* Fix some stuff
  • Loading branch information
emmatown authored and Kye Hohenberger committed Jul 28, 2017
1 parent 477c715 commit e4e3438
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 111 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"enzyme-to-json": "^1.5.1",
"jest": "^20.0.4",
"jest-cli": "^20.0.4",
"jest-glamor-react": "^3.0.0",
"jest-glamor-react": "^3.1.0",
"npm-run-all": "^4.0.2",
"polished": "^1.2.1",
"prettier-eslint-cli": "^4.0.3",
Expand Down
2 changes: 1 addition & 1 deletion src/glamor/CSSPropertyOperations/CSSProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ let prefixes = ['Webkit', 'ms', 'Moz', 'O']

// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
// infinite loop, because it iterates over the newly added props too.
keys(isUnitlessNumber).forEach(function (prop) {
forEach(keys(isUnitlessNumber), function (prop) {
forEach(prefixes, function (prefix) {
isUnitlessNumber[prefixKey(prefix, prop)] = 1
})
Expand Down
7 changes: 5 additions & 2 deletions src/glamor/CSSPropertyOperations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
*/

import dangerousStyleValue from './dangerousStyleValue'
import hyphenateStyleName from 'fbjs/lib/hyphenateStyleName'
import memoizeStringOnly from 'fbjs/lib/memoizeStringOnly'

export const processStyleName = memoizeStringOnly(hyphenateStyleName)
const hyphenateRegex = /[A-Z]|^ms/g

export const processStyleName = memoizeStringOnly(styleName =>
styleName.replace(hyphenateRegex, '-$&').toLowerCase()
)

if (process.env.NODE_ENV !== 'production') {
const warning = require('fbjs/lib/warning')
Expand Down
2 changes: 1 addition & 1 deletion src/glamor/clean.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function cleanObject (object) {

let acc = {}
let hasFalsy = false
forEach(keys(object), (value) => {
forEach(keys(object), value => {
const filteredValue = clean(value)
if (filteredValue === null || filteredValue !== value) {
hasFalsy = true
Expand Down
118 changes: 54 additions & 64 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
// @flow
import { StyleSheet } from './sheet'
import { forEach, map, reduce } from './utils'
import { forEach, map, reduce, keys, assign } from './utils'
import { hashString as hash, hashObject } from './hash'
import { createMarkupForStyles } from './glamor/CSSPropertyOperations'
import clean from './glamor/clean.js'

const IS_DEV = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV

export const sheet = new StyleSheet()
// 🚀
sheet.inject()

export let inserted: { [string | number]: boolean | void } = {}

type inputVar = string | number

type vars = Array<inputVar>

export function flush () {
sheet.flush()
inserted = {}
Expand Down Expand Up @@ -45,27 +39,15 @@ function _getRegistered (rule) {
return rule
}

// The idea on how to merge object class names come from glamorous
// 💄
// https://github.com/paypal/glamorous/blob/master/src/get-glamor-classname.js
function getEmotionStylesFromClassName (className) {
const id = className.trim().slice('css-'.length)
if (sheet.registered[id]) {
return sheet.registered[id].style
} else {
return []
}
}

function buildStyles (objs) {
let computedClassName = ''
let objectStyles = []

// This needs to be moved into the core
forEach(objs, (cls): void => {
if (typeof cls === 'string') {
if (cls.trim().indexOf('css-') === 0) {
objectStyles.push(getEmotionStylesFromClassName(cls))
const match = emotionClassRegex.exec(cls)
if (match) {
objectStyles.push(ruleCache[match[1]])
} else {
computedClassName && (computedClassName += ' ')
computedClassName += cls
Expand Down Expand Up @@ -118,7 +100,7 @@ export function injectGlobal (
// injectGlobal is flattened by postcss
// we don't support nested selectors on objects
forEach(combined, obj => {
forEach(Object.keys(obj), selector => {
forEach(keys(obj), selector => {
insertRawRule(`${selector} {${createMarkupForStyles(obj[selector])}}`)
})
})
Expand All @@ -131,7 +113,7 @@ export function fontFace (
) {
const combined = reduce(
content ? objs.concat(content.apply(null, vars)) : objs,
(accum, item, i) => Object.assign(accum, item),
(accum, item, i) => assign(accum, item),
{}
)

Expand All @@ -141,7 +123,7 @@ export function fontFace (
function insertKeyframe (spec) {
if (!inserted[spec.id]) {
const inner = map(
Object.keys(spec.keyframes),
keys(spec.keyframes),
kf => `${kf} {${createMarkupForStyles(spec.keyframes[kf])}}`
).join('')

Expand Down Expand Up @@ -181,16 +163,12 @@ type EmotionRule = { [string]: any }

type CSSRuleList = Array<EmotionRule>

type EmotionClassName = {
[string]: any
}

let cachedCss: (rules: CSSRuleList) => EmotionClassName =
let cachedCss: (rules: CSSRuleList) => EmotionRule =
typeof WeakMap !== 'undefined' ? multiIndexCache(_css) : _css

// 🍩
// https://github.com/threepointone/glamor
export function objStyle (...rules: CSSRuleList): EmotionClassName {
export function objStyle (...rules: CSSRuleList): EmotionRule {
rules = clean(rules)
if (!rules) {
return nullrule
Expand All @@ -211,46 +189,52 @@ function _css (rules) {
return toRule(spec)
}

const emotionClassRegex = /css-([a-zA-Z0-9]+)/

// of shape { 'data-css-<id>': '' }
export function isLikeRule (rule: EmotionRule) {
let keys = Object.keys(rule).filter(x => x !== 'toString')
if (keys.length !== 1) {
const ruleKeys = keys(rule)
if (ruleKeys.length !== 1) {
return false
}
return !!/css-([a-zA-Z0-9]+)/.exec(keys[0])
return !!emotionClassRegex.exec(ruleKeys[0])
}

// extracts id from a { 'css-<id>': ''} like object
export function idFor (rule: EmotionRule) {
let keys = Object.keys(rule).filter(x => x !== 'toString')
if (keys.length !== 1) throw new Error('not a rule')
let regex = /css-([a-zA-Z0-9]+)/
let match = regex.exec(keys[0])
const ruleKeys = keys(rule)
if (ruleKeys.length !== 1) throw new Error('not a rule')
let match = emotionClassRegex.exec(ruleKeys[0])
if (!match) throw new Error('not a rule')
return match[1]
}

const parentSelectorRegex = /&/gm

function selector (id: string, path: string = '') {
if (!id) {
return path.replace(/&/g, '')
return path.replace(parentSelectorRegex, '')
}
if (!path) return `.css-${id}`

let x = path
.split(',')
.map(
x =>
x.indexOf('&') >= 0 ? x.replace(/&/gm, `.css-${id}`) : `.css-${id}${x}`
)
.join(',')
let x = map(
path.split(','),
x =>
x.indexOf('&') >= 0
? x.replace(parentSelectorRegex, `.css-${id}`)
: `.css-${id}${x}`
).join(',')

return x
}

function deconstruct (style) {
// we can be sure it's not infinitely nested here
let plain, selects, medias, supports
Object.keys(style).forEach(key => {
let plain
let selects
let medias
let supports
forEach(keys(style), key => {
if (key.indexOf('&') >= 0) {
selects = selects || {}
selects[key] = style[key]
Expand All @@ -276,17 +260,17 @@ function deconstructedStyleToCSS (id, style) {
css.push(`${selector(id)}{${createMarkupForStyles(plain)}}`)
}
if (selects) {
Object.keys(selects).forEach((key: string) =>
forEach(keys(selects), (key: string) =>
css.push(`${selector(id, key)}{${createMarkupForStyles(selects[key])}}`)
)
}
if (medias) {
Object.keys(medias).forEach(key =>
forEach(keys(medias), key =>
css.push(`${key}{${deconstructedStyleToCSS(id, medias[key]).join('')}}`)
)
}
if (supports) {
Object.keys(supports).forEach(key =>
forEach(keys(supports), key =>
css.push(`${key}{${deconstructedStyleToCSS(id, supports[key]).join('')}}`)
)
}
Expand All @@ -298,7 +282,7 @@ function insert (spec) {
if (!inserted[spec.id]) {
inserted[spec.id] = true
let deconstructed = deconstruct(spec.style)
deconstructedStyleToCSS(spec.id, deconstructed).map(cssRule =>
map(deconstructedStyleToCSS(spec.id, deconstructed), cssRule =>
sheet.insert(cssRule)
)
}
Expand Down Expand Up @@ -342,9 +326,11 @@ function joinSelectors (a, b) {
let as = map(a.split(','), a => (!(a.indexOf('&') >= 0) ? '&' + a : a))
let bs = map(b.split(','), b => (!(b.indexOf('&') >= 0) ? '&' + b : b))

return bs
.reduce((arr, b) => arr.concat(as.map(a => b.replace(/&/g, a))), [])
.join(',')
return reduce(
bs,
(arr, b) => arr.concat(map(as, a => b.replace(parentSelectorRegex, a))),
[]
).join(',')
}

function joinMediaQueries (a, b) {
Expand All @@ -366,10 +352,11 @@ function joinSupports (a, b) {
// flatten a nested array
function flatten (inArr) {
let arr = []
for (let i = 0; i < inArr.length; i++) {
if (Array.isArray(inArr[i])) arr = arr.concat(flatten(inArr[i]))
else arr = arr.concat(inArr[i])
}
forEach(inArr, val => {
if (Array.isArray(val)) arr = arr.concat(flatten(val))
else arr = arr.concat(val)
})

return arr
}

Expand All @@ -380,7 +367,7 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) {
}
src = flatten(src)

src.forEach(_src => {
forEach(src, _src => {
if (isLikeRule(_src)) {
let reg = _getRegistered(_src)
if (reg.type !== 'css') {
Expand All @@ -392,7 +379,7 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) {
if (_src && _src.composes) {
build(dest, { selector, mq, supp, src: _src.composes })
}
Object.keys(_src || {}).forEach(key => {
forEach(keys(_src || {}), key => {
if (isSelector(key)) {
build(dest, {
selector: joinSelectors(selector, key),
Expand Down Expand Up @@ -478,16 +465,19 @@ function multiIndexCache (fn) {
}
let value = fn(args)
if (inputCaches[args.length]) {
let ctr = 0,
coi = inputCaches[args.length]
let ctr = 0
let coi = inputCaches[args.length]
while (ctr < args.length - 1) {
coi = coi.get(args[ctr])
ctr++
}
try {
coi.set(args[ctr], value)
} catch (err) {
if (IS_DEV && !warnedWeakMapError) {
if (
(process.env.NODE_ENV === 'development' || !process.env.NODE_ENV) &&
!warnedWeakMapError
) {
warnedWeakMapError = true
console.warn('failed setting the WeakMap cache for args:', ...args) // eslint-disable-line no-console
console.warn(
Expand Down
11 changes: 2 additions & 9 deletions src/parser.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// @flow
import parse from 'postcss-safe-parser'
import postcssNested from 'postcss-nested'
import stringify from 'postcss/lib/stringify'
import postcssJs from 'postcss-js'
import objParse from './obj-parse'
import objParse from 'postcss-js/parser'
import autoprefixer from 'autoprefixer'
import { processStyleName } from './glamor/CSSPropertyOperations'
import { objStyle } from './index'
Expand Down Expand Up @@ -71,13 +70,7 @@ export function parseCSS (
}

function stringifyCSSRoot (root) {
return root.nodes.map((node, i) => {
let str = ''
stringify(node, x => {
str += x
})
return str
})
return root.nodes.map(node => node.toString())
}

export function expandCSSFallbacks (style: { [string]: any }) {
Expand Down
12 changes: 5 additions & 7 deletions src/react/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component, createElement as h } from 'react'
import PropTypes from 'prop-types'
import { css } from '../index'
import { map, omit, reduce } from '../utils'
import { map, omit, reduce, assign } from '../utils'
import { CHANNEL } from './constants'

export {
Expand Down Expand Up @@ -50,10 +50,9 @@ export default function (tag, objs, vars = [], content) {

render () {
const { props, state, context } = this
const mergedProps = {
...props,
const mergedProps = assign({}, props, {
theme: state.theme
}
})

const getValue = v => {
if (v && typeof v === 'function') {
Expand Down Expand Up @@ -102,11 +101,10 @@ export default function (tag, objs, vars = [], content) {
return h(
tag,
omit(
{
...mergedProps,
assign({}, mergedProps, {
ref: mergedProps.innerRef,
className
},
}),
['innerRef', 'theme']
)
)
Expand Down
Loading

0 comments on commit e4e3438

Please sign in to comment.