From 143c4a4b7b7dd5142e457a441c9753dfe92c61bf Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Wed, 12 Jul 2017 11:19:14 -0600 Subject: [PATCH 01/57] Initial working version of using glamor core instead of cxs --- package.json | 1 + .../CSSPropertyOperations/CSSProperty.js | 146 ++++++ .../dangerousStyleValue.js | 92 ++++ src/glamor/CSSPropertyOperations/index.js | 170 +++++++ src/glamor/clean.js | 50 ++ src/index.js | 473 +++++++++++++++--- test/__snapshots__/css.test.js.snap | 14 +- test/__snapshots__/react.test.js.snap | 6 +- test/react.test.js | 6 +- 9 files changed, 864 insertions(+), 94 deletions(-) create mode 100644 src/glamor/CSSPropertyOperations/CSSProperty.js create mode 100644 src/glamor/CSSPropertyOperations/dangerousStyleValue.js create mode 100644 src/glamor/CSSPropertyOperations/index.js create mode 100644 src/glamor/clean.js diff --git a/package.json b/package.json index d58403b20..f2fa0e61c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@arr/foreach": "^0.1.0", "@arr/map": "^0.1.0", "babel-plugin-syntax-jsx": "^6.18.0", + "fbjs": "^0.8.12", "styled-components": "2.0.0", "theming": "^1.0.1", "touch": "^1.0.0" diff --git a/src/glamor/CSSPropertyOperations/CSSProperty.js b/src/glamor/CSSPropertyOperations/CSSProperty.js new file mode 100644 index 000000000..642a839d4 --- /dev/null +++ b/src/glamor/CSSPropertyOperations/CSSProperty.js @@ -0,0 +1,146 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSProperty + */ + +/** + * CSS properties which accept numbers but are not in units of "px". + */ + +let isUnitlessNumber = { + animationIterationCount: true, + borderImageOutset: true, + borderImageSlice: true, + borderImageWidth: true, + boxFlex: true, + boxFlexGroup: true, + boxOrdinalGroup: true, + columnCount: true, + flex: true, + flexGrow: true, + flexPositive: true, + flexShrink: true, + flexNegative: true, + flexOrder: true, + gridRow: true, + gridColumn: true, + fontWeight: true, + lineClamp: true, + lineHeight: true, + opacity: true, + order: true, + orphans: true, + tabSize: true, + widows: true, + zIndex: true, + zoom: true, + + // SVG-related properties + fillOpacity: true, + floodOpacity: true, + stopOpacity: true, + strokeDasharray: true, + strokeDashoffset: true, + strokeMiterlimit: true, + strokeOpacity: true, + strokeWidth: true +} + +/** + * @param {string} prefix vendor-specific prefix, eg: Webkit + * @param {string} key style name, eg: transitionDuration + * @return {string} style name prefixed with `prefix`, properly camelCased, eg: + * WebkitTransitionDuration + */ +function prefixKey (prefix, key) { + return prefix + key.charAt(0).toUpperCase() + key.substring(1) +} + +/** + * Support style names that may come passed in prefixed by adding permutations + * of vendor prefixes. + */ +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. +Object.keys(isUnitlessNumber).forEach(function (prop) { + prefixes.forEach(function (prefix) { + isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop] + }) +}) + +/** + * Most style properties can be unset by doing .style[prop] = '' but IE8 + * doesn't like doing that with shorthand properties so for the properties that + * IE8 breaks on, which are listed here, we instead unset each of the + * individual properties. See http://bugs.jquery.com/ticket/12385. + * The 4-value 'clock' properties like margin, padding, border-width seem to + * behave without any problems. Curiously, list-style works too without any + * special prodding. + */ +let shorthandPropertyExpansions = { + background: { + backgroundAttachment: true, + backgroundColor: true, + backgroundImage: true, + backgroundPositionX: true, + backgroundPositionY: true, + backgroundRepeat: true + }, + backgroundPosition: { + backgroundPositionX: true, + backgroundPositionY: true + }, + border: { + borderWidth: true, + borderStyle: true, + borderColor: true + }, + borderBottom: { + borderBottomWidth: true, + borderBottomStyle: true, + borderBottomColor: true + }, + borderLeft: { + borderLeftWidth: true, + borderLeftStyle: true, + borderLeftColor: true + }, + borderRight: { + borderRightWidth: true, + borderRightStyle: true, + borderRightColor: true + }, + borderTop: { + borderTopWidth: true, + borderTopStyle: true, + borderTopColor: true + }, + font: { + fontStyle: true, + fontVariant: true, + fontWeight: true, + fontSize: true, + lineHeight: true, + fontFamily: true + }, + outline: { + outlineWidth: true, + outlineStyle: true, + outlineColor: true + } +} + +const CSSProperty = { + isUnitlessNumber: isUnitlessNumber, + shorthandPropertyExpansions: shorthandPropertyExpansions +} + +export default CSSProperty diff --git a/src/glamor/CSSPropertyOperations/dangerousStyleValue.js b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js new file mode 100644 index 000000000..69dd058be --- /dev/null +++ b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js @@ -0,0 +1,92 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule dangerousStyleValue + */ + +import CSSProperty from './CSSProperty' +import warning from 'fbjs/lib/warning' + +let isUnitlessNumber = CSSProperty.isUnitlessNumber +let styleWarnings = {} + +/** + * Convert a value into the proper css writable value. The style name `name` + * should be logical (no hyphens), as specified + * in `CSSProperty.isUnitlessNumber`. + * + * @param {string} name CSS property name such as `topMargin`. + * @param {*} value CSS property value such as `10px`. + * @param {ReactDOMComponent} component + * @return {string} Normalized style value with dimensions applied. + */ +function dangerousStyleValue (name, value, component) { + // Note that we've removed escapeTextForBrowser() calls here since the + // whole string will be escaped when the attribute is injected into + // the markup. If you provide unsafe user data here they can inject + // arbitrary CSS which may be problematic (I couldn't repro this): + // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet + // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/ + // This is not an XSS hole but instead a potential CSS injection issue + // which has lead to a greater discussion about how we're going to + // trust URLs moving forward. See #2115901 + + let isEmpty = value == null || typeof value === 'boolean' || value === '' + if (isEmpty) { + return '' + } + + let isNonNumeric = isNaN(value) + if ( + isNonNumeric || + value === 0 || + (isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name]) + ) { + return '' + value // cast to string + } + + if (typeof value === 'string') { + if (process.env.NODE_ENV !== 'production') { + // Allow '0' to pass through without warning. 0 is already special and + // doesn't require units, so we don't need to warn about it. + if (component && value !== '0') { + let owner = component._currentElement._owner + let ownerName = owner ? owner.getName() : null + if (ownerName && !styleWarnings[ownerName]) { + styleWarnings[ownerName] = {} + } + let warned = false + if (ownerName) { + let warnings = styleWarnings[ownerName] + warned = warnings[name] + if (!warned) { + warnings[name] = true + } + } + if (!warned) { + process.env.NODE_ENV !== 'production' + ? warning( + false, + 'a `%s` tag (owner: `%s`) was passed a numeric string value ' + + 'for CSS property `%s` (value: `%s`) which will be treated ' + + 'as a unitless number in a future version of React.', + component._currentElement.type, + ownerName || 'unknown', + name, + value + ) + : void 0 + } + } + } + value = value.trim() + } + return value + 'px' +} + +export default dangerousStyleValue diff --git a/src/glamor/CSSPropertyOperations/index.js b/src/glamor/CSSPropertyOperations/index.js new file mode 100644 index 000000000..760102827 --- /dev/null +++ b/src/glamor/CSSPropertyOperations/index.js @@ -0,0 +1,170 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSPropertyOperations + */ + +import camelizeStyleName from 'fbjs/lib/camelizeStyleName' +import dangerousStyleValue from './dangerousStyleValue' +import hyphenateStyleName from 'fbjs/lib/hyphenateStyleName' +import memoizeStringOnly from 'fbjs/lib/memoizeStringOnly' +import warning from 'fbjs/lib/warning' + +export const processStyleName = memoizeStringOnly(hyphenateStyleName) + +if (process.env.NODE_ENV !== 'production') { + // 'msTransform' is correct, but the other prefixes should be capitalized + let badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/ + + // style values shouldn't contain a semicolon + let badStyleValueWithSemicolonPattern = /;\s*$/ + + let warnedStyleNames = {} + let warnedStyleValues = {} + let warnedForNaNValue = false + + let warnHyphenatedStyleName = function (name, owner) { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return + } + + warnedStyleNames[name] = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + 'Unsupported style property %s. Did you mean %s?%s', + name, + camelizeStyleName(name), + checkRenderMessage(owner) + ) + : void 0 + } + + let warnBadVendoredStyleName = function (name, owner) { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return + } + + warnedStyleNames[name] = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + 'Unsupported vendor-prefixed style property %s. Did you mean %s?%s', + name, + name.charAt(0).toUpperCase() + name.slice(1), + checkRenderMessage(owner) + ) + : void 0 + } + + let warnStyleValueWithSemicolon = function (name, value, owner) { + if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) { + return + } + + warnedStyleValues[value] = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + "Style property values shouldn't contain a semicolon.%s " + + 'Try "%s: %s" instead.', + checkRenderMessage(owner), + name, + value.replace(badStyleValueWithSemicolonPattern, '') + ) + : void 0 + } + + let warnStyleValueIsNaN = function (name, value, owner) { + if (warnedForNaNValue) { + return + } + + warnedForNaNValue = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + '`NaN` is an invalid value for the `%s` css style property.%s', + name, + checkRenderMessage(owner) + ) + : void 0 + } + + let checkRenderMessage = function (owner) { + if (owner) { + let name = owner.getName() + if (name) { + return ' Check the render method of `' + name + '`.' + } + } + return '' + } + + /** + * @param {string} name + * @param {*} value + * @param {ReactDOMComponent} component + */ + var warnValidStyle = function (name, value, component) { + // eslint-disable-line no-var + let owner + if (component) { + owner = component._currentElement._owner + } + if (name.indexOf('-') > -1) { + warnHyphenatedStyleName(name, owner) + } else if (badVendoredStyleNamePattern.test(name)) { + warnBadVendoredStyleName(name, owner) + } else if (badStyleValueWithSemicolonPattern.test(value)) { + warnStyleValueWithSemicolon(name, value, owner) + } + + if (typeof value === 'number' && isNaN(value)) { + warnStyleValueIsNaN(name, value, owner) + } + } +} + +/** + * Serializes a mapping of style properties for use as inline styles: + * + * > createMarkupForStyles({width: '200px', height: 0}) + * "width:200px;height:0;" + * + * Undefined values are ignored so that declarative programming is easier. + * The result should be HTML-escaped before insertion into the DOM. + * + * @param {object} styles + * @param {ReactDOMComponent} component + * @return {?string} + */ + +export function createMarkupForStyles (styles, component) { + let serialized = '' + for (let styleName in styles) { + const isCustomProp = styleName.indexOf('--') === 0 + if (!styles.hasOwnProperty(styleName)) { + continue + } + let styleValue = styles[styleName] + if (process.env.NODE_ENV !== 'production' && !isCustomProp) { + warnValidStyle(styleName, styleValue, component) + } + if (styleValue != null) { + if (isCustomProp) { + serialized += `${styleName}:${styleValue};` + } else { + serialized += processStyleName(styleName) + ':' + serialized += + dangerousStyleValue(styleName, styleValue, component) + ';' + } + } + } + return serialized || null +} diff --git a/src/glamor/clean.js b/src/glamor/clean.js new file mode 100644 index 000000000..d20052de2 --- /dev/null +++ b/src/glamor/clean.js @@ -0,0 +1,50 @@ +// Returns true for null, false, undefined and {} +function isFalsy (value) { + return ( + value === null || + value === undefined || + value === false || + (typeof value === 'object' && Object.keys(value).length === 0) + ) +} + +function cleanObject (object) { + if (isFalsy(object)) return null + if (typeof object !== 'object') return object + + let acc = {}, + keys = Object.keys(object), + hasFalsy = false + for (let i = 0; i < keys.length; i++) { + let value = object[keys[i]] + const filteredValue = clean(value) + if (filteredValue === null || filteredValue !== value) { + hasFalsy = true + } + if (filteredValue !== null) { + acc[keys[i]] = filteredValue + } + } + return Object.keys(acc).length === 0 ? null : hasFalsy ? acc : object +} + +function cleanArray (rules) { + let hasFalsy = false + const filtered = [] + rules.forEach(rule => { + const filteredRule = clean(rule) + if (filteredRule === null || filteredRule !== rule) { + hasFalsy = true + } + if (filteredRule !== null) { + filtered.push(filteredRule) + } + }) + return filtered.length == 0 ? null : hasFalsy ? filtered : rules +} + +// Takes style array or object provided by user and clears all the falsy data +// If there is no styles left after filtration returns null +export default function clean (input) { + return Array.isArray(input) ? cleanArray(input) : cleanObject(input) +} diff --git a/src/index.js b/src/index.js index 8369f7677..d73041426 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,12 @@ // @flow import forEach from '@arr/foreach' import { StyleSheet } from './sheet' -import { hashArray, hashObject } from './hash' +import { hashString as hash, hashArray, hashObject } from './hash' +import { createMarkupForStyles } from './glamor/CSSPropertyOperations' +import clean from './glamor/clean.js' export const sheet = new StyleSheet() +// 🚀 sheet.inject() export let inserted: { [string]: boolean | void } = {} @@ -41,9 +44,11 @@ export function css (classes: string[], vars: vars, content: () => string[]) { } let computedClassName = '' - forEach(classes, (cls): string => { + forEach(classes, (cls): void => { computedClassName && (computedClassName += ' ') - computedClassName += typeof cls === 'string' ? cls : objStyle(cls) + computedClassName += typeof cls === 'string' + ? cls + : objStyle(cls).toString() }) if (content) { @@ -93,94 +98,400 @@ export function hydrate (ids: string[]) { // 🍩 // https://github.com/jxnblk/cxs/blob/master/src/monolithic/index.js -export function objStyle (style: { [string]: any }) { - const hash = hashObject(style) - const className = `css-${hash}` - const selector = '.' + className +// export function objStyle (style: { [string]: any }) { +// const hash = hashObject(style) +// const className = `css-${hash}` +// const selector = '.' + className +// +// if (inserted[hash]) return className +// +// const rules = deconstruct(selector, style) +// forEach(rules, rule => sheet.insert(rule)) +// +// inserted[hash] = true +// +// return className +// } - if (inserted[hash]) return className +type GlamorRule = { [string]: any } - const rules = deconstruct(selector, style) - forEach(rules, rule => sheet.insert(rule)) +type CSSRuleList = Array - inserted[hash] = true +type GlamorClassName = { + [string]: any +} + +let cachedCss: (rules: CSSRuleList) => GlamorClassName = typeof WeakMap !== + 'undefined' + ? multiIndexCache(_css) + : _css + +export function objStyle (...rules: CSSRuleList): GlamorClassName { + rules = clean(rules) + if (!rules) { + return nullrule + } + + return cachedCss(rules) +} + +function _css (rules) { + let style = {} + build(style, { src: rules }) // mutative! but worth it. + + let spec = { + id: hashObject(style), + style, + type: 'css' + } + return toRule(spec) +} - return className +// define some constants + +const isDev = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV +const isTest = process.env.NODE_ENV === 'test' + +// takes a string, converts to lowercase, strips out nonalphanumeric. +function simple (str) { + return str.toLowerCase().replace(/[^a-z0-9]/g, '') } -function deconstruct (selector, styles, media) { - const decs = [] - const rules = [] +// of shape { 'data-css-': '' } +export function isLikeRule (rule: GlamorRule) { + let keys = Object.keys(rule).filter(x => x !== 'toString') + if (keys.length !== 1) { + return false + } + return !!/css\-([a-zA-Z0-9]+)/.exec(keys[0]) +} - for (let key in styles) { - const value = styles[key] - const type = typeof value +// extracts id from a { 'css-': ''} like object +export function idFor (rule: GlamorRule) { + 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]) + if (!match) throw new Error('not a rule') + return match[1] +} - if (type === 'number' || type === 'string') { - decs.push(createDec(key, value)) - } else if (Array.isArray(value)) { - forEach(value, val => decs.push(createDec(key, val))) - } else if (key.charCodeAt(0) === 58) { - forEach(deconstruct(selector + key, value, media), r => rules.push(r)) - } else if (key.indexOf('@media') !== -1) { - forEach(deconstruct(selector, value, key), r => rules.push(r)) +function selector (id: string, path: string = '') { + if (!id) { + return path.replace(/\&/g, '') + } + if (!path) return `.css-${id}` + + let x = path + .split(',') + .map( + x => + x.indexOf('&') >= 0 ? x.replace(/\&/gm, `.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 => { + if (key.indexOf('&') >= 0) { + selects = selects || {} + selects[key] = style[key] + } else if (key.indexOf('@media') === 0) { + medias = medias || {} + medias[key] = deconstruct(style[key]) + } else if (key.indexOf('@supports') === 0) { + supports = supports || {} + supports[key] = deconstruct(style[key]) } else { - forEach(deconstruct(selector + ' ' + key, value, media), r => rules.push(r)) + plain = plain || {} + plain[key] = style[key] + } + }) + return { plain, selects, medias, supports } +} + +function deconstructedStyleToCSS (id, style) { + let css = [] + + // plugins here + let { plain, selects, medias, supports } = style + if (plain) { + css.push(`${selector(id)}{${createMarkupForStyles(plain)}}`) + } + if (selects) { + Object.keys(selects).forEach((key: string) => + css.push(`${selector(id, key)}{${createMarkupForStyles(selects[key])}}`) + ) + } + if (medias) { + Object.keys(medias).forEach(key => + css.push(`${key}{${deconstructedStyleToCSS(id, medias[key]).join('')}}`) + ) + } + if (supports) { + Object.keys(supports).forEach(key => + css.push(`${key}{${deconstructedStyleToCSS(id, supports[key]).join('')}}`) + ) + } + return css +} + +// and helpers to insert rules into said sheet +function insert (spec) { + if (!inserted[spec.id]) { + inserted[spec.id] = true + let deconstructed = deconstruct(spec.style) + deconstructedStyleToCSS(spec.id, deconstructed).map(cssRule => + sheet.insert(cssRule) + ) + } +} + +// a simple cache to store generated rules +let registered = (sheet.registered = {}) + +function register (spec) { + if (!registered[spec.id]) { + registered[spec.id] = spec + } +} + +function _getRegistered (rule) { + if (isLikeRule(rule)) { + let ret = registered[idFor(rule)] + if (ret == null) { + throw new Error( + '[emotion] an unexpected rule cache miss occurred. This is probably a sign of multiple glamor instances in your app. See https://github.com/threepointone/glamor/issues/79' + ) } + return ret + } + return rule +} + +// todo - perf +let ruleCache = {} + +function toRule (spec) { + register(spec) + insert(spec) + + if (ruleCache[spec.id]) { + return ruleCache[spec.id] } - rules.unshift(createRule(selector, decs, media)) - - return rules -} - -function createDec (key, value) { - const prop = hyphenate(key) - const val = addPx(key, value) - return prop + ':' + val -} - -function createRule (selector, decs, media) { - const rule = `${selector}{${decs.join(';')}}` - return media ? `${media}{${rule}}` : rule -} - -function hyphenate (str) { - return ('' + str).replace(/[A-Z]|^ms/g, '-$&').toLowerCase() -} - -function addPx (prop, value) { - if (typeof value !== 'number' || unitlessProps[prop] !== undefined) return value - return value + 'px' -} - -const unitlessProps = { - animationIterationCount: 1, - boxFlex: 1, - boxFlexGroup: 1, - boxOrdinalGroup: 1, - columnCount: 1, - flex: 1, - flexGrow: 1, - flexPositive: 1, - flexShrink: 1, - flexNegative: 1, - flexOrder: 1, - gridRow: 1, - gridColumn: 1, - fontWeight: 1, - lineClamp: 1, - lineHeight: 1, - opacity: 1, - order: 1, - orphans: 1, - tabSize: 1, - widows: 1, - zIndex: 1, - zoom: 1, - fillOpacity: 1, - stopOpacity: 1, - strokeDashoffset: 1, - strokeOpacity: 1, - strokeWidth: 1 + let ret = { [`css-${spec.id}`]: '' } + Object.defineProperty(ret, 'toString', { + enumerable: false, + value () { + return 'css-' + spec.id + } + }) + ruleCache[spec.id] = ret + return ret +} + +function log () { + // eslint-disable-line no-unused-vars + console.log(this) // eslint-disable-line no-console + return this +} + +function isSelector (key) { + let possibles = [':', '.', '[', '>', ' '], + found = false, + ch = key.charAt(0) + for (let i = 0; i < possibles.length; i++) { + if (ch === possibles[i]) { + found = true + break + } + } + return found || key.indexOf('&') >= 0 +} + +function joinSelectors (a, b) { + let as = a.split(',').map(a => (!(a.indexOf('&') >= 0) ? '&' + a : a)) + let bs = b.split(',').map(b => (!(b.indexOf('&') >= 0) ? '&' + b : b)) + + return bs + .reduce((arr, b) => arr.concat(as.map(a => b.replace(/\&/g, a))), []) + .join(',') +} + +function joinMediaQueries (a, b) { + return a ? `@media ${a.substring(6)} and ${b.substring(6)}` : b +} + +function isMediaQuery (key) { + return key.indexOf('@media') === 0 +} + +function isSupports (key) { + return key.indexOf('@supports') === 0 +} + +function joinSupports (a, b) { + return a ? `@supports ${a.substring(9)} and ${b.substring(9)}` : 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]) + } + return arr +} + +// mutable! modifies dest. +function build (dest, { selector = '', mq = '', supp = '', src = {} }) { + if (!Array.isArray(src)) { + src = [src] + } + src = flatten(src) + + src.forEach(_src => { + if (isLikeRule(_src)) { + let reg = _getRegistered(_src) + if (reg.type !== 'css') { + throw new Error('cannot merge this rule') + } + _src = reg.style + } + _src = clean(_src) + if (_src && _src.composes) { + build(dest, { selector, mq, supp, src: _src.composes }) + } + Object.keys(_src || {}).forEach(key => { + if (isSelector(key)) { + if (key === '::placeholder') { + build(dest, { + selector: joinSelectors(selector, '::-webkit-input-placeholder'), + mq, + supp, + src: _src[key] + }) + build(dest, { + selector: joinSelectors(selector, '::-moz-placeholder'), + mq, + supp, + src: _src[key] + }) + build(dest, { + selector: joinSelectors(selector, '::-ms-input-placeholder'), + mq, + supp, + src: _src[key] + }) + } + + build(dest, { + selector: joinSelectors(selector, key), + mq, + supp, + src: _src[key] + }) + } else if (isMediaQuery(key)) { + build(dest, { + selector, + mq: joinMediaQueries(mq, key), + supp, + src: _src[key] + }) + } else if (isSupports(key)) { + build(dest, { + selector, + mq, + supp: joinSupports(supp, key), + src: _src[key] + }) + } else if (key === 'composes') { + // ignore, we already dealth with it + } else { + let _dest = dest + if (supp) { + _dest[supp] = _dest[supp] || {} + _dest = _dest[supp] + } + if (mq) { + _dest[mq] = _dest[mq] || {} + _dest = _dest[mq] + } + if (selector) { + _dest[selector] = _dest[selector] || {} + _dest = _dest[selector] + } + + _dest[key] = _src[key] + } + }) + }) +} + +let nullrule: GlamorClassName = { + // 'data-css-nil': '' +} + +Object.defineProperty(nullrule, 'toString', { + enumerable: false, + value () { + return 'css-nil' + } +}) + +let inputCaches = typeof WeakMap !== 'undefined' + ? [nullrule, new WeakMap(), new WeakMap(), new WeakMap()] + : [nullrule] + +let warnedWeakMapError = false + +function multiIndexCache (fn) { + return function (args) { + if (inputCaches[args.length]) { + let coi = inputCaches[args.length] + let ctr = 0 + while (ctr < args.length - 1) { + if (!coi.has(args[ctr])) { + coi.set(args[ctr], new WeakMap()) + } + coi = coi.get(args[ctr]) + ctr++ + } + if (coi.has(args[args.length - 1])) { + let ret = coi.get(args[ctr]) + + if (registered[ret.toString().substring(4)]) { + // make sure it hasn't been flushed + return ret + } + } + } + let value = fn(args) + if (inputCaches[args.length]) { + let ctr = 0, + coi = inputCaches[args.length] + while (ctr < args.length - 1) { + coi = coi.get(args[ctr]) + ctr++ + } + try { + coi.set(args[ctr], value) + } catch (err) { + if (isDev && !warnedWeakMapError) { + warnedWeakMapError = true + console.warn('failed setting the WeakMap cache for args:', ...args) // eslint-disable-line no-console + console.warn( + 'this should NOT happen, please file a bug on the github repo.' + ) // eslint-disable-line no-console + } + } + } + return value + } } diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index c4a23a783..3380a7d82 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -15,16 +15,16 @@ exports[`css composes 3`] = ` z-index: 100; font-size: 18px; text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { + border: solid 1px red; }.css-cls2-1qb2ovg-1em6aur { -webkit-justify-content: center; -ms-flex-pack: center; -webkit-box-pack: center; justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }" `; -exports[`css composes with objects 1`] = `"css-q8izmm"`; +exports[`css composes with objects 1`] = `"css-1a5gpco"`; -exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-q8izmm"`; +exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-1a5gpco"`; exports[`css composes with objects 3`] = ` ".css-cls1-1gi569l-b877w5 { background: white; @@ -37,14 +37,14 @@ exports[`css composes with objects 3`] = ` z-index: 100; font-size: 18px; text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { + border: solid 1px red; }.css-cls2-1qb2ovg-1em6aur { -webkit-justify-content: center; -ms-flex-pack: center; -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-1fe3owl{display:flex}.css-q8izmm{display:flex;display:block;width:30px;height:calc(40vw - 50px)}.css-q8izmm:hover{color:blue}.css-q8izmm:after{content:\\" \\";color:red}@media(min-width: 420px){.css-q8izmm{color:green}}" + justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-1fe3owl{display:flex;}.css-1a5gpco{display:flex,block;width:30px;height:calc(40vw - 50px);}.css-1a5gpco:hover{color:blue;}.css-1a5gpco:after{content:\\" \\";color:red;}@media(min-width: 420px){.css-1a5gpco{color:green;}}" `; -exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-15famh2"`; +exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-nil"`; exports[`css composes with undefined values 2`] = ` ".css-cls1-1gi569l-b877w5 { background: white; @@ -57,7 +57,7 @@ exports[`css composes with undefined values 2`] = ` z-index: 100; font-size: 18px; text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { + border: solid 1px red; }.css-cls2-1qb2ovg-1em6aur { -webkit-justify-content: center; -ms-flex-pack: center; -webkit-box-pack: center; diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index c1319c413..214287d8a 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -138,7 +138,7 @@ exports[`styled composes with objects 1`] = ` font-size: 32px; } -.css-1olphq9 { +.css-ioza2b { color: #333; font-size: 1.333em; } @@ -152,13 +152,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-1olphq9 { + .css-ioza2b { font-size: 1.4323121856191332em; } }

hello world diff --git a/test/react.test.js b/test/react.test.js index 2ea17f6fe..4b64d8cc3 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -169,9 +169,9 @@ describe('styled', () => { test('composes with objects', () => { const cssA = { color: lighten(0.2, '#000'), - 'font-size': modularScale(1), - [hiDPI(1.5)]: { - 'font-size': modularScale(1.25) + 'fontSize': modularScale(1), + [hiDPI(1.5).trim()]: { + fontSize: modularScale(1.25) } } From f6b179867011926d204f73ff26d3ce93f9af4430 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Wed, 12 Jul 2017 11:22:53 -0600 Subject: [PATCH 02/57] Bump bundle size --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2fa0e61c..2f1d8602e 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "bundlesize": [ { "path": "./dist/DO-NOT-USE.min.js", - "threshold": "3 Kb" + "threshold": "6 Kb" } ] } From 2c0064151f5f73770b21ffb5e6c8f0ec48509b7d Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Wed, 12 Jul 2017 23:02:53 -0600 Subject: [PATCH 03/57] Correctly merge styles. fixes #128 --- src/index.js | 107 +++++++++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/src/index.js b/src/index.js index d73041426..7878155d1 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,8 @@ export const sheet = new StyleSheet() // 🚀 sheet.inject() +window.sheet = sheet + export let inserted: { [string]: boolean | void } = {} type inputVar = string | number @@ -38,19 +40,66 @@ export function flush () { sheet.inject() } +// a simple cache to store generated rules +let registered = (sheet.registered = {}) + +function register (spec) { + if (!registered[spec.id]) { + registered[spec.id] = spec + } +} + +function _getRegistered (rule) { + if (isLikeRule(rule)) { + let ret = registered[idFor(rule)] + if (ret == null) { + throw new Error( + '[emotion] an unexpected rule cache miss occurred. This is probably a sign of multiple glamor instances in your app. See https://github.com/threepointone/glamor/issues/79' + ) + } + return ret + } + 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 getGlamorStylesFromClassName (className) { + const id = className.trim().slice('css-obj-'.length) + if (sheet.registered[id]) { + return sheet.registered[id].style + } else { + return null + } +} + export function css (classes: string[], vars: vars, content: () => string[]) { if (!Array.isArray(classes)) { classes = [classes] } let computedClassName = '' + let objectStyles = [] forEach(classes, (cls): void => { computedClassName && (computedClassName += ' ') - computedClassName += typeof cls === 'string' - ? cls - : objStyle(cls).toString() + + if (typeof cls === 'string') { + if (cls.trim().indexOf('css-obj-') === 0) { + const glamorStylesFromClassName = getGlamorStylesFromClassName(cls) + objectStyles.push(glamorStylesFromClassName) + } else { + computedClassName += cls + } + } else { + objectStyles.push(cls) + } }) + if (objectStyles.length) { + computedClassName += ' ' + objStyle(...objectStyles).toString() + } + if (content) { // inline mode let src = content(...vars) // returns an array @@ -60,7 +109,8 @@ export function css (classes: string[], vars: vars, content: () => string[]) { inserted[hash] = true const rgx = new RegExp(classes[0], 'gm') forEach(src, r => { - sheet.insert(r.replace(rgx, `${classes[0]}-${hash}`)) + const finalRule = r.replace(rgx, `${classes[0]}-${hash}`) + sheet.insert(finalRule) }) } return `${classes[0]}-${hash} ${computedClassName}` @@ -98,21 +148,6 @@ export function hydrate (ids: string[]) { // 🍩 // https://github.com/jxnblk/cxs/blob/master/src/monolithic/index.js -// export function objStyle (style: { [string]: any }) { -// const hash = hashObject(style) -// const className = `css-${hash}` -// const selector = '.' + className -// -// if (inserted[hash]) return className -// -// const rules = deconstruct(selector, style) -// forEach(rules, rule => sheet.insert(rule)) -// -// inserted[hash] = true -// -// return className -// } - type GlamorRule = { [string]: any } type CSSRuleList = Array @@ -163,14 +198,14 @@ export function isLikeRule (rule: GlamorRule) { if (keys.length !== 1) { return false } - return !!/css\-([a-zA-Z0-9]+)/.exec(keys[0]) + return !!/css\-obj\-([a-zA-Z0-9]+)/.exec(keys[0]) } // extracts id from a { 'css-': ''} like object export function idFor (rule: GlamorRule) { 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 regex = /css\-obj\-([a-zA-Z0-9]+)/ let match = regex.exec(keys[0]) if (!match) throw new Error('not a rule') return match[1] @@ -180,13 +215,15 @@ function selector (id: string, path: string = '') { if (!id) { return path.replace(/\&/g, '') } - if (!path) return `.css-${id}` + if (!path) return `.css-obj-${id}` let x = path .split(',') .map( x => - x.indexOf('&') >= 0 ? x.replace(/\&/gm, `.css-${id}`) : `.css-${id}${x}` + x.indexOf('&') >= 0 + ? x.replace(/\&/gm, `.css-obj-${id}`) + : `.css-obj-${id}${x}` ) .join(',') @@ -251,28 +288,6 @@ function insert (spec) { } } -// a simple cache to store generated rules -let registered = (sheet.registered = {}) - -function register (spec) { - if (!registered[spec.id]) { - registered[spec.id] = spec - } -} - -function _getRegistered (rule) { - if (isLikeRule(rule)) { - let ret = registered[idFor(rule)] - if (ret == null) { - throw new Error( - '[emotion] an unexpected rule cache miss occurred. This is probably a sign of multiple glamor instances in your app. See https://github.com/threepointone/glamor/issues/79' - ) - } - return ret - } - return rule -} - // todo - perf let ruleCache = {} @@ -288,7 +303,7 @@ function toRule (spec) { Object.defineProperty(ret, 'toString', { enumerable: false, value () { - return 'css-' + spec.id + return 'css-obj-' + spec.id } }) ruleCache[spec.id] = ret From 833f82d3d12b8a840012d3d33f9026d73de8f27b Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Wed, 12 Jul 2017 23:09:00 -0600 Subject: [PATCH 04/57] Update snapshots and add test for composition with objects --- src/index.js | 2 -- test/__snapshots__/css.test.js.snap | 10 +++--- test/__snapshots__/react.test.js.snap | 36 ++++++++++++++------ test/react.test.js | 47 +++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index 7878155d1..42802fd1b 100644 --- a/src/index.js +++ b/src/index.js @@ -9,8 +9,6 @@ export const sheet = new StyleSheet() // 🚀 sheet.inject() -window.sheet = sheet - export let inserted: { [string]: boolean | void } = {} type inputVar = string | number diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 3380a7d82..449df6eac 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -22,9 +22,9 @@ exports[`css composes 3`] = ` justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }" `; -exports[`css composes with objects 1`] = `"css-1a5gpco"`; +exports[`css composes with objects 1`] = `" css-obj-1a5gpco"`; -exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-1a5gpco"`; +exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-obj-1a5gpco"`; exports[`css composes with objects 3`] = ` ".css-cls1-1gi569l-b877w5 { background: white; @@ -41,10 +41,10 @@ exports[`css composes with objects 3`] = ` -webkit-justify-content: center; -ms-flex-pack: center; -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-1fe3owl{display:flex;}.css-1a5gpco{display:flex,block;width:30px;height:calc(40vw - 50px);}.css-1a5gpco:hover{color:blue;}.css-1a5gpco:after{content:\\" \\";color:red;}@media(min-width: 420px){.css-1a5gpco{color:green;}}" + justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-obj-1fe3owl{display:flex;}.css-obj-1a5gpco{display:flex,block;width:30px;height:calc(40vw - 50px);}.css-obj-1a5gpco:hover{color:blue;}.css-obj-1a5gpco:after{content:\\" \\";color:red;}@media(min-width: 420px){.css-obj-1a5gpco{color:green;}}" `; -exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-nil"`; +exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-nil"`; exports[`css composes with undefined values 2`] = ` ".css-cls1-1gi569l-b877w5 { background: white; @@ -80,4 +80,4 @@ exports[`css handles more than 10 dynamic properties 2`] = ` border: solid 1px red; }" `; -exports[`css handles objects 1`] = `"css-1fe3owl"`; +exports[`css handles objects 1`] = `" css-obj-1fe3owl"`; diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 214287d8a..62eb28e6f 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -26,12 +26,12 @@ exports[`styled basic render 1`] = ` `; exports[`styled basic render with object as style 1`] = ` -.css-kp2uw9 { +.css-obj-kp2uw9 { font-size: 20px; }

hello world

@@ -138,7 +138,7 @@ exports[`styled composes with objects 1`] = ` font-size: 32px; } -.css-ioza2b { +.css-obj-ioza2b { color: #333; font-size: 1.333em; } @@ -152,13 +152,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-ioza2b { + .css-obj-ioza2b { font-size: 1.4323121856191332em; } }

hello world @@ -257,17 +257,33 @@ exports[`styled no dynamic 1`] = `

`; -exports[`styled objects 1`] = ` -.css-1x9ygd3 { - padding: 10px; +exports[`styled object composition 1`] = ` +.css-obj-1vqgc1f { + border-radius: 50%; + transition: transform 400ms ease-in-out; + border: 3px solid currentColor; + width: 96px; + height: 96px; + color: blue; +} + +.css-obj-1vqgc1f:hover { + transform: scale(1.2); } -.css-1fe3owl { + +`; + +exports[`styled objects 1`] = ` +.css-obj-1aibhc { + padding: 10px; display: flex; }

hello world diff --git a/test/react.test.js b/test/react.test.js index 4b64d8cc3..bfee2c680 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -97,6 +97,53 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) + test('object composition', () => { + const imageStyles = css({ + width: 96, + height: 96 + }) + + const fakeBlue = css([ + { + color: "blue" + } + ]); + + const red = css([ + { + color: "red" + } + ]); + + const blue = css([ + red, + { + color: "blue" + } + ]); + + const prettyStyles = css([ + { + borderRadius: '50%', + transition: 'transform 400ms ease-in-out', + ':hover': { + transform: 'scale(1.2)' + } + }, + {border: '3px solid currentColor'} + ]) + + const Avatar = styled('img')` + composes: ${prettyStyles} ${imageStyles} ${blue} + ` + + const tree = renderer + .create() + .toJSON() + + expect(tree).toMatchSnapshotWithEmotion() + }) + test('component as selector', () => { const fontSize = '20px' const H1 = styled.h1` From 6a494f881f06c8f50fe1ec1c501bc86a9e8676d8 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Thu, 13 Jul 2017 21:28:58 -0600 Subject: [PATCH 05/57] initial work --- example/src/blocks/objects.example | 40 ++-- example/src/main.emotion.css | 10 +- example/src/main.js | 254 ++++--------------------- example/src/markdown/index.emotion.css | 1 - package.json | 1 + src/babel.js | 2 +- src/glamor/clean.js | 4 + src/index.js | 39 +++- test/react.test.js | 13 +- 9 files changed, 106 insertions(+), 258 deletions(-) diff --git a/example/src/blocks/objects.example b/example/src/blocks/objects.example index c3adb4f89..0601b7322 100644 --- a/example/src/blocks/objects.example +++ b/example/src/blocks/objects.example @@ -3,6 +3,25 @@ const imageStyles = css({ height: 96 }) +const fakeBlue = css([ + { + color: "blue" + } +]); + +const red = css([ + { + color: "red" + } +]); + +const blue = css([ + red, + { + color: "blue" + } +]); + const prettyStyles = css([ { borderRadius: '50%', @@ -11,29 +30,14 @@ const prettyStyles = css([ transform: 'scale(1.2)' } }, - { border: '3px solid #8c81d8' } + {border: '3px solid currentColor'} ]) const Avatar = styled('img')` - composes: ${prettyStyles} ${imageStyles}; + composes: ${prettyStyles} ${imageStyles} ${blue} ` -const AvatarContainer = styled.div( - imageStyles, - { - borderRadius: 10, - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - }, - props => ({ - backgroundColor: props.backgroundColor - }) -) - render( - - - , + , mountNode ) diff --git a/example/src/main.emotion.css b/example/src/main.emotion.css index f6092f436..3895dab6e 100644 --- a/example/src/main.emotion.css +++ b/example/src/main.emotion.css @@ -24,4 +24,12 @@ html, body { font-style: normal; font-weight: 400; src: local('Oxygen Regular'), local('Oxygen-Regular'), url(https://fonts.gstatic.com/s/oxygen/v6/qBSyz106i5ud7wkBU-FrPevvDin1pK8aKteLpeZ5c0A.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;} \ No newline at end of file + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;} +.css-cssA-7o6h5j { color: green; } +.css-cssB-1wxgarx { + color: red; + font-size: 48px; } +.css-BlueH1-1jtrwz { + color: blue; } +.css-FinalH2-19c0xqt { font-size: 32px; + color: var(--css-FinalH2-19c0xqt-0) } \ No newline at end of file diff --git a/example/src/main.js b/example/src/main.js index a9fc04ab1..009df9901 100755 --- a/example/src/main.js +++ b/example/src/main.js @@ -19,6 +19,7 @@ const keyframesExample = require('./blocks/keyframes.example') // const readmeMarkdown = require('../../README.md') const avatarUrl = require('../../emotion.png') + injectGlobal` html, body { font-family: -apple-system, @@ -89,234 +90,43 @@ const PlaygroundWrapper = styled('div')` } ` -class App extends React.Component { - render () { - return ( - - - -
-
-

- emotion - - emotion -

-

The Next Generation of CSS-in-JS

-
- - - - - - - - - - +const cssA = css` + color: green; + ` - - +const cssB = css` + composes: ${cssA} + color: red; + font-size: 48px; + ` - - - - - - - - - - {/* */} - {/* */} - - - - - - +const BlueH1 = styled('h1')` + composes: ${cssB}; + color: blue; +` - - +const FinalH2 = styled(BlueH1)` + font-size:32px; + color: ${p => p.block ? '#EA33C3' : '#e67700'} +` - - +// const Avatar = styled('div')` +// composes: ${prettyStyles} ${blue}; +// +// &:hover > ${Image} { +// width: 96px; +// height: 96px; +// borderRadius: 50%; +// } +// ` - - +class App extends React.Component { + render () { + return ( + + - - -
+ Hello
diff --git a/example/src/markdown/index.emotion.css b/example/src/markdown/index.emotion.css index 9b31238d1..c5ac6b432 100644 --- a/example/src/markdown/index.emotion.css +++ b/example/src/markdown/index.emotion.css @@ -1,4 +1,3 @@ -.css-MarkdownContainer-j714fw { } .css-MarkdownContainer-j714fw h1, .css-MarkdownContainer-j714fw h2, .css-MarkdownContainer-j714fw h3, .css-MarkdownContainer-j714fw h4, .css-MarkdownContainer-j714fw h5 { margin: 16px 0 8px 0; letter-spacing: 1px; diff --git a/package.json b/package.json index 5d338023e..fd25c1814 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "babel-plugin-syntax-jsx": "^6.18.0", "fbjs": "^0.8.12", "styled-components": "2.0.0", + "stylis": "^3.2.3", "theming": "^1.0.1", "touch": "^1.0.0" }, diff --git a/src/babel.js b/src/babel.js index 1c1b7ec00..ff65e4586 100644 --- a/src/babel.js +++ b/src/babel.js @@ -191,7 +191,7 @@ export default function (babel) { composes, hasCssFunction } = inline(path.node.quasi, identifierName, 'css', state.inline) - + // console.log('rules', rules) // hash will be '0' when no styles are passed so we can just return the original tag if (hash === '0') { return tag diff --git a/src/glamor/clean.js b/src/glamor/clean.js index d20052de2..608f962fb 100644 --- a/src/glamor/clean.js +++ b/src/glamor/clean.js @@ -46,5 +46,9 @@ function cleanArray (rules) { // Takes style array or object provided by user and clears all the falsy data // If there is no styles left after filtration returns null export default function clean (input) { + if (typeof input === 'string') { + return input.trim() + } + return Array.isArray(input) ? cleanArray(input) : cleanObject(input) } diff --git a/src/index.js b/src/index.js index 42802fd1b..a8ea51f66 100644 --- a/src/index.js +++ b/src/index.js @@ -15,21 +15,42 @@ type inputVar = string | number type vars = Array +// https://github.com/js-next/react-style/blob/master/lib/stylesToCSS.js#L74 +function replicateSelector (selector, uniqueKey, max, key) { + const maxLength = max || 10 + const _key = key || '' + const replicatedSelector = [] + for (let i = 0; i < maxLength; i++) { + let newSelector = '' + let j = 0 + let l2 = i + 1 + for (; j < l2; j++) { + const selectorX = j === 0 ? selector : '.' + uniqueKey + newSelector += selectorX + (j !== 0 ? j : '') + } + replicatedSelector[i] = newSelector + _key + } + return replicatedSelector.join(',') +} + function values (cls: string, vars: vars) { const hash = hashArray([cls, ...vars]) - const varCls = `vars-${hash}` + console.log(hash) + if (inserted[hash]) { - return varCls + return `vars-${hash} ${hash}` } + const varCls = `vars-${hash}` let src = '' forEach(vars, (val: inputVar, i: number) => { src && (src += '; ') src += `--${cls}-${i}: ${val}` }) - sheet.insert(`.${varCls} {${src}}`) + + sheet.insert(`.${replicateSelector(`vars-${hash}`, hash, 2)} {${src}}`) inserted[hash] = true - return varCls + return varCls + ' ' + hash } export function flush () { @@ -107,11 +128,12 @@ export function css (classes: string[], vars: vars, content: () => string[]) { inserted[hash] = true const rgx = new RegExp(classes[0], 'gm') forEach(src, r => { - const finalRule = r.replace(rgx, `${classes[0]}-${hash}`) + const finalRule = r.replace(rgx, replicateSelector(`${classes[0]}`, hash, 2, '')) + console.log(finalRule) sheet.insert(finalRule) }) } - return `${classes[0]}-${hash} ${computedClassName}` + return `${classes[0]} ${hash} ${computedClassName}` } return ( @@ -250,10 +272,9 @@ function deconstruct (style) { } function deconstructedStyleToCSS (id, style) { + let { plain, selects, medias, supports } = style let css = [] - // plugins here - let { plain, selects, medias, supports } = style if (plain) { css.push(`${selector(id)}{${createMarkupForStyles(plain)}}`) } @@ -297,7 +318,7 @@ function toRule (spec) { return ruleCache[spec.id] } - let ret = { [`css-${spec.id}`]: '' } + let ret = { [`css-obj-${spec.id}`]: '' } Object.defineProperty(ret, 'toString', { enumerable: false, value () { diff --git a/test/react.test.js b/test/react.test.js index bfee2c680..ca1338b08 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -192,21 +192,22 @@ describe('styled', () => { const cssB = css` composes: ${cssA} - height: 64px; + color: red; ` - const H1 = styled('h1')` - composes: ${cssB} + const BlueH1 = styled('h1')` + composes: ${cssB}; + color: blue; font-size: ${fontSize}; ` - const H2 = styled(H1)`font-size:32px;` + const FinalH2 = styled(BlueH1)`font-size:32px;` const tree = renderer .create( -

+ hello world -

+ ) .toJSON() From 83a3bd9590413ceea4fc875511d61b3e4af85d43 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Sat, 15 Jul 2017 15:17:56 -0600 Subject: [PATCH 06/57] getting it how i want it --- src/index.js | 35 ++++++----------------------------- src/parser.js | 1 + 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/index.js b/src/index.js index a8ea51f66..35021ef0e 100644 --- a/src/index.js +++ b/src/index.js @@ -15,42 +15,21 @@ type inputVar = string | number type vars = Array -// https://github.com/js-next/react-style/blob/master/lib/stylesToCSS.js#L74 -function replicateSelector (selector, uniqueKey, max, key) { - const maxLength = max || 10 - const _key = key || '' - const replicatedSelector = [] - for (let i = 0; i < maxLength; i++) { - let newSelector = '' - let j = 0 - let l2 = i + 1 - for (; j < l2; j++) { - const selectorX = j === 0 ? selector : '.' + uniqueKey - newSelector += selectorX + (j !== 0 ? j : '') - } - replicatedSelector[i] = newSelector + _key - } - return replicatedSelector.join(',') -} - function values (cls: string, vars: vars) { const hash = hashArray([cls, ...vars]) - console.log(hash) - + const varCls = `vars-${hash}` if (inserted[hash]) { - return `vars-${hash} ${hash}` + return varCls } - const varCls = `vars-${hash}` let src = '' forEach(vars, (val: inputVar, i: number) => { src && (src += '; ') src += `--${cls}-${i}: ${val}` }) - - sheet.insert(`.${replicateSelector(`vars-${hash}`, hash, 2)} {${src}}`) + sheet.insert(`.${varCls} {${src}}`) inserted[hash] = true - return varCls + ' ' + hash + return varCls } export function flush () { @@ -128,12 +107,10 @@ export function css (classes: string[], vars: vars, content: () => string[]) { inserted[hash] = true const rgx = new RegExp(classes[0], 'gm') forEach(src, r => { - const finalRule = r.replace(rgx, replicateSelector(`${classes[0]}`, hash, 2, '')) - console.log(finalRule) - sheet.insert(finalRule) + sheet.insert(r.replace(rgx, `${classes[0]}-${hash}`)) }) } - return `${classes[0]} ${hash} ${computedClassName}` + return `${classes[0]}-${hash} ${computedClassName}` } return ( diff --git a/src/parser.js b/src/parser.js index 5767dd8ee..46b6d740f 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,4 +1,5 @@ // @flow +import { t } from 'babel-types' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' From 18c46e63d140807531b88d08769ca236d7af653d Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Sat, 15 Jul 2017 20:53:39 -0600 Subject: [PATCH 07/57] Progress --- src/index.js | 3 ++- src/parser.js | 9 ++------- test/react.test.js | 7 +++++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index 35021ef0e..4ed408474 100644 --- a/src/index.js +++ b/src/index.js @@ -107,7 +107,7 @@ export function css (classes: string[], vars: vars, content: () => string[]) { inserted[hash] = true const rgx = new RegExp(classes[0], 'gm') forEach(src, r => { - sheet.insert(r.replace(rgx, `${classes[0]}-${hash}`)) + sheet.insert(r.replace(rgx, `${classes[0]}-${hash} ${computedClassName.trim().replace(/ /g, '.')}`)) }) } return `${classes[0]}-${hash} ${computedClassName}` @@ -506,3 +506,4 @@ function multiIndexCache (fn) { return value } } + diff --git a/src/parser.js b/src/parser.js index b7be950a9..2e54559ad 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,5 +1,5 @@ // @flow -import { t } from 'babel-types' +import camelizeStyleName from 'fbjs/lib/camelizeStyleName' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' @@ -43,6 +43,7 @@ export function parseCSS ( let vars = 0 let composes: number = 0 let hasCssFunction = false + root.walkDecls((decl: CSSDecl): void => { if (decl.prop === 'name') decl.remove() if (decl.value.match(/attr/)) { @@ -84,12 +85,6 @@ export function parseCSS ( }) } - root.walkRules(rule => { - if (rule.nodes.length === 0) { - rule.remove() - } - }) - autoprefix(root) return { rules: stringifyCSSRoot(root), diff --git a/test/react.test.js b/test/react.test.js index ca1338b08..a519a25a9 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -19,9 +19,12 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) - test('basic render', () => { + test.only('basic render', () => { const fontSize = 20 - const H1 = styled.h1`font-size: ${fontSize}px;` + const H1 = styled.h1` + color: blue; + font-size: ${fontSize}px; + ` const tree = renderer.create(

hello world

).toJSON() From c819e1972dd0a1bdb52c5835a1db4d49d5dda2c1 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Sat, 15 Jul 2017 22:03:47 -0600 Subject: [PATCH 08/57] Merge branch 'master' of https://github.com/tkh44/emotion into glamor-extended # Conflicts: # package.json # test/__snapshots__/css.test.js.snap # test/__snapshots__/react.test.js.snap --- package.json | 4 +- src/parser.js | 49 ++++++++++++++++++-- test/babel/__snapshots__/styled.test.js.snap | 22 +++++++++ test/babel/styled.test.js | 23 +++++++++ test/react.test.js | 1 + 5 files changed, 95 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c9a068f88..23501ce54 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,13 @@ "format": "prettier-eslint --write \"src/**/*.js\" \"test/**/*.js\" \"example/**/*.js\" \"jest-utils/**/*.js\"" }, "dependencies": { - "autoprefixer": "^7.1.2", "@arr/filter.mutate": "^1.0.0", "@arr/foreach": "^1.0.0", "@arr/map": "^1.0.0", + "autoprefixer": "^7.1.2", "babel-plugin-syntax-jsx": "^6.18.0", "fbjs": "^0.8.12", + "inline-style-prefixer": "^3.0.6", "postcss-js": "^1.0.0", "styled-components": "2.0.0", "theming": "^1.0.1", @@ -50,6 +51,7 @@ "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "bundlesize": "^0.5.7", + "caniuse-api": "^2.0.0", "jest": "^20.0.4", "jest-cli": "^20.0.4", "jest-emotion-react": "^0.0.2", diff --git a/src/parser.js b/src/parser.js index 2e54559ad..01e6c7895 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,9 +1,11 @@ // @flow import camelizeStyleName from 'fbjs/lib/camelizeStyleName' +import prefixAll from 'inline-style-prefixer/static' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' import autoprefix from 'styled-components/lib/utils/autoprefix' +import { objStyle } from './index' type CSSDecl = { parent: { selector: string, nodes: Array }, @@ -85,15 +87,56 @@ export function parseCSS ( }) } - autoprefix(root) + const objStyles = {} + + root.walkRules((rule, i) => { + if (rule.nodes.length === 0) { + rule.remove() + } + + // console.log(JSON.stringify(rule, null, 2)) + const { selector } = rule + const style = (objStyles[selector] = objStyles[selector] || {}) + + rule.walkDecls((decl: CSSDecl): void => { + style[camelizeStyleName(decl.prop)] = decl.value + }) + }) + + root.walkAtRules((atRule, i) => { + const {name, params} = atRule + const key = `@${name} ${params}`.trim() + const atRuleStyle = (objStyles[key] = objStyles[key] || {}) + + atRule.walkRules((rule, i) => { + if (rule.nodes.length === 0) { + rule.remove() + } + + // console.log(JSON.stringify(rule, null, 2)) + const {selector} = rule + const style = (atRuleStyle[selector] = atRuleStyle[selector] || {}) + + rule.walkDecls((decl: CSSDecl): void => { + style[camelizeStyleName(decl.prop)] = decl.value + }) + }) + }) + + const prefixedObjStyles = prefixAll(objStyles) + + console.log(JSON.stringify(prefixedObjStyles, null, 2)) + return { rules: stringifyCSSRoot(root), hasOtherMatch: vars !== options.matches, hasVar: (!!vars && vars !== composes) || - !!(options.inlineMode && options.matches), + !!(options.inlineMode && options.matches), composes, - hasCssFunction + hasCssFunction, + objStyles, + prefixedObjStyles } } diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index d42654e85..456f71b18 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -199,6 +199,28 @@ exports[`babel styled component inline match works on multiple 1`] = ` });" `; +exports[`babel styled component inline media query 1`] = ` +"const H1 = styled(\\"h1\\", [\\"css-H1-162s3sk\\"], [], function createEmotionStyledRules() { + return [\`@media print { + .css-H1-162s3sk body { + font-size: 10pt + } +}\`, \`@media screen { + .css-H1-162s3sk body { + font-size: 13px + } +}\`, \`@media screen, print { + .css-H1-162s3sk body { + line-height: 1.2 + } +}\`, \`@media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) { + .css-H1-162s3sk body { + line-height: 1.4 + } +}\`]; +});" +`; + exports[`babel styled component inline name is correct with no identifier 1`] = ` " css([\\"css-1cppx8p\\"], [], function createEmotionRules() { diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index ef68fc4fe..0184378c9 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -48,6 +48,29 @@ describe('babel styled component', () => { expect(code).toMatchSnapshot() }) + test('media query', () => { + const basic = + 'const H1 = styled.h1`@media print {' + + ' font-size: 10pt' + + '}' + + '@media screen {' + + ' .child-selector { font-size: 13px }' + + '}' + + '@media screen, print {' + + ' &:hover + & { line-height: 1.2 }' + + '}' + + '@media only screen ' + + ' and (min-device-width: 320px) ' + + ' and (max-device-width: 480px)' + + ' and (-webkit-min-device-pixel-ratio: 2) {' + + ' .child-selector { line-height: 1.4 }' + + '}`' + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + test('function call', () => { const basic = "styled(MyComponent)`font-size: ${fontSize + 'px'};`" const { code } = babel.transform(basic, { diff --git a/test/react.test.js b/test/react.test.js index acb1f650d..e89c0d3f8 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -23,6 +23,7 @@ describe('styled', () => { const fontSize = 20 const H1 = styled.h1` color: blue; + font-size: ${fontSize}px; ` From d332fe7fa39eb5d582d89d0a35a423b89c2675f7 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Sat, 15 Jul 2017 23:28:33 -0600 Subject: [PATCH 09/57] Fix flow types --- src/parser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parser.js b/src/parser.js index 01e6c7895..9c34c7ed3 100644 --- a/src/parser.js +++ b/src/parser.js @@ -36,7 +36,8 @@ export function parseCSS ( hasOtherMatch: boolean, hasVar: boolean, composes: number, - hasCssFunction: boolean + hasCssFunction: boolean, + prefixedObjStyles: { [string]: any } } { // todo - handle errors const root = parse(css) From be90c45f813eaf6595e0c18c5d0c75070191323a Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Sat, 15 Jul 2017 23:55:32 -0600 Subject: [PATCH 10/57] pass the obj styles back to babel.js --- src/babel.js | 2 +- src/inline.js | 17 ++++++++++++++--- src/parser.js | 2 -- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/babel.js b/src/babel.js index b638bf808..81aedc7fe 100644 --- a/src/babel.js +++ b/src/babel.js @@ -107,7 +107,7 @@ function parseDynamicValues (rules, t, options) { export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - let { hash, rules, name, hasOtherMatch, composes, hasCssFunction } = inline( + let { hash, rules, name, hasOtherMatch, composes, hasCssFunction, prefixedObjStyles } = inline( path.node.quasi, identifierName, 'css', diff --git a/src/inline.js b/src/inline.js index 9a42f0baa..b4e5959c8 100644 --- a/src/inline.js +++ b/src/inline.js @@ -57,7 +57,8 @@ export function inline ( hasVar: boolean, hasOtherMatch: boolean, composes: number, - hasCssFunction: boolean + hasCssFunction: boolean, + prefixedObjStyles: { [string]: any } } { let strs = quasi.quasis.map(x => x.value.cooked) let hash = hashArray([...strs]) // todo - add current filename? @@ -72,7 +73,8 @@ export function inline ( hasVar, hasOtherMatch, composes, - hasCssFunction + hasCssFunction, + prefixedObjStyles } = parseCSS(`.${name}-${hash} { ${src} }`, { inlineMode: inlineMode, matches, @@ -80,7 +82,16 @@ export function inline ( hash, canCompose: true }) - return { hash, name, rules, hasVar, hasOtherMatch, composes, hasCssFunction } + return { + hash, + name, + rules, + hasVar, + hasOtherMatch, + composes, + hasCssFunction, + prefixedObjStyles + } } export function keyframes ( diff --git a/src/parser.js b/src/parser.js index 9c34c7ed3..984e2386f 100644 --- a/src/parser.js +++ b/src/parser.js @@ -126,8 +126,6 @@ export function parseCSS ( const prefixedObjStyles = prefixAll(objStyles) - console.log(JSON.stringify(prefixedObjStyles, null, 2)) - return { rules: stringifyCSSRoot(root), hasOtherMatch: vars !== options.matches, From 2bbafa878011ea076c480390801331d6bf47b3bd Mon Sep 17 00:00:00 2001 From: mitchellhamilton Date: Mon, 17 Jul 2017 11:31:10 +1000 Subject: [PATCH 11/57] Stuff --- src/babel.js | 45 +++++++++++++++++++++----------------- src/inline.js | 2 +- src/parser.js | 46 ++++----------------------------------- test/babel/styled.test.js | 2 +- 4 files changed, 31 insertions(+), 64 deletions(-) diff --git a/src/babel.js b/src/babel.js index 81aedc7fe..75dac0d1b 100644 --- a/src/babel.js +++ b/src/babel.js @@ -7,6 +7,7 @@ import { inline, keyframes, fontFace, injectGlobal } from './inline' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' import createAttrExpression from './attrs' +import * as babylon from 'babylon' function joinExpressionsWithSpaces (expressions, t) { const quasis = [t.templateElement({ cooked: '', raw: '' }, true)] @@ -113,30 +114,34 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { 'css', state.inline ) - + console.log(prefixedObjStyles) + // return path.replaceWith(t.callExpression(identifier, [ + // tag, + // t.arrayExpression([prefixedObjStyles]) + // ])) // hash will be '0' when no styles are passed so we can just return the original tag if (hash === '0') { return tag } - const inputClasses = [t.stringLiteral(`${name}-${hash}`)] - for (var i = 0; i < composes; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) - } - - const vars = path.node.quasi.expressions - - const dynamicValues = parseDynamicValues(rules, t, { composes, vars }) - const args = [tag, t.arrayExpression(inputClasses), t.arrayExpression(vars)] - if (!hasOtherMatch && !state.inline && !hasCssFunction) { - state.insertStaticRules(rules) - } else if (rules.length !== 0) { - const inlineContentExpr = t.functionExpression( - t.identifier('createEmotionStyledRules'), - vars.map((x, i) => t.identifier(`x${i}`)), - t.blockStatement([t.returnStatement(t.arrayExpression(dynamicValues))]) - ) - args.push(inlineContentExpr) - } + // const inputClasses = [t.stringLiteral(`${name}-${hash}`)] + // for (var i = 0; i < composes; i++) { + // inputClasses.push(path.node.quasi.expressions.shift()) + // } + + // const vars = path.node.quasi.expressions + const objectAST = babylon.parseExpression(JSON.stringify(prefixedObjStyles)) + // const dynamicValues = parseDynamicValues(rules, t, { composes, vars }) + const args = [tag, t.arrayExpression([objectAST]), t.arrayExpression([])] + // if (!hasOtherMatch && !state.inline && !hasCssFunction) { + // state.insertStaticRules(rules) + // } else if (rules.length !== 0) { + // const inlineContentExpr = t.functionExpression( + // t.identifier('createEmotionStyledRules'), + // vars.map((x, i) => t.identifier(`x${i}`)), + // t.blockStatement([t.returnStatement(t.arrayExpression(dynamicValues))]) + // ) + // args.push(inlineContentExpr) + // } return t.callExpression(identifier, args) } diff --git a/src/inline.js b/src/inline.js index b4e5959c8..3fee56c55 100644 --- a/src/inline.js +++ b/src/inline.js @@ -75,7 +75,7 @@ export function inline ( composes, hasCssFunction, prefixedObjStyles - } = parseCSS(`.${name}-${hash} { ${src} }`, { + } = parseCSS(`${src}`, { inlineMode: inlineMode, matches, name, diff --git a/src/parser.js b/src/parser.js index 984e2386f..d41321b87 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,11 +1,9 @@ // @flow -import camelizeStyleName from 'fbjs/lib/camelizeStyleName' -import prefixAll from 'inline-style-prefixer/static' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' import autoprefix from 'styled-components/lib/utils/autoprefix' -import { objStyle } from './index' +import postcssJs from 'postcss-js' type CSSDecl = { parent: { selector: string, nodes: Array }, @@ -87,44 +85,8 @@ export function parseCSS ( }) }) } - - const objStyles = {} - - root.walkRules((rule, i) => { - if (rule.nodes.length === 0) { - rule.remove() - } - - // console.log(JSON.stringify(rule, null, 2)) - const { selector } = rule - const style = (objStyles[selector] = objStyles[selector] || {}) - - rule.walkDecls((decl: CSSDecl): void => { - style[camelizeStyleName(decl.prop)] = decl.value - }) - }) - - root.walkAtRules((atRule, i) => { - const {name, params} = atRule - const key = `@${name} ${params}`.trim() - const atRuleStyle = (objStyles[key] = objStyles[key] || {}) - - atRule.walkRules((rule, i) => { - if (rule.nodes.length === 0) { - rule.remove() - } - - // console.log(JSON.stringify(rule, null, 2)) - const {selector} = rule - const style = (atRuleStyle[selector] = atRuleStyle[selector] || {}) - - rule.walkDecls((decl: CSSDecl): void => { - style[camelizeStyleName(decl.prop)] = decl.value - }) - }) - }) - - const prefixedObjStyles = prefixAll(objStyles) + autoprefix(root) + const objStyles = postcssJs.objectify(root) return { rules: stringifyCSSRoot(root), @@ -135,7 +97,7 @@ export function parseCSS ( composes, hasCssFunction, objStyles, - prefixedObjStyles + prefixedObjStyles: objStyles } } diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index 0184378c9..22f46df17 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -8,7 +8,7 @@ jest.mock('fs') fs.existsSync.mockReturnValue(true) describe('babel styled component', () => { - describe('inline', () => { + describe.only('inline', () => { test('no use', () => { const basic = 'styled.h1``' const { code } = babel.transform(basic, { From 5e2296de9ef455f786fae2d16a475bb64861ec29 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Sun, 16 Jul 2017 19:47:21 -0600 Subject: [PATCH 12/57] Deleting more code. Its broken state but its progress --- example/.babelrc | 2 +- example/src/main.js | 4 +- src/babel.js | 196 +++++++++++++++++++++++--------------------- src/index.js | 17 +--- src/inline.js | 60 +++++--------- src/parser.js | 104 ++++------------------- test/react.test.js | 2 +- 7 files changed, 147 insertions(+), 238 deletions(-) diff --git a/example/.babelrc b/example/.babelrc index 7a1353afd..373725d3f 100644 --- a/example/.babelrc +++ b/example/.babelrc @@ -11,6 +11,6 @@ "react" ], "plugins": [ - 'emotion/babel' + ['emotion/babel', { inline: true }] ] } diff --git a/example/src/main.js b/example/src/main.js index 009df9901..b7f39686a 100755 --- a/example/src/main.js +++ b/example/src/main.js @@ -1,7 +1,7 @@ import React from 'react' import { render } from 'react-dom' -import styled, { ThemeProvider } from 'emotion/react' -import { css, fontFace, keyframes, injectGlobal } from 'emotion' +import { ThemeProvider } from 'emotion/react/theming' +import styled, { css, fontFace, keyframes, injectGlobal } from 'emotion/react' import Markdown from './markdown' import Playground from './playground' import logoUrl from '../../emotion.png' diff --git a/src/babel.js b/src/babel.js index 81aedc7fe..3822350c8 100644 --- a/src/babel.js +++ b/src/babel.js @@ -19,7 +19,7 @@ function joinExpressionsWithSpaces (expressions, t) { return t.templateLiteral(quasis, expressions) } -function parseDynamicValues (rules, t, options) { +function parseDynamicValues (styles, t, options) { if (options.composes === undefined) options.composes = 0 return rules.map(rule => { const re = /xxx(\d+)xxx|attr\(([^,\s)]+)(?:\s*([^,)]*)?)(?:,\s*(\S+))?\)/gm @@ -107,35 +107,24 @@ function parseDynamicValues (rules, t, options) { export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - let { hash, rules, name, hasOtherMatch, composes, hasCssFunction, prefixedObjStyles } = inline( - path.node.quasi, - identifierName, - 'css', - state.inline - ) + const { + styles, + isStaticBlock + } = inline(path.node.quasi, identifierName, 'css', state.inline) + + // console.log(JSON.stringify(styles, null, 2)) - // hash will be '0' when no styles are passed so we can just return the original tag - if (hash === '0') { - return tag - } - const inputClasses = [t.stringLiteral(`${name}-${hash}`)] - for (var i = 0; i < composes; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) - } const vars = path.node.quasi.expressions - const dynamicValues = parseDynamicValues(rules, t, { composes, vars }) - const args = [tag, t.arrayExpression(inputClasses), t.arrayExpression(vars)] + const dynamicValues = parseDynamicValues(rules, t, { vars }) + const args = [ + tag, + t.arrayExpression([createAstObj(styles, t)]), + t.arrayExpression(vars) + ] if (!hasOtherMatch && !state.inline && !hasCssFunction) { state.insertStaticRules(rules) - } else if (rules.length !== 0) { - const inlineContentExpr = t.functionExpression( - t.identifier('createEmotionStyledRules'), - vars.map((x, i) => t.identifier(`x${i}`)), - t.blockStatement([t.returnStatement(t.arrayExpression(dynamicValues))]) - ) - args.push(inlineContentExpr) } return t.callExpression(identifier, args) @@ -147,8 +136,7 @@ export function buildStyledObjectCallExpression (path, identifier, t) { : t.stringLiteral(path.node.callee.property.name) return t.callExpression(identifier, [ tag, - t.arrayExpression(prefixAst(path.node.arguments, t)), - t.arrayExpression() + t.arrayExpression(prefixAst(path.node.arguments, t)) ]) } @@ -180,16 +168,76 @@ export function replaceGlobalWithCallExpression ( } } +export function replaceCssWithCallExpression (path, identifier, state, t) { + try { + const { + hash, + name, + rules, + hasVar, + composes, + hasOtherMatch, + styles + } = inline(path.node.quasi, getIdentifierName(path, t), 'css', state.inline) + const inputClasses = [t.stringLiteral(`${name}-${hash}`)] + for (var i = 0; i < composes; i++) { + inputClasses.push(path.node.quasi.expressions.shift()) + } + const args = [ + t.arrayExpression([createAstObj(styles, t)]), + t.arrayExpression(path.node.quasi.expressions) + ] + if (!hasOtherMatch && !state.inline) { + state.insertStaticRules(rules) + if (!hasVar) { + return path.replaceWith(joinExpressionsWithSpaces(inputClasses, t)) + } + } else if (rules.length !== 0) { + const expressions = path.node.quasi.expressions.map((x, i) => + t.identifier(`x${i}`) + ) + } + path.replaceWith(t.callExpression(identifier, args)) + } catch (e) { + throw path.buildCodeFrameError(e) + } +} + +export function replaceKeyframesWithCallExpression (path, identifier, state, t) { + const { hash, name, rules, hasInterpolation } = keyframes( + path.node.quasi, + getIdentifierName(path, t), + 'animation' + ) + const animationName = `${name}-${hash}` + if (!hasInterpolation && !state.inline) { + state.insertStaticRules([`@keyframes ${animationName} ${rules.join('')}`]) + path.replaceWith(t.stringLiteral(animationName)) + } else { + path.replaceWith( + t.callExpression(identifier, [ + t.stringLiteral(animationName), + t.arrayExpression( + parseDynamicValues(rules, t, { + inputExpressions: path.node.quasi.expressions + }) + ) + ]) + ) + } +} + function prefixAst (args, t) { const prefixer = postcssJs.sync([autoprefixer]) function isLiteral (value) { return ( - t.isStringLiteral(value) || - t.isNumericLiteral(value) || - t.isBooleanLiteral(value) + t.isStringLiteral(value) || + t.isNumericLiteral(value) || + t.isBooleanLiteral(value) ) } + if (Array.isArray(args)) { return args.map(element => prefixAst(element, t)) } @@ -254,75 +302,37 @@ function prefixAst (args, t) { return args } -export function replaceCssWithCallExpression (path, identifier, state, t) { - try { - const { hash, name, rules, hasVar, composes, hasOtherMatch } = inline( - path.node.quasi, - getIdentifierName(path, t), - 'css', - state.inline - ) - const inputClasses = [t.stringLiteral(`${name}-${hash}`)] - for (var i = 0; i < composes; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) - } - const args = [ - t.arrayExpression(inputClasses), - t.arrayExpression(path.node.quasi.expressions) - ] - if (!hasOtherMatch && !state.inline) { - state.insertStaticRules(rules) - if (!hasVar) { - return path.replaceWith(joinExpressionsWithSpaces(inputClasses, t)) - } - } else if (rules.length !== 0) { - const expressions = path.node.quasi.expressions.map((x, i) => - t.identifier(`x${i}`) - ) - const inlineContentExpr = t.functionExpression( - t.identifier('createEmotionRules'), - expressions, - t.blockStatement([ - t.returnStatement( - t.arrayExpression( - parseDynamicValues(rules, t, { - inputExpressions: expressions, - composes - }) - ) - ) - ]) - ) - args.push(inlineContentExpr) - } - path.replaceWith(t.callExpression(identifier, args)) - } catch (e) { - throw path.buildCodeFrameError(e) +function objValueToAst (value, t) { + if (typeof value === 'string') { + return t.stringLiteral(value) + } else if (Array.isArray(value)) { + return t.arrayExpression(value.map(v => objValueToAst(v, t))) } + + return createAstObj(value, t) } -export function replaceKeyframesWithCallExpression (path, identifier, state, t) { - const { hash, name, rules, hasInterpolation } = keyframes( - path.node.quasi, - getIdentifierName(path, t), - 'animation' - ) - const animationName = `${name}-${hash}` - if (!hasInterpolation && !state.inline) { - state.insertStaticRules([`@keyframes ${animationName} ${rules.join('')}`]) - path.replaceWith(t.stringLiteral(animationName)) - } else { - path.replaceWith( - t.callExpression(identifier, [ - t.stringLiteral(animationName), - t.arrayExpression( - parseDynamicValues(rules, t, { - inputExpressions: path.node.quasi.expressions - }) - ) - ]) +function createAstObj (obj, t) { + console.log(JSON.stringify(obj, null, 2)) + const props = [] + const computed = false, + shorthand = false, + decorators = [] + for (let key in obj) { + const rawValue = obj[key] + let computedValue = objValueToAst(rawValue, t) + props.push( + t.objectProperty( + t.stringLiteral(key), + computedValue, + computed, + shorthand, + decorators + ) ) } + + return t.objectExpression(props) } const visited = Symbol('visited') diff --git a/src/index.js b/src/index.js index 4ed408474..20e6b1403 100644 --- a/src/index.js +++ b/src/index.js @@ -72,7 +72,7 @@ function getGlamorStylesFromClassName (className) { } } -export function css (classes: string[], vars: vars, content: () => string[]) { +export function css (classes: string[], vars: vars) { if (!Array.isArray(classes)) { classes = [classes] } @@ -98,21 +98,6 @@ export function css (classes: string[], vars: vars, content: () => string[]) { computedClassName += ' ' + objStyle(...objectStyles).toString() } - if (content) { - // inline mode - let src = content(...vars) // returns an array - let hash = hashArray(src) - - if (!inserted[hash]) { - inserted[hash] = true - const rgx = new RegExp(classes[0], 'gm') - forEach(src, r => { - sheet.insert(r.replace(rgx, `${classes[0]}-${hash} ${computedClassName.trim().replace(/ /g, '.')}`)) - }) - } - return `${classes[0]}-${hash} ${computedClassName}` - } - return ( computedClassName + (vars && vars.length > 0 ? ' ' + values(classes[0], vars) : '') diff --git a/src/inline.js b/src/inline.js index b4e5959c8..ea64f41eb 100644 --- a/src/inline.js +++ b/src/inline.js @@ -29,20 +29,20 @@ function createSrc ( strs: string[], name: string, hash: string -): { src: string, matches: number } { - let matches = 0 +): { src: string, dynamicValueCount: number } { + let dynamicValueCount = 0 const src = strs .reduce((arr, str, i) => { arr.push(str) if (i !== strs.length - 1) { - matches++ + dynamicValueCount++ arr.push(`xxx${i}xxx`) } return arr }, []) .join('') .trim() - return { src, matches } + return { src, dynamicValueCount } } export function inline ( @@ -51,14 +51,8 @@ export function inline ( prefix: string, inlineMode: boolean ): { - hash: string, - name: string, - rules: string[], - hasVar: boolean, - hasOtherMatch: boolean, - composes: number, - hasCssFunction: boolean, - prefixedObjStyles: { [string]: any } + isStaticBlock: boolean, + styles: { [string]: any } } { let strs = quasi.quasis.map(x => x.value.cooked) let hash = hashArray([...strs]) // todo - add current filename? @@ -67,30 +61,20 @@ export function inline ( identifierName, prefix ) - let { src, matches } = createSrc(strs, name, hash) + let { src, dynamicValueCount } = createSrc(strs, name, hash) let { - rules, - hasVar, - hasOtherMatch, - composes, - hasCssFunction, - prefixedObjStyles - } = parseCSS(`.${name}-${hash} { ${src} }`, { - inlineMode: inlineMode, - matches, - name, - hash, - canCompose: true - }) + styles, + isStaticBlock + } = parseCSS(src) + + console.log('styles', JSON.stringify(styles, null, 2)) + console.log('isStaticBlock', isStaticBlock) + return { hash, name, - rules, - hasVar, - hasOtherMatch, - composes, - hasCssFunction, - prefixedObjStyles + isStaticBlock, + styles } } @@ -111,11 +95,11 @@ export function keyframes ( identifierName, prefix ) - const { src, matches } = createSrc(strs, name, hash) + const { src, dynamicValueCount } = createSrc(strs, name, hash) let { rules, hasVar, hasOtherMatch } = parseCSS(`{ ${src} }`, { nested: false, inlineMode: true, - matches, + dynamicValueCount: dynamicValueCount, name, hash }) @@ -126,9 +110,9 @@ export function fontFace ( quasi: any ): { rules: string[], hasInterpolation: boolean } { let strs = quasi.quasis.map(x => x.value.cooked) - const { src, matches } = createSrc(strs, 'name', 'hash') + const { src, dynamicValueCount } = createSrc(strs, 'name', 'hash') let { rules, hasVar, hasOtherMatch } = parseCSS(`@font-face {${src}}`, { - matches, + dynamicValueCount: dynamicValueCount, inlineMode: true, name: 'name', hash: 'hash' @@ -140,9 +124,9 @@ export function injectGlobal ( quasi: any ): { rules: string[], hasInterpolation: boolean } { let strs = quasi.quasis.map(x => x.value.cooked) - const { src, matches } = createSrc(strs, 'name', 'hash') + const { src, dynamicValueCount } = createSrc(strs, 'name', 'hash') let { rules, hasVar, hasOtherMatch } = parseCSS(src, { - matches, + dynamicValueCount: dynamicValueCount, inlineMode: true, name: 'name', hash: 'hash' diff --git a/src/parser.js b/src/parser.js index 984e2386f..927732fee 100644 --- a/src/parser.js +++ b/src/parser.js @@ -15,78 +15,15 @@ type CSSDecl = { } export function parseCSS ( - css: string, - options: { - nested?: boolean, - inlineMode: boolean, - matches: number, - name: string, - hash: string, - canCompose?: boolean - } = { - nested: true, - inlineMode: true, - matches: 0, - name: 'name', - hash: 'hash', - canCompose: false - } + css: string ): { - rules: string[], - hasOtherMatch: boolean, - hasVar: boolean, - composes: number, - hasCssFunction: boolean, - prefixedObjStyles: { [string]: any } + isStaticBlock: boolean, + styles: { [string]: any } } { // todo - handle errors const root = parse(css) - if (options.nested !== false) postcssNested(root) - let vars = 0 let composes: number = 0 - let hasCssFunction = false - - root.walkDecls((decl: CSSDecl): void => { - if (decl.prop === 'name') decl.remove() - if (decl.value.match(/attr/)) { - hasCssFunction = true - } - if (options.canCompose) { - if (decl.prop === 'composes') { - if (decl.parent.selector !== `.${options.name}-${options.hash}`) { - throw new Error('composes cannot be on nested selectors') - } - if (!/xxx(\d+)xxx/gm.exec(decl.value)) { - throw new Error('composes must be a interpolation') - } - if (decl.parent.nodes[0] !== decl) { - throw new Error('composes must be the first rule') - } - const composeMatches = decl.value.match(/xxx(\d+)xxx/gm) - const numOfComposes: number = !composeMatches - ? 0 - : composeMatches.length - composes += numOfComposes - vars += numOfComposes - decl.remove() - return - } - } - if (!options.inlineMode) { - const match = /xxx(\d+)xxx/gm.exec(decl.value) - if (match) { - vars++ - } - } - }) - if (!options.inlineMode && vars === options.matches && !hasCssFunction) { - root.walkDecls(decl => { - decl.value = decl.value.replace(/xxx(\d+)xxx/gm, (match, p1) => { - return `var(--${options.name}-${options.hash}-${p1 - composes})` - }) - }) - } const objStyles = {} @@ -100,12 +37,17 @@ export function parseCSS ( const style = (objStyles[selector] = objStyles[selector] || {}) rule.walkDecls((decl: CSSDecl): void => { + const match = /xxx(\d+)xxx/gm.exec(decl.value) + if (match) { + vars++ + } + style[camelizeStyleName(decl.prop)] = decl.value }) }) root.walkAtRules((atRule, i) => { - const {name, params} = atRule + const { name, params } = atRule const key = `@${name} ${params}`.trim() const atRuleStyle = (objStyles[key] = objStyles[key] || {}) @@ -115,10 +57,15 @@ export function parseCSS ( } // console.log(JSON.stringify(rule, null, 2)) - const {selector} = rule + const { selector } = rule const style = (atRuleStyle[selector] = atRuleStyle[selector] || {}) rule.walkDecls((decl: CSSDecl): void => { + const match = /xxx(\d+)xxx/gm.exec(decl.value) + if (match) { + vars++ + } + style[camelizeStyleName(decl.prop)] = decl.value }) }) @@ -127,24 +74,7 @@ export function parseCSS ( const prefixedObjStyles = prefixAll(objStyles) return { - rules: stringifyCSSRoot(root), - hasOtherMatch: vars !== options.matches, - hasVar: - (!!vars && vars !== composes) || - !!(options.inlineMode && options.matches), - composes, - hasCssFunction, - objStyles, - prefixedObjStyles + styles: prefixedObjStyles, + isStaticBlock: vars === 0 } } - -function stringifyCSSRoot (root) { - return root.nodes.map((node, i) => { - let str = '' - stringify(node, x => { - str += x - }) - return str - }) -} diff --git a/test/react.test.js b/test/react.test.js index 5ab6ccdd9..9ba23b464 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -20,7 +20,7 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) - test.only('basic render', () => { + test('basic render', () => { const fontSize = 20 const H1 = styled.h1` color: blue; From 64392729054fe1a1abb9453ac5707fd823628a6e Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Sun, 16 Jul 2017 23:51:19 -0600 Subject: [PATCH 13/57] broke broke broke --- src/babel.js | 150 ++++++++++++++++---------- src/inline.js | 47 ++------ src/parser.js | 27 ++++- src/react/index.js | 16 +-- test/__snapshots__/react.test.js.snap | 6 ++ test/extract/extract.test.js | 2 +- test/react.test.js | 2 +- 7 files changed, 144 insertions(+), 106 deletions(-) diff --git a/src/babel.js b/src/babel.js index cf003c7c4..c7db14ad1 100644 --- a/src/babel.js +++ b/src/babel.js @@ -1,8 +1,10 @@ +// @flow weak import fs from 'fs' import { basename } from 'path' import { touchSync } from 'touch' import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' +import forEach from '@arr/foreach' import { inline, keyframes, fontFace, injectGlobal } from './inline' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' @@ -108,24 +110,20 @@ function parseDynamicValues (styles, t, options) { export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - const { - styles, - isStaticBlock - } = inline(path.node.quasi, identifierName, 'css', state.inline) - - // console.log(JSON.stringify(styles, null, 2)) - + const { styles, isStaticBlock } = inline( + path.node.quasi, + identifierName, + ) - const vars = path.node.quasi.expressions + console.log(JSON.stringify(styles, null, 2)) - const dynamicValues = parseDynamicValues(rules, t, { vars }) const args = [ tag, - t.arrayExpression([createAstObj(styles, t)]), - t.arrayExpression(vars) + t.arrayExpression([createAstObj(styles, path.node.quasi.expressions, t)]) ] - if (!hasOtherMatch && !state.inline && !hasCssFunction) { - state.insertStaticRules(rules) + + if (state.extractStatic && isStaticBlock) { + // state.insertStaticRules(rules) } return t.callExpression(identifier, args) @@ -171,34 +169,21 @@ export function replaceGlobalWithCallExpression ( export function replaceCssWithCallExpression (path, identifier, state, t) { try { - const { - hash, - name, - rules, - hasVar, - composes, - hasOtherMatch, - styles - } = inline(path.node.quasi, getIdentifierName(path, t), 'css', state.inline) - const inputClasses = [t.stringLiteral(`${name}-${hash}`)] - for (var i = 0; i < composes; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) - } - const args = [ - t.arrayExpression([createAstObj(styles, t)]), - t.arrayExpression(path.node.quasi.expressions) - ] - if (!hasOtherMatch && !state.inline) { - state.insertStaticRules(rules) - if (!hasVar) { - return path.replaceWith(joinExpressionsWithSpaces(inputClasses, t)) - } - } else if (rules.length !== 0) { - const expressions = path.node.quasi.expressions.map((x, i) => - t.identifier(`x${i}`) - ) + const { styles, isStaticBlock } = inline( + path.node.quasi, + getIdentifierName(path, t) + ) + + if (state.extractStatic && isStaticBlock) { + // state.insertStaticRules(rules) + // if (!hasVar) { + // return path.replaceWith(t.stringLiteral(`${name}-${hash}`)) + // } } - path.replaceWith(t.callExpression(identifier, args)) + + path.replaceWith(t.callExpression(identifier, [ + t.arrayExpression([createAstObj(styles, path.node.quasi.expressions, t)]) + ])) } catch (e) { throw path.buildCodeFrameError(e) } @@ -303,34 +288,79 @@ function prefixAst (args, t) { return args } -function objValueToAst (value, t) { +function getDynamicMatches (str) { + const re = /xxx(\d+)xxx/gm + let match + const matches = [] + while ((match = re.exec(str)) !== null) { + matches.push({ + value: match[0], + p1: match[1], + index: match.index + }) + } + return matches +} + +function replacePlaceholdersWithExpressions (matches, str, expressions, t) { + const parts = [] + + forEach(matches, ({ value, p1, index }, i) => { + if (i === 0 && index !== 0) { + parts.push(t.stringLiteral(str.substring(0, i))) + } + + parts.push(expressions[parseInt(p1, 10)]) + if ( + i === matches.length - 1 && + str.substring(index + value.length).length + ) { + parts.push(t.stringLiteral(str.substring(index + value.length))) + } + }) + + return parts.reduce((expr, part, i) => { + if (i === 0) return part + return t.binaryExpression('+', expr, part) + }) +} + +function objKeyToAst (key, expressions, t): { computed: boolean, ast: any } { + const matches = getDynamicMatches(key) + + if (matches.length) { + return { + computed: true, + ast: replacePlaceholdersWithExpressions(matches, key, expressions, t) + } + } + + return { computed: false, ast: t.stringLiteral(key) } +} + +function objValueToAst (value, expressions, t) { if (typeof value === 'string') { + const matches = getDynamicMatches(value) + if (matches.length) { + return replacePlaceholdersWithExpressions(matches, value, expressions, t) + } return t.stringLiteral(value) } else if (Array.isArray(value)) { return t.arrayExpression(value.map(v => objValueToAst(v, t))) } - return createAstObj(value, t) + return createAstObj(value, expressions, t) } -function createAstObj (obj, t) { - console.log(JSON.stringify(obj, null, 2)) +function createAstObj (obj, expressions, t) { + // console.log(JSON.stringify(obj, null, 2)) const props = [] - const computed = false, - shorthand = false, - decorators = [] for (let key in obj) { const rawValue = obj[key] - let computedValue = objValueToAst(rawValue, t) - props.push( - t.objectProperty( - t.stringLiteral(key), - computedValue, - computed, - shorthand, - decorators - ) - ) + const { computed, ast: keyAST } = objKeyToAst(key, expressions, t) + const valueAST = objValueToAst(rawValue, expressions, t) + + props.push(t.objectProperty(keyAST, valueAST, computed)) } return t.objectExpression(props) @@ -349,7 +379,13 @@ export default function (babel) { enter (path, state) { state.inline = path.hub.file.opts.filename === 'unknown' || state.opts.inline + + state.extractStatic = + path.hub.file.opts.filename !== 'unknown' || + state.opts.extractStatic + state.staticRules = [] + state.insertStaticRules = function (staticRules) { state.staticRules.push(...staticRules) } diff --git a/src/inline.js b/src/inline.js index 99b08f3cd..e185c6f47 100644 --- a/src/inline.js +++ b/src/inline.js @@ -25,10 +25,8 @@ function getName ( return parts.join('-') } -function createSrc ( +function createRawStringFromQuasi ( strs: string[], - name: string, - hash: string ): { src: string, dynamicValueCount: number } { let dynamicValueCount = 0 const src = strs @@ -48,34 +46,15 @@ function createSrc ( export function inline ( quasi: any, identifierName?: string, - prefix: string, - inlineMode: boolean ): { isStaticBlock: boolean, - styles: { [string]: any } + styles: { [string]: any }, + composesCount: number } { let strs = quasi.quasis.map(x => x.value.cooked) - let hash = hashArray([...strs]) // todo - add current filename? - let name = getName( - extractNameFromProperty(strs.join('xxx')), - identifierName, - prefix - ) - let { src, dynamicValueCount } = createSrc(strs, name, hash) - let { - styles, - isStaticBlock - } = parseCSS(src) - - console.log('styles', JSON.stringify(styles, null, 2)) - console.log('isStaticBlock', isStaticBlock) - - return { - hash, - name, - isStaticBlock, - styles - } + let { src } = createRawStringFromQuasi(strs) + console.log(src) + return parseCSS(src) } export function keyframes ( @@ -95,14 +74,8 @@ export function keyframes ( identifierName, prefix ) - const { src, dynamicValueCount } = createSrc(strs, name, hash) - let { rules, hasVar, hasOtherMatch } = parseCSS(`{ ${src} }`, { - nested: false, - inlineMode: true, - dynamicValueCount, - name, - hash - }) + const { src, dynamicValueCount } = createRawStringFromQuasi(strs, name, hash) + let { rules, hasVar, hasOtherMatch } = parseCSS(`{ ${src} }`) return { hash, name, rules, hasInterpolation: hasVar || hasOtherMatch } } @@ -110,7 +83,7 @@ export function fontFace ( quasi: any ): { rules: string[], hasInterpolation: boolean } { let strs = quasi.quasis.map(x => x.value.cooked) - const { src, dynamicValueCount } = createSrc(strs, 'name', 'hash') + const { src, dynamicValueCount } = createRawStringFromQuasi(strs, 'name', 'hash') let { rules, hasVar, hasOtherMatch } = parseCSS(`@font-face {${src}}`, { dynamicValueCount: dynamicValueCount, inlineMode: true, @@ -124,7 +97,7 @@ export function injectGlobal ( quasi: any ): { rules: string[], hasInterpolation: boolean } { let strs = quasi.quasis.map(x => x.value.cooked) - const { src, dynamicValueCount } = createSrc(strs, 'name', 'hash') + const { src, dynamicValueCount } = createRawStringFromQuasi(strs, 'name', 'hash') let { rules, hasVar, hasOtherMatch } = parseCSS(src, { dynamicValueCount: dynamicValueCount, inlineMode: true, diff --git a/src/parser.js b/src/parser.js index 927732fee..d0eba22e9 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,4 +1,5 @@ // @flow +import { t } from 'babel-types' import camelizeStyleName from 'fbjs/lib/camelizeStyleName' import prefixAll from 'inline-style-prefixer/static' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' @@ -18,10 +19,11 @@ export function parseCSS ( css: string ): { isStaticBlock: boolean, - styles: { [string]: any } + styles: { [string]: any }, + composesCount: number } { // todo - handle errors - const root = parse(css) + let root = parse(css) let vars = 0 let composes: number = 0 @@ -32,7 +34,6 @@ export function parseCSS ( rule.remove() } - // console.log(JSON.stringify(rule, null, 2)) const { selector } = rule const style = (objStyles[selector] = objStyles[selector] || {}) @@ -71,10 +72,28 @@ export function parseCSS ( }) }) + root.walkDecls((decl: CSSDecl): void => { + if (decl.prop === 'composes') { + if (!/xxx(\d+)xxx/gm.exec(decl.value)) { + throw new Error('composes must be a interpolation') + } + if (decl.parent.nodes[0] !== decl) { + throw new Error('composes must be the first rule') + } + const composeMatches = decl.value.match(/xxx(\d+)xxx/gm) + const numOfComposes: number = !composeMatches ? 0 : composeMatches.length + composes += numOfComposes + vars += numOfComposes + decl.remove() + } + }) + const prefixedObjStyles = prefixAll(objStyles) + console.log(prefixedObjStyles) return { styles: prefixedObjStyles, - isStaticBlock: vars === 0 + isStaticBlock: vars === 0, + composesCount: composes } } diff --git a/src/react/index.js b/src/react/index.js index 30753138c..59f0d9966 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -7,7 +7,7 @@ import { CHANNEL } from './constants' export { flush, css, injectGlobal, fontFace, keyframes, hydrate, objStyle } from '../index' -export default function (tag, cls, vars = [], content) { +export default function (tag, objs) { if (!tag) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' @@ -44,10 +44,14 @@ export default function (tag, cls, vars = [], content) { theme: state.theme } - const getValue = v => - v && typeof v === 'function' ? v.cls || v(mergedProps) : v + const getValue = v => { + console.log(v) + return v && typeof v === 'function' ? v.cls || v(mergedProps) : v + } + - const className = css(map(cls, getValue), map(vars, getValue), content) + const className = css(map(objs, getValue)) + console.log() return h( tag, @@ -70,9 +74,9 @@ export default function (tag, cls, vars = [], content) { [CHANNEL]: PropTypes.object } - const name = typeof cls[0] === 'string' ? cls[0].split('-')[1] : '' + const name = typeof objs[0] === 'string' ? objs[0].split('-')[1] : '' const componentTag = tag.displayName || tag.name || 'Component' Styled.displayName = `styled(${componentTag}${name})` - Styled.cls = '.' + cls + Styled.cls = '.' + objs return Styled } diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 2d77caae7..7af67ac47 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -257,6 +257,12 @@ exports[`styled no dynamic 1`] = `

`; +exports[`styled object composition 1`] = ` + +`; + exports[`styled objects 1`] = ` .css-1viuxsa { padding: 10px; diff --git a/test/extract/extract.test.js b/test/extract/extract.test.js index bddc1c86c..5b5fe0959 100644 --- a/test/extract/extract.test.js +++ b/test/extract/extract.test.js @@ -10,7 +10,7 @@ import styled from '../../src/react' expect.addSnapshotSerializer(serializer) expect.extend(matcher) -describe('styled', () => { +describe.skip('styled', () => { test('no dynamic', () => { const H1 = styled.h1`font-size: 12px;` diff --git a/test/react.test.js b/test/react.test.js index 9ba23b464..895d02b21 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -25,7 +25,7 @@ describe('styled', () => { const H1 = styled.h1` color: blue; - font-size: ${fontSize}px; + font-size: ${fontSize}; ` const tree = renderer.create(

hello world

).toJSON() From 8e535fd7939abd10f6fc36ae2a79143c2fd25c24 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Mon, 17 Jul 2017 00:01:18 -0600 Subject: [PATCH 14/57] Formatting --- src/babel.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/babel.js b/src/babel.js index c7db14ad1..28b38ca69 100644 --- a/src/babel.js +++ b/src/babel.js @@ -110,10 +110,7 @@ function parseDynamicValues (styles, t, options) { export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - const { styles, isStaticBlock } = inline( - path.node.quasi, - identifierName, - ) + const { styles, isStaticBlock } = inline(path.node.quasi, identifierName) console.log(JSON.stringify(styles, null, 2)) @@ -181,9 +178,13 @@ export function replaceCssWithCallExpression (path, identifier, state, t) { // } } - path.replaceWith(t.callExpression(identifier, [ - t.arrayExpression([createAstObj(styles, path.node.quasi.expressions, t)]) - ])) + path.replaceWith( + t.callExpression(identifier, [ + t.arrayExpression([ + createAstObj(styles, path.node.quasi.expressions, t) + ]) + ]) + ) } catch (e) { throw path.buildCodeFrameError(e) } @@ -335,7 +336,11 @@ function objKeyToAst (key, expressions, t): { computed: boolean, ast: any } { } } - return { computed: false, ast: t.stringLiteral(key) } + return { + computed: false, + composes: key === 'composes', + ast: t.stringLiteral(key) + } } function objValueToAst (value, expressions, t) { @@ -355,11 +360,11 @@ function objValueToAst (value, expressions, t) { function createAstObj (obj, expressions, t) { // console.log(JSON.stringify(obj, null, 2)) const props = [] + for (let key in obj) { const rawValue = obj[key] const { computed, ast: keyAST } = objKeyToAst(key, expressions, t) const valueAST = objValueToAst(rawValue, expressions, t) - props.push(t.objectProperty(keyAST, valueAST, computed)) } From 0ad13c8a4c101dbbf999918afafe4f62f74d097d Mon Sep 17 00:00:00 2001 From: mitchellhamilton Date: Mon, 17 Jul 2017 21:40:35 +1000 Subject: [PATCH 15/57] Use postcss-js objectify and change dynamic value parsing --- src/babel.js | 52 +++++++++++-------- src/inline.js | 5 +- src/parser.js | 53 ++------------------ test/babel/__snapshots__/styled.test.js.snap | 21 ++++++-- test/babel/styled.test.js | 18 +++++++ 5 files changed, 75 insertions(+), 74 deletions(-) diff --git a/src/babel.js b/src/babel.js index 28b38ca69..4d9848b22 100644 --- a/src/babel.js +++ b/src/babel.js @@ -9,7 +9,6 @@ import { inline, keyframes, fontFace, injectGlobal } from './inline' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' import createAttrExpression from './attrs' -import * as babylon from 'babylon' function joinExpressionsWithSpaces (expressions, t) { const quasis = [t.templateElement({ cooked: '', raw: '' }, true)] @@ -110,13 +109,21 @@ function parseDynamicValues (styles, t, options) { export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - const { styles, isStaticBlock } = inline(path.node.quasi, identifierName) + const { styles, isStaticBlock, composesCount } = inline(path.node.quasi, identifierName) - console.log(JSON.stringify(styles, null, 2)) + // console.log(JSON.stringify(styles, null, 2)) + + const inputClasses = [] + + for (var i = 0; i < composesCount; i++) { + inputClasses.push(path.node.quasi.expressions.shift()) + } + + inputClasses.push(createAstObj(styles, path.node.quasi.expressions, t)) const args = [ tag, - t.arrayExpression([createAstObj(styles, path.node.quasi.expressions, t)]) + t.arrayExpression(inputClasses) ] if (state.extractStatic && isStaticBlock) { @@ -304,26 +311,31 @@ function getDynamicMatches (str) { } function replacePlaceholdersWithExpressions (matches, str, expressions, t) { - const parts = [] - + const templateElements = [] + const templateExpressions = [] + let cursor = 0 + let hasSingleInterpolation = false forEach(matches, ({ value, p1, index }, i) => { - if (i === 0 && index !== 0) { - parts.push(t.stringLiteral(str.substring(0, i))) + const preMatch = str.substring(cursor, index) + cursor = cursor + preMatch.length + value.length + if (preMatch) { + templateElements.push(t.templateElement({ raw: preMatch, cooked: preMatch })) + } else if (i === 0) { + templateElements.push(t.templateElement({ raw: '', cooked: '' })) } - - parts.push(expressions[parseInt(p1, 10)]) - if ( - i === matches.length - 1 && - str.substring(index + value.length).length - ) { - parts.push(t.stringLiteral(str.substring(index + value.length))) + if (value === str) { + hasSingleInterpolation = true } - }) - return parts.reduce((expr, part, i) => { - if (i === 0) return part - return t.binaryExpression('+', expr, part) + templateExpressions.push(t.identifier(`x${p1}`)) + if (i === matches.length - 1) { + templateElements.push(t.templateElement({ raw: str.substring(index + value.length), cooked: str.substring(index + value.length) }, true)) + } }) + if (hasSingleInterpolation) { + return templateExpressions[0] + } + return t.templateLiteral(templateElements, templateExpressions) } function objKeyToAst (key, expressions, t): { computed: boolean, ast: any } { @@ -351,7 +363,7 @@ function objValueToAst (value, expressions, t) { } return t.stringLiteral(value) } else if (Array.isArray(value)) { - return t.arrayExpression(value.map(v => objValueToAst(v, t))) + return t.arrayExpression(value.map(v => objValueToAst(v, expressions, t))) } return createAstObj(value, expressions, t) diff --git a/src/inline.js b/src/inline.js index e185c6f47..c28e29cca 100644 --- a/src/inline.js +++ b/src/inline.js @@ -26,7 +26,7 @@ function getName ( } function createRawStringFromQuasi ( - strs: string[], + strs: string[] ): { src: string, dynamicValueCount: number } { let dynamicValueCount = 0 const src = strs @@ -45,7 +45,7 @@ function createRawStringFromQuasi ( export function inline ( quasi: any, - identifierName?: string, + identifierName?: string ): { isStaticBlock: boolean, styles: { [string]: any }, @@ -53,7 +53,6 @@ export function inline ( } { let strs = quasi.quasis.map(x => x.value.cooked) let { src } = createRawStringFromQuasi(strs) - console.log(src) return parseCSS(src) } diff --git a/src/parser.js b/src/parser.js index d0eba22e9..9c15fa477 100644 --- a/src/parser.js +++ b/src/parser.js @@ -7,6 +7,7 @@ import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' import autoprefix from 'styled-components/lib/utils/autoprefix' import { objStyle } from './index' +import postcssJs from 'postcss-js' type CSSDecl = { parent: { selector: string, nodes: Array }, @@ -27,51 +28,6 @@ export function parseCSS ( let vars = 0 let composes: number = 0 - const objStyles = {} - - root.walkRules((rule, i) => { - if (rule.nodes.length === 0) { - rule.remove() - } - - const { selector } = rule - const style = (objStyles[selector] = objStyles[selector] || {}) - - rule.walkDecls((decl: CSSDecl): void => { - const match = /xxx(\d+)xxx/gm.exec(decl.value) - if (match) { - vars++ - } - - style[camelizeStyleName(decl.prop)] = decl.value - }) - }) - - root.walkAtRules((atRule, i) => { - const { name, params } = atRule - const key = `@${name} ${params}`.trim() - const atRuleStyle = (objStyles[key] = objStyles[key] || {}) - - atRule.walkRules((rule, i) => { - if (rule.nodes.length === 0) { - rule.remove() - } - - // console.log(JSON.stringify(rule, null, 2)) - const { selector } = rule - const style = (atRuleStyle[selector] = atRuleStyle[selector] || {}) - - rule.walkDecls((decl: CSSDecl): void => { - const match = /xxx(\d+)xxx/gm.exec(decl.value) - if (match) { - vars++ - } - - style[camelizeStyleName(decl.prop)] = decl.value - }) - }) - }) - root.walkDecls((decl: CSSDecl): void => { if (decl.prop === 'composes') { if (!/xxx(\d+)xxx/gm.exec(decl.value)) { @@ -88,11 +44,12 @@ export function parseCSS ( } }) - const prefixedObjStyles = prefixAll(objStyles) - console.log(prefixedObjStyles) + autoprefix(root) + + const styles = postcssJs.objectify(root) return { - styles: prefixedObjStyles, + styles, isStaticBlock: vars === 0, composesCount: composes } diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 456f71b18..d3855a02f 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -128,6 +128,21 @@ exports[`babel styled component inline function call 1`] = ` });" `; +exports[`babel styled component inline interpolation in different places 1`] = ` +" +const H1 = styled('h1', [{ + 'fontSize': x0, + 'height': '20px', + 'WebkitTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], + 'msTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], + 'transform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], + 'height1': \`\${x2}wow\`, + 'width': \`w\${x3}ow\`, + 'transform1': \`translateX(\${x6}) translateY(\${x7})\`, + 'transform2': \`translateX(\${x8}) translateY(\${x9}\` +}]);" +`; + exports[`babel styled component inline lots of attrs with interpolated values 1`] = ` "styled('input', ['css-1kdt7pt'], [marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, displayProp, function getProp(props) { return props[marginProp]; @@ -230,9 +245,9 @@ css([\\"css-1cppx8p\\"], [], function createEmotionRules() { `; exports[`babel styled component inline no dynamic 1`] = ` -"styled(\\"h1\\", [\\"css-14ksm7b\\"], [], function createEmotionStyledRules() { - return [\`.css-14ksm7b { color: blue; }\`]; -});" +"styled(\\"h1\\", [{ + \\"color\\": \\"blue\\" +}]);" `; exports[`babel styled component inline no use 1`] = `"\\"h1\\";"`; diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index 22f46df17..6bbd5aaaf 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -48,6 +48,24 @@ describe('babel styled component', () => { expect(code).toMatchSnapshot() }) + test.only('interpolation in different places', () => { + const basic = ` + const H1 = styled.h1\` + font-size: \${fontSize + 'px'}; + height: 20px; + transform: translateX(\${(props) => props.translateX}); + height1: \${something}wow; + width: w\${something}ow; + transform: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + transform1: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + transform2: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}; + \`` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + test('media query', () => { const basic = 'const H1 = styled.h1`@media print {' + From 68ad756f8004905fd649776a6c32b456bdea471c Mon Sep 17 00:00:00 2001 From: mitchellhamilton Date: Mon, 17 Jul 2017 22:39:49 +1000 Subject: [PATCH 16/57] I broke css but styled is great now --- src/babel.js | 42 ++++++++++++++++++++++----------------- src/index.js | 25 ++--------------------- src/react/index.js | 7 ++----- test/babel/css.test.js | 2 +- test/babel/styled.test.js | 2 +- 5 files changed, 30 insertions(+), 48 deletions(-) diff --git a/src/babel.js b/src/babel.js index 4d9848b22..3cc842140 100644 --- a/src/babel.js +++ b/src/babel.js @@ -119,11 +119,16 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { inputClasses.push(path.node.quasi.expressions.shift()) } - inputClasses.push(createAstObj(styles, path.node.quasi.expressions, t)) + inputClasses.push(createAstObj(styles, false, composesCount, t)) const args = [ tag, - t.arrayExpression(inputClasses) + t.arrayExpression(path.node.quasi.expressions), + t.functionExpression( + t.identifier('createEmotionStyledRules'), + path.node.quasi.expressions.map((x, i) => t.identifier(`x${i}`)), + t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) + ) ] if (state.extractStatic && isStaticBlock) { @@ -173,7 +178,7 @@ export function replaceGlobalWithCallExpression ( export function replaceCssWithCallExpression (path, identifier, state, t) { try { - const { styles, isStaticBlock } = inline( + const { styles, isStaticBlock, composesCount } = inline( path.node.quasi, getIdentifierName(path, t) ) @@ -184,11 +189,12 @@ export function replaceCssWithCallExpression (path, identifier, state, t) { // return path.replaceWith(t.stringLiteral(`${name}-${hash}`)) // } } - - path.replaceWith( + const thing = createAstObj(styles, path.node.quasi.expressions, composesCount, t) + console.log(thing) + return path.replaceWith( t.callExpression(identifier, [ t.arrayExpression([ - createAstObj(styles, path.node.quasi.expressions, t) + thing ]) ]) ) @@ -310,7 +316,7 @@ function getDynamicMatches (str) { return matches } -function replacePlaceholdersWithExpressions (matches, str, expressions, t) { +function replacePlaceholdersWithExpressions (matches: any[], str: string, expressions?: any[], composesCount, t) { const templateElements = [] const templateExpressions = [] let cursor = 0 @@ -327,7 +333,7 @@ function replacePlaceholdersWithExpressions (matches, str, expressions, t) { hasSingleInterpolation = true } - templateExpressions.push(t.identifier(`x${p1}`)) + templateExpressions.push(expressions ? expressions[p1 - composesCount] : t.identifier(`x${p1 - composesCount}`)) if (i === matches.length - 1) { templateElements.push(t.templateElement({ raw: str.substring(index + value.length), cooked: str.substring(index + value.length) }, true)) } @@ -338,13 +344,13 @@ function replacePlaceholdersWithExpressions (matches, str, expressions, t) { return t.templateLiteral(templateElements, templateExpressions) } -function objKeyToAst (key, expressions, t): { computed: boolean, ast: any } { +function objKeyToAst (key, expressions, composesCount: number, t): { computed: boolean, ast: any } { const matches = getDynamicMatches(key) if (matches.length) { return { computed: true, - ast: replacePlaceholdersWithExpressions(matches, key, expressions, t) + ast: replacePlaceholdersWithExpressions(matches, key, expressions, composesCount, t) } } @@ -355,31 +361,31 @@ function objKeyToAst (key, expressions, t): { computed: boolean, ast: any } { } } -function objValueToAst (value, expressions, t) { +function objValueToAst (value, expressions, composesCount, t) { if (typeof value === 'string') { const matches = getDynamicMatches(value) if (matches.length) { - return replacePlaceholdersWithExpressions(matches, value, expressions, t) + return replacePlaceholdersWithExpressions(matches, value, expressions, composesCount, t) } return t.stringLiteral(value) } else if (Array.isArray(value)) { - return t.arrayExpression(value.map(v => objValueToAst(v, expressions, t))) + return t.arrayExpression(value.map(v => objValueToAst(v, expressions, composesCount, t))) } - return createAstObj(value, expressions, t) + return createAstObj(value, expressions, composesCount, t) } -function createAstObj (obj, expressions, t) { +function createAstObj (obj, expressions, composesCount, t) { // console.log(JSON.stringify(obj, null, 2)) const props = [] for (let key in obj) { const rawValue = obj[key] - const { computed, ast: keyAST } = objKeyToAst(key, expressions, t) - const valueAST = objValueToAst(rawValue, expressions, t) + const { computed, ast: keyAST } = objKeyToAst(key, expressions, composesCount, t) + const valueAST = objValueToAst(rawValue, expressions, composesCount, t) props.push(t.objectProperty(keyAST, valueAST, computed)) } - + console.log(props) return t.objectExpression(props) } diff --git a/src/index.js b/src/index.js index 20e6b1403..2fb586254 100644 --- a/src/index.js +++ b/src/index.js @@ -15,23 +15,6 @@ type inputVar = string | number type vars = Array -function values (cls: string, vars: vars) { - const hash = hashArray([cls, ...vars]) - const varCls = `vars-${hash}` - if (inserted[hash]) { - return varCls - } - let src = '' - forEach(vars, (val: inputVar, i: number) => { - src && (src += '; ') - src += `--${cls}-${i}: ${val}` - }) - sheet.insert(`.${varCls} {${src}}`) - inserted[hash] = true - - return varCls -} - export function flush () { sheet.flush() inserted = {} @@ -72,7 +55,7 @@ function getGlamorStylesFromClassName (className) { } } -export function css (classes: string[], vars: vars) { +export function css (classes: any) { if (!Array.isArray(classes)) { classes = [classes] } @@ -98,10 +81,7 @@ export function css (classes: string[], vars: vars) { computedClassName += ' ' + objStyle(...objectStyles).toString() } - return ( - computedClassName + - (vars && vars.length > 0 ? ' ' + values(classes[0], vars) : '') - ) + return computedClassName } export function injectGlobal (src: string[]) { @@ -491,4 +471,3 @@ function multiIndexCache (fn) { return value } } - diff --git a/src/react/index.js b/src/react/index.js index 59f0d9966..41c6c2e9c 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -7,7 +7,7 @@ import { CHANNEL } from './constants' export { flush, css, injectGlobal, fontFace, keyframes, hydrate, objStyle } from '../index' -export default function (tag, objs) { +export default function (tag, vars, content) { if (!tag) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' @@ -45,13 +45,10 @@ export default function (tag, objs) { } const getValue = v => { - console.log(v) return v && typeof v === 'function' ? v.cls || v(mergedProps) : v } - - const className = css(map(objs, getValue)) - console.log() + const className = css(content(map(vars, getValue))) return h( tag, diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 56911a193..7791f4e7e 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -8,7 +8,7 @@ fs.existsSync.mockReturnValue(true) describe('babel css', () => { describe('inline', () => { - test('css basic', () => { + test.only('css basic', () => { const basic = ` css\` margin: 12px 48px; diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index 6bbd5aaaf..489372f0d 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -48,7 +48,7 @@ describe('babel styled component', () => { expect(code).toMatchSnapshot() }) - test.only('interpolation in different places', () => { + test('interpolation in different places', () => { const basic = ` const H1 = styled.h1\` font-size: \${fontSize + 'px'}; From e32b77ccb1732b9c7a283a1cb3fd854c01b95a46 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Mon, 17 Jul 2017 12:44:54 -0600 Subject: [PATCH 17/57] ITS WORKING --- src/babel.js | 142 +++++++++++++++++++++++++++++++++------------ src/index.js | 59 ++++++++++++------- src/react/index.js | 13 +++-- test/vue.test.js | 2 +- 4 files changed, 151 insertions(+), 65 deletions(-) diff --git a/src/babel.js b/src/babel.js index 3cc842140..8e5d6b1dc 100644 --- a/src/babel.js +++ b/src/babel.js @@ -107,9 +107,57 @@ function parseDynamicValues (styles, t, options) { }) } +export function replaceCssWithCallExpression (path, identifier, state, t) { + try { + const { styles, isStaticBlock, composesCount } = inline( + path.node.quasi, + getIdentifierName(path, t) + ) + + const inputClasses = [] + + for (var i = 0; i < composesCount; i++) { + inputClasses.push(path.node.quasi.expressions.shift()) + } + + inputClasses.push(createAstObj(styles, false, composesCount, t)) + + const thing = createAstObj( + styles, + path.node.quasi.expressions, + composesCount, + t + ) + + // console.log(thing) + if (state.extractStatic && isStaticBlock) { + // state.insertStaticRules(rules) + // if (!hasVar) { + // return path.replaceWith(t.stringLiteral(`${name}-${hash}`)) + // } + } + return path.replaceWith( + t.callExpression(identifier, [ + t.arrayExpression([]), + t.arrayExpression(path.node.quasi.expressions), + t.functionExpression( + t.identifier('createEmotionStyledRules'), + path.node.quasi.expressions.map((x, i) => t.identifier(`x${i}`)), + t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) + ) + ]) + ) + } catch (e) { + throw path.buildCodeFrameError(e) + } +} + export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - const { styles, isStaticBlock, composesCount } = inline(path.node.quasi, identifierName) + const { styles, isStaticBlock, composesCount } = inline( + path.node.quasi, + identifierName + ) // console.log(JSON.stringify(styles, null, 2)) @@ -123,6 +171,7 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { const args = [ tag, + t.arrayExpression([]), t.arrayExpression(path.node.quasi.expressions), t.functionExpression( t.identifier('createEmotionStyledRules'), @@ -176,33 +225,6 @@ export function replaceGlobalWithCallExpression ( } } -export function replaceCssWithCallExpression (path, identifier, state, t) { - try { - const { styles, isStaticBlock, composesCount } = inline( - path.node.quasi, - getIdentifierName(path, t) - ) - - if (state.extractStatic && isStaticBlock) { - // state.insertStaticRules(rules) - // if (!hasVar) { - // return path.replaceWith(t.stringLiteral(`${name}-${hash}`)) - // } - } - const thing = createAstObj(styles, path.node.quasi.expressions, composesCount, t) - console.log(thing) - return path.replaceWith( - t.callExpression(identifier, [ - t.arrayExpression([ - thing - ]) - ]) - ) - } catch (e) { - throw path.buildCodeFrameError(e) - } -} - export function replaceKeyframesWithCallExpression (path, identifier, state, t) { const { hash, name, rules, hasInterpolation } = keyframes( path.node.quasi, @@ -316,7 +338,13 @@ function getDynamicMatches (str) { return matches } -function replacePlaceholdersWithExpressions (matches: any[], str: string, expressions?: any[], composesCount, t) { +function replacePlaceholdersWithExpressions ( + matches: any[], + str: string, + expressions?: any[], + composesCount, + t +) { const templateElements = [] const templateExpressions = [] let cursor = 0 @@ -325,7 +353,9 @@ function replacePlaceholdersWithExpressions (matches: any[], str: string, expres const preMatch = str.substring(cursor, index) cursor = cursor + preMatch.length + value.length if (preMatch) { - templateElements.push(t.templateElement({ raw: preMatch, cooked: preMatch })) + templateElements.push( + t.templateElement({ raw: preMatch, cooked: preMatch }) + ) } else if (i === 0) { templateElements.push(t.templateElement({ raw: '', cooked: '' })) } @@ -333,9 +363,21 @@ function replacePlaceholdersWithExpressions (matches: any[], str: string, expres hasSingleInterpolation = true } - templateExpressions.push(expressions ? expressions[p1 - composesCount] : t.identifier(`x${p1 - composesCount}`)) + templateExpressions.push( + expressions + ? expressions[p1 - composesCount] + : t.identifier(`x${p1 - composesCount}`) + ) if (i === matches.length - 1) { - templateElements.push(t.templateElement({ raw: str.substring(index + value.length), cooked: str.substring(index + value.length) }, true)) + templateElements.push( + t.templateElement( + { + raw: str.substring(index + value.length), + cooked: str.substring(index + value.length) + }, + true + ) + ) } }) if (hasSingleInterpolation) { @@ -344,13 +386,24 @@ function replacePlaceholdersWithExpressions (matches: any[], str: string, expres return t.templateLiteral(templateElements, templateExpressions) } -function objKeyToAst (key, expressions, composesCount: number, t): { computed: boolean, ast: any } { +function objKeyToAst ( + key, + expressions, + composesCount: number, + t +): { computed: boolean, ast: any } { const matches = getDynamicMatches(key) if (matches.length) { return { computed: true, - ast: replacePlaceholdersWithExpressions(matches, key, expressions, composesCount, t) + ast: replacePlaceholdersWithExpressions( + matches, + key, + expressions, + composesCount, + t + ) } } @@ -365,11 +418,19 @@ function objValueToAst (value, expressions, composesCount, t) { if (typeof value === 'string') { const matches = getDynamicMatches(value) if (matches.length) { - return replacePlaceholdersWithExpressions(matches, value, expressions, composesCount, t) + return replacePlaceholdersWithExpressions( + matches, + value, + expressions, + composesCount, + t + ) } return t.stringLiteral(value) } else if (Array.isArray(value)) { - return t.arrayExpression(value.map(v => objValueToAst(v, expressions, composesCount, t))) + return t.arrayExpression( + value.map(v => objValueToAst(v, expressions, composesCount, t)) + ) } return createAstObj(value, expressions, composesCount, t) @@ -381,11 +442,16 @@ function createAstObj (obj, expressions, composesCount, t) { for (let key in obj) { const rawValue = obj[key] - const { computed, ast: keyAST } = objKeyToAst(key, expressions, composesCount, t) + const { computed, ast: keyAST } = objKeyToAst( + key, + expressions, + composesCount, + t + ) const valueAST = objValueToAst(rawValue, expressions, composesCount, t) props.push(t.objectProperty(keyAST, valueAST, computed)) } - console.log(props) + // console.log(props) return t.objectExpression(props) } diff --git a/src/index.js b/src/index.js index 2fb586254..281c00b6e 100644 --- a/src/index.js +++ b/src/index.js @@ -46,7 +46,7 @@ function _getRegistered (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 getGlamorStylesFromClassName (className) { +function getEmotionStylesFromClassName (className) { const id = className.trim().slice('css-obj-'.length) if (sheet.registered[id]) { return sheet.registered[id].style @@ -55,33 +55,52 @@ function getGlamorStylesFromClassName (className) { } } -export function css (classes: any) { - if (!Array.isArray(classes)) { - classes = [classes] - } - +function buildStyles (objs) { let computedClassName = '' let objectStyles = [] - forEach(classes, (cls): void => { - computedClassName && (computedClassName += ' ') + console.log(JSON.stringify(objs, null, 2)) + + // This needs to be moved into the core + forEach(objs, (cls): void => { + computedClassName && (computedClassName += ' ') + console.log('cls', cls) if (typeof cls === 'string') { if (cls.trim().indexOf('css-obj-') === 0) { - const glamorStylesFromClassName = getGlamorStylesFromClassName(cls) - objectStyles.push(glamorStylesFromClassName) + console.log('emotion style detected', cls) + const emotionStylesFromClassName = getEmotionStylesFromClassName(cls) + objectStyles.push(emotionStylesFromClassName) } else { computedClassName += cls } + } else if(Array.isArray(cls)) { + console.log('cls is an array') } else { objectStyles.push(cls) } }) + return { computedClassName, objectStyles } +} + +export function css (objs: any, vars: Array, content: () => Array) { + if (!Array.isArray(objs)) { + objs = [objs] + } + + let { computedClassName = '', objectStyles = [] } = buildStyles(objs) + + if (content) { + objectStyles.push(content.apply(null, vars)) + } + + console.log('objectStyles', JSON.stringify(objectStyles, null, 2)) + if (objectStyles.length) { - computedClassName += ' ' + objStyle(...objectStyles).toString() + computedClassName += ' ' + objStyle.apply(null, objectStyles).toString() } - return computedClassName + return computedClassName.trim() } export function injectGlobal (src: string[]) { @@ -110,20 +129,20 @@ export function hydrate (ids: string[]) { // 🍩 // https://github.com/jxnblk/cxs/blob/master/src/monolithic/index.js -type GlamorRule = { [string]: any } +type EmotionRule = { [string]: any } -type CSSRuleList = Array +type CSSRuleList = Array -type GlamorClassName = { +type EmotionClassName = { [string]: any } -let cachedCss: (rules: CSSRuleList) => GlamorClassName = typeof WeakMap !== +let cachedCss: (rules: CSSRuleList) => EmotionClassName = typeof WeakMap !== 'undefined' ? multiIndexCache(_css) : _css -export function objStyle (...rules: CSSRuleList): GlamorClassName { +export function objStyle (...rules: CSSRuleList): EmotionClassName { rules = clean(rules) if (!rules) { return nullrule @@ -155,7 +174,7 @@ function simple (str) { } // of shape { 'data-css-': '' } -export function isLikeRule (rule: GlamorRule) { +export function isLikeRule (rule: EmotionRule) { let keys = Object.keys(rule).filter(x => x !== 'toString') if (keys.length !== 1) { return false @@ -164,7 +183,7 @@ export function isLikeRule (rule: GlamorRule) { } // extracts id from a { 'css-': ''} like object -export function idFor (rule: GlamorRule) { +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\-obj\-([a-zA-Z0-9]+)/ @@ -410,7 +429,7 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { }) } -let nullrule: GlamorClassName = { +let nullrule: EmotionClassName = { // 'data-css-nil': '' } diff --git a/src/react/index.js b/src/react/index.js index 41c6c2e9c..d1b81f44d 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -7,13 +7,15 @@ import { CHANNEL } from './constants' export { flush, css, injectGlobal, fontFace, keyframes, hydrate, objStyle } from '../index' -export default function (tag, vars, content) { +export default function (tag, objs, vars, content) { if (!tag) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' ) } + const componentTag = tag.displayName || tag.name || 'Component' + class Styled extends Component { state = { theme: {} @@ -48,7 +50,7 @@ export default function (tag, vars, content) { return v && typeof v === 'function' ? v.cls || v(mergedProps) : v } - const className = css(content(map(vars, getValue))) + const className = `${css(map(objs, getValue), map(vars, getValue), content)}` return h( tag, @@ -71,9 +73,8 @@ export default function (tag, vars, content) { [CHANNEL]: PropTypes.object } - const name = typeof objs[0] === 'string' ? objs[0].split('-')[1] : '' - const componentTag = tag.displayName || tag.name || 'Component' - Styled.displayName = `styled(${componentTag}${name})` - Styled.cls = '.' + objs + + Styled.displayName = `styled(${componentTag})` + Styled.cls = '.' + componentTag return Styled } diff --git a/test/vue.test.js b/test/vue.test.js index e88ad9349..1774aa3ba 100644 --- a/test/vue.test.js +++ b/test/vue.test.js @@ -48,7 +48,7 @@ Vue.component('based-on-props-composes', basedOnPropsComposes) Vue.component('non-html-component', NonHtmlComponent) Vue.component('child-selector', ChildSelector) -describe('vue styled', () => { +describe.skip('vue styled', () => { test('basic', async () => { document.body.innerHTML = `
From e52a61a7525e50cce2b36228000a1df479999102 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Mon, 17 Jul 2017 13:36:58 -0600 Subject: [PATCH 18/57] more progress --- example/.babelrc | 3 +- example/package.json | 1 + example/src/main.js | 93 +------------------- src/babel.js | 11 ++- src/index.js | 18 ++-- src/react/index.js | 37 ++++++-- test/babel/__snapshots__/styled.test.js.snap | 4 +- 7 files changed, 58 insertions(+), 109 deletions(-) diff --git a/example/.babelrc b/example/.babelrc index 373725d3f..2215504c9 100644 --- a/example/.babelrc +++ b/example/.babelrc @@ -11,6 +11,7 @@ "react" ], "plugins": [ - ['emotion/babel', { inline: true }] + ['emotion/babel'], + "transform-decorators-legacy" ] } diff --git a/example/package.json b/example/package.json index b3a951f7a..c76225543 100755 --- a/example/package.json +++ b/example/package.json @@ -7,6 +7,7 @@ "build": "webpack -p --env production --progress" }, "dependencies": { + "babel-plugin-transform-decorators-legacy": "^1.3.4", "open-color": "^1.5.1", "react": "^15.5.4", "react-dom": "^15.5.4", diff --git a/example/src/main.js b/example/src/main.js index b7f39686a..82b8a7f74 100755 --- a/example/src/main.js +++ b/example/src/main.js @@ -1,94 +1,11 @@ import React from 'react' import { render } from 'react-dom' import { ThemeProvider } from 'emotion/react/theming' -import styled, { css, fontFace, keyframes, injectGlobal } from 'emotion/react' +import styled, { css } from 'emotion/react' import Markdown from './markdown' import Playground from './playground' import logoUrl from '../../emotion.png' -const introExample = require('./blocks/intro.example') -const propsExample = require('./blocks/props.example') -const nestedExample = require('./blocks/nested.example') -const mediaExample = require('./blocks/media.example') -const anyComponentExample = require('./blocks/styling-any-component.example') -const namedExample = require('./blocks/named.example') -const pseudoExample = require('./blocks/pseudo.example') -const keyframesExample = require('./blocks/keyframes.example') -// const fontFaceExample = require('./blocks/font-face.example') -// const docMarkdown = require('./docs/index.md') -// const readmeMarkdown = require('../../README.md') -const avatarUrl = require('../../emotion.png') - - -injectGlobal` - html, body { - font-family: -apple-system, - BlinkMacSystemFont, - "Segoe UI", - "Roboto", - "Roboto Light", - "Oxygen", - "Ubuntu", - "Cantarell", - "Fira Sans", - "Droid Sans", - "Helvetica Neue", - sans-serif, - "Apple Color Emoji", - "Segoe UI Emoji", - "Segoe UI Symbol"; - color: #495057; - width: 100%; - height: 100%; - padding: 0; - margin: 0; - } -` - -fontFace` - font-family: 'Oxygen'; - font-style: normal; - font-weight: 400; - src: local('Oxygen Regular'), local('Oxygen-Regular'), url(https://fonts.gstatic.com/s/oxygen/v6/qBSyz106i5ud7wkBU-FrPevvDin1pK8aKteLpeZ5c0A.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; -` - -const theme = { - white: '#f8f9fa', - purple: '#8c81d8', - gold: '#ffd43b' -} - -const PlaygroundWrapper = styled('div')` - font-family: 'Oxygen', sans-serif; - flex:1; - color: attr(color, #343a40); - background: #f8f9fa; - - & .inner { - margin: 0 auto; - width: calc(100% - 32px); - max-width: 960px; - - @media (min-width: 960px) { - width: 100%; - } - } - - & .header { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 100%; - - & img { - display: block; - width: 128px; - height: 128px; - } - } -` const cssA = css` color: green; @@ -123,13 +40,7 @@ const FinalH2 = styled(BlueH1)` class App extends React.Component { render () { return ( - - - - Hello - - - + Hello ) } } diff --git a/src/babel.js b/src/babel.js index 8e5d6b1dc..c12b4d34c 100644 --- a/src/babel.js +++ b/src/babel.js @@ -442,13 +442,20 @@ function createAstObj (obj, expressions, composesCount, t) { for (let key in obj) { const rawValue = obj[key] - const { computed, ast: keyAST } = objKeyToAst( + const { computed, composes, ast: keyAST } = objKeyToAst( key, expressions, composesCount, t ) - const valueAST = objValueToAst(rawValue, expressions, composesCount, t) + + let valueAST + if (composes) { + valueAST = t.arrayExpression(expressions.slice(0, composesCount)) + } else { + valueAST = objValueToAst(rawValue, expressions, composesCount, t) + } + props.push(t.objectProperty(keyAST, valueAST, computed)) } // console.log(props) diff --git a/src/index.js b/src/index.js index 281c00b6e..d5bfdd193 100644 --- a/src/index.js +++ b/src/index.js @@ -51,7 +51,7 @@ function getEmotionStylesFromClassName (className) { if (sheet.registered[id]) { return sheet.registered[id].style } else { - return null + return [] } } @@ -59,7 +59,7 @@ function buildStyles (objs) { let computedClassName = '' let objectStyles = [] - console.log(JSON.stringify(objs, null, 2)) + console.log('objs', JSON.stringify(objs, null, 2)) // This needs to be moved into the core forEach(objs, (cls): void => { @@ -69,14 +69,15 @@ function buildStyles (objs) { if (cls.trim().indexOf('css-obj-') === 0) { console.log('emotion style detected', cls) const emotionStylesFromClassName = getEmotionStylesFromClassName(cls) + console.log('emotionStylesFromClassName', emotionStylesFromClassName) objectStyles.push(emotionStylesFromClassName) } else { computedClassName += cls } - } else if(Array.isArray(cls)) { - console.log('cls is an array') } else { - objectStyles.push(cls) + objectStyles.push( + cls + ) } }) @@ -361,9 +362,15 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { } _src = clean(_src) if (_src && _src.composes) { + console.log('found composes') build(dest, { selector, mq, supp, src: _src.composes }) } Object.keys(_src || {}).forEach(key => { + if (key.indexOf('css') === 0) { + console.log('fuck') + key = '.' + key + } + if (isSelector(key)) { if (key === '::placeholder') { build(dest, { @@ -408,6 +415,7 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { }) } else if (key === 'composes') { // ignore, we already dealth with it + console.log('key === composes') } else { let _dest = dest if (supp) { diff --git a/src/react/index.js b/src/react/index.js index d1b81f44d..c3fc65bc9 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -5,7 +5,15 @@ import { css } from '../index' import { omit } from '../utils' import { CHANNEL } from './constants' -export { flush, css, injectGlobal, fontFace, keyframes, hydrate, objStyle } from '../index' +export { + flush, + css, + injectGlobal, + fontFace, + keyframes, + hydrate, + objStyle +} from '../index' export default function (tag, objs, vars, content) { if (!tag) { @@ -47,19 +55,29 @@ export default function (tag, objs, vars, content) { } const getValue = v => { - return v && typeof v === 'function' ? v.cls || v(mergedProps) : v + return v && typeof v === 'function' + ? (v.__emotion_spec && + css( + map(v.__emotion_spec.objs, getValue), + map(v.__emotion_spec.vars, getValue), + v.__emotion_spec.content + )) || + v(mergedProps) + : v } - const className = `${css(map(objs, getValue), map(vars, getValue), content)}` + const className = `${css( + map(objs.concat(mergedProps.className.split(' ')), getValue), + map(vars, getValue), + content + )}` return h( tag, omit( Object.assign({}, mergedProps, { ref: mergedProps.innerRef, - className: mergedProps.className - ? className + ' ' + mergedProps.className - : className + className }), ['innerRef', 'theme'] ) @@ -73,8 +91,11 @@ export default function (tag, objs, vars, content) { [CHANNEL]: PropTypes.object } - Styled.displayName = `styled(${componentTag})` - Styled.cls = '.' + componentTag + Styled.__emotion_spec = { + vars, + content, + objs + } return Styled } diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index d3855a02f..cfe169922 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -307,7 +307,7 @@ const H1 = styled(\\"h1\\", [{ padding: \\"10px\\" }, props => ({ display: props.display -})], []);" +})]);" `; exports[`babel styled component inline styled. objects prefixed 1`] = ` @@ -325,5 +325,5 @@ const H1 = styled('h1', [{ } }, props => ({ display: props.display -})], []);" +})]);" `; From 2cd0f5c5ce109953c1c8f6a66a9f7e647d373b87 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger Date: Mon, 17 Jul 2017 22:09:49 -0600 Subject: [PATCH 19/57] working setup --- example/.babelrc | 3 +- example/package.json | 1 - src/attrs.js | 59 -------- src/babel.js | 1 - src/index.js | 43 +++--- src/react/index.js | 2 +- src/vue/index.js | 28 ---- src/vue/macro.js | 1 - test/__snapshots__/react.test.js.snap | 18 +-- test/__snapshots__/vue.test.js.snap | 63 --------- test/babel/styled.test.js | 101 -------------- test/extract/extract.test.js | 26 ---- test/macro/__snapshots__/vue.test.js.snap | 63 --------- test/macro/react.test.js | 22 +-- test/macro/vue.test.js | 161 ---------------------- test/react.test.js | 26 +--- test/server.test.js | 4 +- test/vue.test.js | 161 ---------------------- 18 files changed, 28 insertions(+), 755 deletions(-) delete mode 100644 src/attrs.js delete mode 100644 src/vue/index.js delete mode 100644 src/vue/macro.js delete mode 100644 test/__snapshots__/vue.test.js.snap delete mode 100644 test/macro/__snapshots__/vue.test.js.snap delete mode 100644 test/macro/vue.test.js delete mode 100644 test/vue.test.js diff --git a/example/.babelrc b/example/.babelrc index 2215504c9..c287bec0f 100644 --- a/example/.babelrc +++ b/example/.babelrc @@ -11,7 +11,6 @@ "react" ], "plugins": [ - ['emotion/babel'], - "transform-decorators-legacy" + ['emotion/babel'] ] } diff --git a/example/package.json b/example/package.json index c76225543..b3a951f7a 100755 --- a/example/package.json +++ b/example/package.json @@ -7,7 +7,6 @@ "build": "webpack -p --env production --progress" }, "dependencies": { - "babel-plugin-transform-decorators-legacy": "^1.3.4", "open-color": "^1.5.1", "react": "^15.5.4", "react-dom": "^15.5.4", diff --git a/src/attrs.js b/src/attrs.js deleted file mode 100644 index 4a1f0d323..000000000 --- a/src/attrs.js +++ /dev/null @@ -1,59 +0,0 @@ -export default function createAttrExpression (match, vars, composes, t) { - const placeholderRegex = /xxx(\d+)xxx/gm - const propNameMatch = placeholderRegex.exec(match.propName) - let propName = t.identifier(match.propName) - if (propNameMatch) { - const varsIndex = propNameMatch[1] - composes - propName = vars[varsIndex] - } - - const valueTypeMatch = placeholderRegex.exec(match.valueType) - let valueType = t.stringLiteral(match.valueType || '') - if (valueTypeMatch) { - const varsIndex = valueTypeMatch[1] - composes - valueType = vars[varsIndex] - } - - const defaultValueMatch = placeholderRegex.exec(match.defaultValue) - let defaultValue = t.stringLiteral(match.defaultValue || '') - if (defaultValueMatch) { - const varsIndex = defaultValueMatch[1] - composes - defaultValue = vars[varsIndex] - } - - let createMemberExpression = () => - t.memberExpression(t.identifier('props'), propName, !!propNameMatch) - - let returnValue = createMemberExpression() - - if (match.valueType) { - returnValue = t.binaryExpression( - '+', - createMemberExpression(), - valueType - ) - } - - if (match.defaultValue) { - returnValue = t.binaryExpression( - '+', - t.conditionalExpression( - createMemberExpression(), - createMemberExpression(), - defaultValue - ), - valueType - ) - } - - const body = t.blockStatement([t.returnStatement(returnValue)]) - - const expr = t.functionExpression( - t.identifier( - `get${propNameMatch ? 'Prop' : match.propName.charAt(0).toUpperCase() + match.propName.slice(1)}` - ), - [t.identifier('props')], - body - ) - return expr -} diff --git a/src/babel.js b/src/babel.js index c12b4d34c..00a22412c 100644 --- a/src/babel.js +++ b/src/babel.js @@ -8,7 +8,6 @@ import forEach from '@arr/foreach' import { inline, keyframes, fontFace, injectGlobal } from './inline' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' -import createAttrExpression from './attrs' function joinExpressionsWithSpaces (expressions, t) { const quasis = [t.templateElement({ cooked: '', raw: '' }, true)] diff --git a/src/index.js b/src/index.js index d5bfdd193..231ffc2e2 100644 --- a/src/index.js +++ b/src/index.js @@ -47,7 +47,7 @@ function _getRegistered (rule) { // 💄 // https://github.com/paypal/glamorous/blob/master/src/get-glamor-classname.js function getEmotionStylesFromClassName (className) { - const id = className.trim().slice('css-obj-'.length) + const id = className.trim().slice('css-'.length) if (sheet.registered[id]) { return sheet.registered[id].style } else { @@ -59,22 +59,20 @@ function buildStyles (objs) { let computedClassName = '' let objectStyles = [] - console.log('objs', JSON.stringify(objs, null, 2)) - // This needs to be moved into the core forEach(objs, (cls): void => { computedClassName && (computedClassName += ' ') - console.log('cls', cls) if (typeof cls === 'string') { - if (cls.trim().indexOf('css-obj-') === 0) { - console.log('emotion style detected', cls) - const emotionStylesFromClassName = getEmotionStylesFromClassName(cls) - console.log('emotionStylesFromClassName', emotionStylesFromClassName) - objectStyles.push(emotionStylesFromClassName) + if (cls.trim().indexOf('css-') === 0) { + objectStyles.push(getEmotionStylesFromClassName(cls)) } else { computedClassName += cls } } else { + if (Array.isArray(cls)) { + console.log('cls is an array') + } + console.log('was a pure object') objectStyles.push( cls ) @@ -89,14 +87,7 @@ export function css (objs: any, vars: Array, content: () => Array) { objs = [objs] } - let { computedClassName = '', objectStyles = [] } = buildStyles(objs) - - if (content) { - objectStyles.push(content.apply(null, vars)) - } - - console.log('objectStyles', JSON.stringify(objectStyles, null, 2)) - + let { computedClassName = '', objectStyles = [] } = buildStyles(content ? objs.concat(content.apply(null, vars)) : objs) if (objectStyles.length) { computedClassName += ' ' + objStyle.apply(null, objectStyles).toString() } @@ -197,15 +188,15 @@ function selector (id: string, path: string = '') { if (!id) { return path.replace(/\&/g, '') } - if (!path) return `.css-obj-${id}` + if (!path) return `.css-${id}` let x = path .split(',') .map( x => x.indexOf('&') >= 0 - ? x.replace(/\&/gm, `.css-obj-${id}`) - : `.css-obj-${id}${x}` + ? x.replace(/\&/gm, `.css-${id}`) + : `.css-${id}${x}` ) .join(',') @@ -280,11 +271,11 @@ function toRule (spec) { return ruleCache[spec.id] } - let ret = { [`css-obj-${spec.id}`]: '' } + let ret = { [`css-${spec.id}`]: '' } Object.defineProperty(ret, 'toString', { enumerable: false, value () { - return 'css-obj-' + spec.id + return 'css-' + spec.id } }) ruleCache[spec.id] = ret @@ -366,10 +357,10 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { build(dest, { selector, mq, supp, src: _src.composes }) } Object.keys(_src || {}).forEach(key => { - if (key.indexOf('css') === 0) { - console.log('fuck') - key = '.' + key - } + // if (key.indexOf('css') === 0) { + // console.log('fuck') + // key = '.' + key + // } if (isSelector(key)) { if (key === '::placeholder') { diff --git a/src/react/index.js b/src/react/index.js index c3fc65bc9..4a9f7ad08 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -67,7 +67,7 @@ export default function (tag, objs, vars, content) { } const className = `${css( - map(objs.concat(mergedProps.className.split(' ')), getValue), + map(objs.concat(mergedProps.className ? mergedProps.className.split(' ') : []), getValue), map(vars, getValue), content )}` diff --git a/src/vue/index.js b/src/vue/index.js deleted file mode 100644 index d6796388d..000000000 --- a/src/vue/index.js +++ /dev/null @@ -1,28 +0,0 @@ -import { css as magic } from '../index' - -export { flush, css, injectGlobal, fontFace, keyframes, hydrate, objStyle } from '../index' - -const styled = (tag, cls, vars = [], content) => { - return { - cls: '.' + cls, - functional: true, - render (h, context) { - const getValue = v => (v && typeof v === 'function' ? v(context.props) : v.cls || v) - const className = magic( - cls.map(getValue), - vars.map(getValue), - content - ) - return h( - tag, - { - ...context.data, - class: [context.data.class, className] - }, - context.children - ) - } - } -} - -export default styled diff --git a/src/vue/macro.js b/src/vue/macro.js deleted file mode 100644 index e098e05c0..000000000 --- a/src/vue/macro.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../macro-styled') diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 7af67ac47..04a918683 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -1,25 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`styled attr 1`] = ` -.css-H1-l44jbj-1u4tp6a { - font-size: 48; - margin: 4rem; - position: absolute; -} - -

-`; - exports[`styled basic render 1`] = ` -.css-H1-8xpzga-1e133rd { +.css-16m5m6t { + color: blue; font-size: 20px; }

hello world

diff --git a/test/__snapshots__/vue.test.js.snap b/test/__snapshots__/vue.test.js.snap deleted file mode 100644 index d19994206..000000000 --- a/test/__snapshots__/vue.test.js.snap +++ /dev/null @@ -1,63 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`vue styled based on props 1`] = ` -" -

- " -`; - -exports[`vue styled based on props composes 1`] = ` -" -
- " -`; - -exports[`vue styled basic 1`] = ` -" -
- " -`; - -exports[`vue styled child selector 1`] = ` -" -
wow
- " -`; - -exports[`vue styled creates the correct styles 1`] = ` -".css-StyledComponent-1brvt4i-ulwf5d { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; - -webkit-box-align: center; - align-items: center; - position: inherit; }.css-BasedOnProps-csm9t8-1qo3zsu { font-weight: bold; }.css-NonHtmlComponent-15287sz-1wq2ikh { background-color: purple; - color: green; }.css-ChildSelector-13pvedz-1e7xvxz { color: yellow; }.css-ChildSelector-13pvedz-1e7xvxz .css-StyledComponent-1brvt4i { display: none; }" -`; - -exports[`vue styled custom external component 1`] = ` -" -

some text

- " -`; - -exports[`vue styled with class 1`] = ` -" -
- " -`; - -exports[`vue styled with properties 1`] = ` -" -
- " -`; - -exports[`vue styled with style 1`] = ` -" -
- " -`; diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index 489372f0d..0fc9cdac0 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -110,107 +110,6 @@ describe('babel styled component', () => { expect(code).toMatchSnapshot() }) - test('attr', () => { - const basic = `styled('input')\` - margin: attr(margin); - color: #ffffff; - height: \${props => props.height * props.scale}; - width: attr(width); - color: blue; - display: \${flex}; - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('lots of attrs with interpolated values', () => { - const basic = `styled('input')\` - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - display: attr(\${displayProp}); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('attr with value type', () => { - const basic = `styled('input')\` - margin: attr(margin px); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('attr with default value', () => { - const basic = `styled('input')\` - margin: attr(margin, 16); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('attr with value type and default value', () => { - const basic = `styled('input')\` - margin: attr(margin px, 16); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('match works on multiple', () => { - const basic = `styled('input')\` - margin: attr(margin px, 16); - color: blue; - padding: attr(padding em, 16); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('attr kitchen sink', () => { - const basic = `styled('input')\` - margin: attr(margin px, 16); - padding: attr(padding em, 16); - font-size: attr(fontSize ch, 8); - width: attr(width %, 95); - height: attr(height vw, 90); - display: attr(display, flex); - position: attr(alignItems \${alignItemsUnit}, fallback); - position: attr(thing px, \${defaultValue}); - position: attr(\${thing} px); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) test('objects fn call', () => { const basic = ` const H1 = styled('h1')({ diff --git a/test/extract/extract.test.js b/test/extract/extract.test.js index 5b5fe0959..426127a59 100644 --- a/test/extract/extract.test.js +++ b/test/extract/extract.test.js @@ -40,32 +40,6 @@ describe.skip('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) - test('attr', () => { - const H1 = styled.h1` - font-size: attr(fontSize); - margin: attr(margin rem, 4); - ` - - const Title = ({ title }) => { - return ( -

- {title} -

- ) - } - - const tree = renderer.create().toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('another attr', () => { - const PlaygroundWrapper = styled('div')` - color: attr(color, #343a40); - ` - expect(renderer.create(<PlaygroundWrapper />).toJSON()).toMatchSnapshotWithEmotion() - }) - test('call expression', () => { const fontSize = '20px' const H1 = styled('h1')` diff --git a/test/macro/__snapshots__/vue.test.js.snap b/test/macro/__snapshots__/vue.test.js.snap deleted file mode 100644 index d19994206..000000000 --- a/test/macro/__snapshots__/vue.test.js.snap +++ /dev/null @@ -1,63 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`vue styled based on props 1`] = ` -" - <div id=\\"app\\"><h1 weight=\\"bold\\" class=\\"css-BasedOnProps-csm9t8-1qo3zsu css-BasedOnProps-csm9t8\\"></h1></div> - " -`; - -exports[`vue styled based on props composes 1`] = ` -" - <div id=\\"app\\"><div thing=\\"true\\" class=\\"css-basedOnPropsComposes-17rcs0o some-special-class\\"></div></div> - " -`; - -exports[`vue styled basic 1`] = ` -" - <div id=\\"app\\"><div class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\"></div></div> - " -`; - -exports[`vue styled child selector 1`] = ` -" - <div id=\\"app\\"><main class=\\"css-ChildSelector-13pvedz-1e7xvxz css-ChildSelector-13pvedz\\"><div class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\">wow</div></main></div> - " -`; - -exports[`vue styled creates the correct styles 1`] = ` -".css-StyledComponent-1brvt4i-ulwf5d { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; - -webkit-box-align: center; - align-items: center; - position: inherit; }.css-BasedOnProps-csm9t8-1qo3zsu { font-weight: bold; }.css-NonHtmlComponent-15287sz-1wq2ikh { background-color: purple; - color: green; }.css-ChildSelector-13pvedz-1e7xvxz { color: yellow; }.css-ChildSelector-13pvedz-1e7xvxz .css-StyledComponent-1brvt4i { display: none; }" -`; - -exports[`vue styled custom external component 1`] = ` -" - <div id=\\"app\\"><div class=\\"css-NonHtmlComponent-15287sz-1wq2ikh css-NonHtmlComponent-15287sz\\"><div></div><div></div><h1>some text</h1><div></div></div></div> - " -`; - -exports[`vue styled with class 1`] = ` -" - <div id=\\"app\\"><div class=\\"some-class css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\"></div></div> - " -`; - -exports[`vue styled with properties 1`] = ` -" - <div id=\\"app\\"><div aria-label=\\"label\\" class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\"></div></div> - " -`; - -exports[`vue styled with style 1`] = ` -" - <div id=\\"app\\"><div class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\" style=\\"display: none;\\"></div></div> - " -`; diff --git a/test/macro/react.test.js b/test/macro/react.test.js index d63a1bf75..318e35777 100644 --- a/test/macro/react.test.js +++ b/test/macro/react.test.js @@ -49,26 +49,6 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) - test('attr', () => { - const H1 = styled.h1` - font-size: attr(fontSize); - margin: attr(margin rem, 4); - position: attr(position, absolute); - ` - - const Title = ({ title }) => { - return ( - <H1 fontSize={48}> - {title} - </H1> - ) - } - - const tree = renderer.create(<Title />).toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - test('call expression', () => { const fontSize = 20 const H1 = styled('h1')` @@ -170,7 +150,7 @@ describe('styled', () => { const cssA = { color: lighten(0.2, '#000'), 'font-size': modularScale(1), - [hiDPI(1.5)]: { + [hiDPI(1.5).replace('\n', ' ').trim()]: { 'font-size': modularScale(1.25) } } diff --git a/test/macro/vue.test.js b/test/macro/vue.test.js deleted file mode 100644 index 05bc00d3c..000000000 --- a/test/macro/vue.test.js +++ /dev/null @@ -1,161 +0,0 @@ -/* eslint-disable no-new */ -/* eslint-env jest */ -import Vue from 'vue/dist/vue' -import { sheet } from '../../src' -import styled from '../../src/vue/macro' - -const inherit = 'inherit' - -const StyledComponent = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: ${inherit}; -` - -const BasedOnProps = styled.h1`font-weight: ${props => props.weight};` - -const baseComponent = { - render (h) { - return h('div', {}, [ - h('div'), - h('div'), - h('h1', {}, 'some text'), - h('div') - ]) - } -} - -const NonHtmlComponent = styled(baseComponent)` - background-color: purple; - color: green; -` - -const basedOnPropsComposes = styled.div` - composes: ${(props) => props.thing ? 'some-special-class' : ''} -` - -const ChildSelector = styled.main` - color: yellow; - ${StyledComponent} { - display: none; - } -` - -Vue.component('styled-component', StyledComponent) -Vue.component('based-on-props', BasedOnProps) -Vue.component('based-on-props-composes', basedOnPropsComposes) -Vue.component('non-html-component', NonHtmlComponent) -Vue.component('child-selector', ChildSelector) - -describe('vue styled', () => { - test('basic', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with properties', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component aria-label="label"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with class', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component class="some-class"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with style', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component style="display: none"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props weight="bold"></based-on-props> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props composes', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props-composes thing="true"></based-on-props-composes> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('custom external component', async () => { - document.body.innerHTML = ` - <div id="app"> - <non-html-component></non-html-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('child selector', async () => { - document.body.innerHTML = ` - <div id="app"> - <child-selector><styled-component>wow</styled-component></child-selector> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('creates the correct styles', () => { - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() - }) -}) diff --git a/test/react.test.js b/test/react.test.js index 895d02b21..5a498c23e 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -24,7 +24,6 @@ describe('styled', () => { const fontSize = 20 const H1 = styled.h1` color: blue; - font-size: ${fontSize}; ` @@ -54,26 +53,6 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) - test('attr', () => { - const H1 = styled.h1` - font-size: attr(fontSize); - margin: attr(margin rem, 4); - position: attr(position, absolute); - ` - - const Title = ({ title }) => { - return ( - <H1 fontSize={48}> - {title} - </H1> - ) - } - - const tree = renderer.create(<Title />).toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - test('call expression', () => { const fontSize = 20 const H1 = styled('h1')` @@ -191,6 +170,7 @@ describe('styled', () => { }) test('composes', () => { + debugger; const fontSize = '20px' const cssA = css` @@ -224,8 +204,8 @@ describe('styled', () => { test('composes with objects', () => { const cssA = { color: lighten(0.2, '#000'), - 'fontSize': modularScale(1), - [hiDPI(1.5).trim()]: { + fontSize: modularScale(1), + [hiDPI(1.5).replace('\n', ' ').trim()]: { fontSize: modularScale(1.25) } } diff --git a/test/server.test.js b/test/server.test.js index fd867ca69..eb9b83677 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -14,8 +14,8 @@ const Main = styled.main`display: flex;` const Image = styled.img` border-radius: 50%; - height: attr(size px, 50) - width: attr(size px, 50) + height: 50px; + width: 50px; background-color: ${color} ` diff --git a/test/vue.test.js b/test/vue.test.js deleted file mode 100644 index 1774aa3ba..000000000 --- a/test/vue.test.js +++ /dev/null @@ -1,161 +0,0 @@ -/* eslint-disable no-new */ -/* eslint-env jest */ -import Vue from 'vue/dist/vue' -import { sheet } from '../src/index' -import styled from '../src/vue' - -const inherit = 'inherit' - -const StyledComponent = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: ${inherit}; -` - -const BasedOnProps = styled.h1`font-weight: ${props => props.weight};` - -const baseComponent = { - render (h) { - return h('div', {}, [ - h('div'), - h('div'), - h('h1', {}, 'some text'), - h('div') - ]) - } -} - -const NonHtmlComponent = styled(baseComponent)` - background-color: purple; - color: green; -` - -const basedOnPropsComposes = styled.div` - composes: ${(props) => props.thing ? 'some-special-class' : ''} -` - -const ChildSelector = styled.main` - color: yellow; - ${StyledComponent} { - display: none; - } -` - -Vue.component('styled-component', StyledComponent) -Vue.component('based-on-props', BasedOnProps) -Vue.component('based-on-props-composes', basedOnPropsComposes) -Vue.component('non-html-component', NonHtmlComponent) -Vue.component('child-selector', ChildSelector) - -describe.skip('vue styled', () => { - test('basic', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with properties', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component aria-label="label"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with class', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component class="some-class"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with style', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component style="display: none"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props weight="bold"></based-on-props> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props composes', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props-composes thing="true"></based-on-props-composes> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('custom external component', async () => { - document.body.innerHTML = ` - <div id="app"> - <non-html-component></non-html-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('child selector', async () => { - document.body.innerHTML = ` - <div id="app"> - <child-selector><styled-component>wow</styled-component></child-selector> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('creates the correct styles', () => { - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() - }) -}) From b92058b2d06f802f9cb7e8179ac184299f4e8a35 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Tue, 18 Jul 2017 00:08:50 -0600 Subject: [PATCH 20/57] Major progress getting composing working --- src/babel.js | 18 +- src/index.js | 6 +- src/parser.js | 26 ++- src/react/index.js | 36 ++-- test/__snapshots__/css-prop.test.js.snap | 67 +++---- test/__snapshots__/css.test.js.snap | 139 +++++++------- test/__snapshots__/parser.test.js.snap | 126 ++++++------ test/__snapshots__/react.test.js.snap | 160 ++++++---------- .../babel/__snapshots__/css-prop.test.js.snap | 61 ++++-- test/babel/__snapshots__/styled.test.js.snap | 180 +++--------------- test/babel/styled.test.js | 25 ++- test/css.test.js | 35 ++-- .../__snapshots__/extract.test.js.snap | 22 --- test/macro/__snapshots__/css.test.js.snap | 139 +++++++------- test/macro/__snapshots__/react.test.js.snap | 151 ++++----------- test/macro/css.test.js | 39 ++-- test/macro/react.test.js | 23 +-- 17 files changed, 510 insertions(+), 743 deletions(-) diff --git a/src/babel.js b/src/babel.js index 00a22412c..a8a32e7c7 100644 --- a/src/babel.js +++ b/src/babel.js @@ -116,7 +116,7 @@ export function replaceCssWithCallExpression (path, identifier, state, t) { const inputClasses = [] for (var i = 0; i < composesCount; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) + inputClasses.push(path.node.quasi.expressions[i]) } inputClasses.push(createAstObj(styles, false, composesCount, t)) @@ -137,11 +137,11 @@ export function replaceCssWithCallExpression (path, identifier, state, t) { } return path.replaceWith( t.callExpression(identifier, [ - t.arrayExpression([]), - t.arrayExpression(path.node.quasi.expressions), + t.arrayExpression(path.node.quasi.expressions.slice(0, composesCount)), + t.arrayExpression(path.node.quasi.expressions.slice(composesCount)), t.functionExpression( t.identifier('createEmotionStyledRules'), - path.node.quasi.expressions.map((x, i) => t.identifier(`x${i}`)), + path.node.quasi.expressions.slice(composesCount).map((x, i) => t.identifier(`x${i}`)), t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) ) ]) @@ -161,20 +161,20 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { // console.log(JSON.stringify(styles, null, 2)) const inputClasses = [] - + const composeValues = [] for (var i = 0; i < composesCount; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) + composeValues.push(path.node.quasi.expressions[i]) } inputClasses.push(createAstObj(styles, false, composesCount, t)) const args = [ tag, - t.arrayExpression([]), - t.arrayExpression(path.node.quasi.expressions), + t.arrayExpression(path.node.quasi.expressions.slice(0, composesCount)), + t.arrayExpression(path.node.quasi.expressions.slice(composesCount)), t.functionExpression( t.identifier('createEmotionStyledRules'), - path.node.quasi.expressions.map((x, i) => t.identifier(`x${i}`)), + path.node.quasi.expressions.slice(composesCount).map((x, i) => t.identifier(`x${i}`)), t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) ) ] diff --git a/src/index.js b/src/index.js index 231ffc2e2..098beecc9 100644 --- a/src/index.js +++ b/src/index.js @@ -21,7 +21,7 @@ export function flush () { sheet.inject() } -// a simple cache to store generated rules +// a simple cache to store generated obj styles let registered = (sheet.registered = {}) function register (spec) { @@ -69,10 +69,6 @@ function buildStyles (objs) { computedClassName += cls } } else { - if (Array.isArray(cls)) { - console.log('cls is an array') - } - console.log('was a pure object') objectStyles.push( cls ) diff --git a/src/parser.js b/src/parser.js index 9c15fa477..139f8807a 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,15 +1,21 @@ // @flow import { t } from 'babel-types' -import camelizeStyleName from 'fbjs/lib/camelizeStyleName' import prefixAll from 'inline-style-prefixer/static' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' -import autoprefix from 'styled-components/lib/utils/autoprefix' -import { objStyle } from './index' +// import autoprefix from 'styled-components/lib/utils/autoprefix' +// import camelizeStyleName from 'fbjs/lib/camelizeStyleName' import postcssJs from 'postcss-js' +import { objStyle } from './index' + +type Rule = { + parent: { selector: string, nodes: Array<mixed> }, + selector: string, + remove: () => {} +} -type CSSDecl = { +type Decl = { parent: { selector: string, nodes: Array<mixed> }, prop: string, value: string, @@ -28,7 +34,13 @@ export function parseCSS ( let vars = 0 let composes: number = 0 - root.walkDecls((decl: CSSDecl): void => { + root.walkRules((rule: Rule) => { + if (/^xxx\d+xxx/.exec(rule.selector)) { + rule.selector = `.${rule.selector}` + } + }) + + root.walkDecls((decl: Decl): void => { if (decl.prop === 'composes') { if (!/xxx(\d+)xxx/gm.exec(decl.value)) { throw new Error('composes must be a interpolation') @@ -44,9 +56,7 @@ export function parseCSS ( } }) - autoprefix(root) - - const styles = postcssJs.objectify(root) + const styles = prefixAll(postcssJs.objectify(root)) return { styles, diff --git a/src/react/index.js b/src/react/index.js index 4a9f7ad08..aa8803d3e 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -16,6 +16,7 @@ export { } from '../index' export default function (tag, objs, vars, content) { + console.log(vars) if (!tag) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' @@ -55,22 +56,33 @@ export default function (tag, objs, vars, content) { } const getValue = v => { - return v && typeof v === 'function' - ? (v.__emotion_spec && - css( - map(v.__emotion_spec.objs, getValue), - map(v.__emotion_spec.vars, getValue), - v.__emotion_spec.content - )) || - v(mergedProps) - : v + if (v && typeof v === 'function') { + if (v.__emotion_spec) { + return css( + map(v.__emotion_spec.objs, getValue), + map(v.__emotion_spec.vars, getValue), + v.__emotion_spec.content + ) + } + console.log(JSON.stringify(v, null, 2)) + return v(mergedProps) + } + + return v } - const className = `${css( - map(objs.concat(mergedProps.className ? mergedProps.className.split(' ') : []), getValue), + const className = css( + map( + mergedProps.className + ? objs.concat(mergedProps.className.split(' ')) + : objs, + getValue + ), map(vars, getValue), content - )}` + ) + + console.log(className) return h( tag, diff --git a/test/__snapshots__/css-prop.test.js.snap b/test/__snapshots__/css-prop.test.js.snap index f922c0301..c21d0bb25 100644 --- a/test/__snapshots__/css-prop.test.js.snap +++ b/test/__snapshots__/css-prop.test.js.snap @@ -1,94 +1,73 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css prop react basic 1`] = ` -.css-tree-7sri7n-1yz7ela { +.css-xs9rh7 { color: red; font-size: 1px; } <p - className="css-tree-7sri7n-1yz7ela css-tree-7sri7n" + className="css-xs9rh7" > hello world </p> `; exports[`css prop react kitchen sink 1`] = ` -.css-bold-15qcrjv-18w8kdp { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; +.css-tocw18 { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; font-weight: bold; -} - -.css-small-1wh34bm-yjtkkb { - font-size: 6; -} - -.css-flexCenter-mdk64u-llada3 { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; - -webkit-box-align: center; align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -webkit-box-align: center; + -webkit-align-items: center; } -.css-tree-17jiuxc-pi0syi { +.css-2sukuz { + font-size: 6px; color: red; } -.css-tree-14jcta0-1fj87hp { +.css-bithoy { color: blue; } -.css-tree-1gdxe9e-1vltlgj { - display: -webkit-box; - display: -moz-box; - display: -ms-inline-flexbox; - display: -webkit-inline-flex; - display: inline-flex; +.css-1pyxnyx { + display: -webkit-inline-box,-moz-inline-box,-ms-inline-flexbox,-webkit-inline-flex,inline-flex; } -.css-tree-1g8ltio-i3faqz { +.css-c5xqxn { color: red; - border-radius: 5; + border-radius: 5px; } -.css-tree-1g8ltio-i3faqz:hover { +.css-c5xqxn:hover { font-weight: bold; color: gray; } <div - className="css__legacy-stuff css-tree-1ii4sc4 css-bold-15qcrjv-18w8kdp css-bold-15qcrjv css-flexCenter-mdk64u-llada3 css-flexCenter-mdk64u" + className="css__legacy-stuff css-tocw18" > <h1 - className="css-tree-17jiuxc-pi0syi css-tree-17jiuxc css-small-1wh34bm-yjtkkb css-small-1wh34bm" + className="css-2sukuz" > BOOM </h1> <p - className="test_class1 css-tree-14jcta0-1fj87hp css-tree-14jcta0" + className="test_class1 css-bithoy" > Hello </p> <p - className="test_class1 test___class45 css-tree-1gdxe9e-1vltlgj css-tree-1gdxe9e" + className="test_class1 test___class45 css-1pyxnyx" > World </p> <p - className="css-tree-1g8ltio-i3faqz css-tree-1g8ltio" + className="css-c5xqxn" > hello world </p> @@ -96,14 +75,14 @@ exports[`css prop react kitchen sink 1`] = ` `; exports[`css prop react string expression 1`] = ` -.css-tree-7sqgip-8upfq4 { +.css-1ylcmay { color: red; background: blue; font-size: 48px; } <p - className="css-tree-7sqgip-8upfq4 css-tree-7sqgip" + className="css-1ylcmay" > hello world </p> diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 8025db85b..26db9b55c 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -1,83 +1,86 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`css composes 1`] = `"css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; +exports[`css composes 1`] = ` +.css-1746rll { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; + justify-content: center; + -webkit-box-pack: center; + -webkit-justify-content: center; +} -exports[`css composes 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; - -exports[`css composes 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }" +<div + className="css-1746rll" +/> `; -exports[`css composes with objects 1`] = `"css-1puxias"`; +exports[`css composes with objects 1`] = ` +.css-v0v3tq { + display: -webkit-box,-ms-flexbox,flex,block; + width: 30px; + height: calc(40vw - 50px); + justify-content: center; + -webkit-box-pack: center; + -webkit-justify-content: center; +} + +.css-v0v3tq:hover { + color: blue; +} + +.css-v0v3tq:after { + content: " "; + color: red; +} -exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-1puxias"`; +@media (min-width: 420px) { + .css-v0v3tq { + color: green; + } +} -exports[`css composes with objects 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-xmm9em{display:-webkit-box;display:-ms-flexbox;display:flex}.css-1puxias{display:-webkit-box;display:-ms-flexbox;display:flex;display:block;width:30px;height:calc(40vw - 50px)}.css-1puxias:hover{color:blue}.css-1puxias:after{content:\\" \\";color:red}@media(min-width: 420px){.css-1puxias{color:green}}" +<div + className="css-v0v3tq" +/> `; -exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-15famh2"`; +exports[`css composes with undefined values 1`] = ` +.css-9mu7jm { + justify-content: center; + -webkit-box-pack: center; + -webkit-justify-content: center; +} -exports[`css composes with undefined values 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }" +<div + className="css-9mu7jm" +/> `; -exports[`css handles more than 10 dynamic properties 1`] = `"css-cls1-1gi569l-b877w5 css-cls1-1gi569l"`; +exports[`css handles more than 10 dynamic properties 1`] = ` +.css-550542 { + background: white; + color: black; + text-decoration: underline; + display: block; + border-radius: 3px; + padding: 25px; + width: 500px; + z-index: 100; + font-size: 18px; + text-align: center; + border: solid 1px red; +} -exports[`css handles more than 10 dynamic properties 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }" +<div + className="css-550542" +/> `; -exports[`css handles objects 1`] = `"css-xmm9em"`; +exports[`css handles objects 1`] = ` +.css-xmm9em { + display: -webkit-box,-ms-flexbox,flex; +} + +<div + className="css-xmm9em" +/> +`; diff --git a/test/__snapshots__/parser.test.js.snap b/test/__snapshots__/parser.test.js.snap index c1a88a6ad..9cbaca289 100644 --- a/test/__snapshots__/parser.test.js.snap +++ b/test/__snapshots__/parser.test.js.snap @@ -2,82 +2,74 @@ exports[`parser basic 1`] = ` Object { - "composes": 0, - "hasCssFunction": false, - "hasOtherMatch": false, - "hasVar": false, - "rules": Array [ - ".thing { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - width: var(--css-hash-0); -}", - ], + "composesCount": 0, + "isStaticBlock": true, + "styles": Object { + ".thing": Object { + "WebkitBoxPack": "center", + "WebkitJustifyContent": "center", + "display": Array [ + "-webkit-box", + "-moz-box", + "-ms-flexbox", + "-webkit-flex", + "flex", + ], + "justifyContent": "center", + "width": "var(--css-hash-0)", + }, + }, } `; exports[`parser fancy 1`] = ` Object { - "composes": 0, - "hasCssFunction": false, - "hasOtherMatch": false, - "hasVar": false, - "rules": Array [ - ".some-selector { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - width: var(--css-hash-0); -}", - ".some-selector:hover { - background-color: green; -}", - "@media (max-width: 500px) { - .some-selector { - height: var(--css-hash-1); - position: fixed; - } -}", - "@media print { - .some-selector { - display: none; - } -}", - ".some-selector::before { - color: blue; - width: 20px; - height: 20px; - content: 'pseudo' -}", - ], + "composesCount": 0, + "isStaticBlock": true, + "styles": Object { + ".some-selector": Object { + "&::before": Object { + "color": "blue", + "content": "'pseudo'", + "height": "20px", + "width": "20px", + }, + "&:hover": Object { + "backgroundColor": "green", + }, + "@media (max-width: 500px)": Object { + "height": "var(--css-hash-1)", + "position": "fixed", + }, + "@media print": Object { + "display": "none", + }, + "WebkitBoxPack": "center", + "WebkitJustifyContent": "center", + "display": Array [ + "-webkit-box", + "-moz-box", + "-ms-flexbox", + "-webkit-flex", + "flex", + ], + "justifyContent": "center", + "width": "var(--css-hash-0)", + }, + }, } `; exports[`parser static 1`] = ` Object { - "composes": 0, - "hasCssFunction": false, - "hasOtherMatch": false, - "hasVar": false, - "rules": Array [ - ".thing { - display: block; - height: 50px; - width: 30px -}", - ], + "composesCount": 0, + "isStaticBlock": true, + "styles": Object { + ".thing": Object { + "display": "block", + "height": "50px", + "width": "30px", + }, + }, } `; diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 04a918683..ac19c0fdf 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -26,40 +26,32 @@ exports[`styled basic render with object as style 1`] = ` `; exports[`styled call expression 1`] = ` -.css-H1-13wdnau-xy96vj { +.css-1vj1sv9 { font-size: 20px; } <h1 - className="css-H1-13wdnau-xy96vj css-H1-13wdnau legacy__class" + className="legacy__class css-1vj1sv9" > hello world </h1> `; exports[`styled component as selector 1`] = ` -.css-Thing-1kdnbhf-19sb43k { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; -} - -.css-Thing-1kdnbhf-19sb43k .css-H1-1t8i2zo { - color: green; +.css-1vj1sv9 { + font-size: 20px; } -.css-H1-1t8i2zo-1c6u60b { - font-size: 20px; +.css-sgyhpk { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; } <div - className="css-Thing-1kdnbhf-19sb43k css-Thing-1kdnbhf" + className="css-sgyhpk" > hello <h1 - className="css-H1-1t8i2zo-1c6u60b css-H1-1t8i2zo" + className="css-1vj1sv9" > This will be green </h1> @@ -68,24 +60,13 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-cssA-cy897j-1p4ppuj { - color: blue; -} - -.css-cssB-ob3sm8-fwh0v { - height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-H1-zejj71-ovrrqc { +.css-z9nuho { font-size: 20px; + color: blue; } <h1 - className="css-H1-zejj71-ovrrqc css-H1-zejj71 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-cssA-cy897j-1p4ppuj css-cssA-cy897j css-H2-idm3bz-361gww css-H2-idm3bz legacy__class" + className="legacy__class css-z9nuho" scale={2} > hello world @@ -93,46 +74,35 @@ exports[`styled composes 1`] = ` `; exports[`styled composes based on props 1`] = ` -.css-cssA-cy897j-1p4ppuj { +.css-bithoy { color: blue; } <h1 a={true} - className="css-H1-16dw226 css-cssA-cy897j-1p4ppuj css-cssA-cy897j" + className="css-bithoy" > hello world </h1> `; exports[`styled composes based on props 2`] = ` -.css-cssB-7o6h5j-dcpp5a { +.css-45352 { color: green; } <h1 - className="css-H1-16dw226 css-cssB-7o6h5j-dcpp5a css-cssB-7o6h5j" + className="css-45352" > hello world </h1> `; exports[`styled composes with objects 1`] = ` -.css-cssB-ob3sm8-fwh0v { - height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-1olphq9 { - color: #333; - font-size: 1.333em; -} - -.css-H1-zejj71-1ym7xv { +.css-3f34ux { font-size: 3.157334518321em; + color: #333; + height: 64px; } @media only screen and (-webkit-min-device-pixel-ratio: 1.5), @@ -140,13 +110,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-1olphq9 { + .css-3f34ux { font-size: 1.4323121856191332em; } } <h1 - className="css-H1-zejj71-1ym7xv css-H1-zejj71 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-1olphq9 css-H2-idm3bz-361gww css-H2-idm3bz legacy__class" + className="legacy__class css-3f34ux" scale={2} > hello world @@ -154,32 +124,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-H2-vxb7tq-1gn1gp5 { - font-size: 13.333333333333334; -} - -.css-H1-rs9k70-qz486c { +.css-1vj1sv9 { font-size: 20px; } <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70 css-H2-vxb7tq-1gn1gp5 css-H2-vxb7tq legacy__class" + className="legacy__class css-1vj1sv9" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-H1-rs9k70-qz486c { +.css-1vj1sv9 { font-size: 20px; } -.css-H2-vxb7tq-1pd0qgw { - font-size: 40px; -} - <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70 css-H2-vxb7tq-1pd0qgw css-H2-vxb7tq legacy__class" + className="legacy__class css-1vj1sv9" scale={2} > hello world @@ -187,81 +149,87 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-squirtle-blue-bg-135ox65-8g3djl { - background-color: #7FC8D6; -} - -.css-onyx-165zlfj-sv1xya { +.css-dlwa03 { + name: onyx; background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; + flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; - flex-direction: column; -} - -.css-Content-13wdnau-1z6hk6 { + -webkit-flex-direction: column; font-size: 20px; } <div - className="css-Content-13wdnau-1z6hk6 css-Content-13wdnau css-onyx-165zlfj-sv1xya css-onyx-165zlfj css-squirtle-blue-bg-135ox65-8g3djl css-squirtle-blue-bg-135ox65" + className="css-dlwa03" /> `; exports[`styled innerRef 1`] = ` -.css-H1-ijh7uz-10q50rl { +.css-f8g05s { font-size: 12px; } <h1 - className="css-H1-ijh7uz-10q50rl css-H1-ijh7uz" + className="css-f8g05s" > hello world </h1> `; exports[`styled name 1`] = ` -.css-FancyH1-1azfbv1-1fzd8kz { +.css-1i0qtj6 { + name: FancyH1; font-size: 20px; } <h1 - className="css-FancyH1-1azfbv1-1fzd8kz css-FancyH1-1azfbv1" + className="css-1i0qtj6" > hello world </h1> `; exports[`styled no dynamic 1`] = ` -.css-H1-ijh7uz-10q50rl { +.css-f8g05s { font-size: 12px; } <h1 - className="css-H1-ijh7uz-10q50rl css-H1-ijh7uz" + className="css-f8g05s" > hello world </h1> `; exports[`styled object composition 1`] = ` +.css-ifc29m { + border-radius: 50%; + -webkit-transition: -webkit-transform 400ms ease-in-out; + transition: -webkit-transform 400ms ease-in-out,transform 400ms ease-in-out,transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out; + border: 3px solid currentColor; + width: 96px; + height: 96px; + color: blue; +} + +.css-ifc29m:hover { + -webkit-transform: scale(1.2); + transform: scale(1.2); +} + <img - className=" css-nil" + className="css-ifc29m" /> `; exports[`styled objects 1`] = ` -.css-1viuxsa { +.css-1j0hret { padding: 10px; -} - -.css-1fe3owl { display: flex; } <h1 - className="some-class css-1viuxsa css-1fe3owl" + className="some-class css-1j0hret" display="flex" > hello world @@ -269,29 +237,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-cssA-cy897j-1p4ppuj { - color: blue; -} - -.css-cssB-ob3sm8-fwh0v { - height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-H1-iwhfi5-s8bsgs { +.css-5q2sb2 { font-size: 20px; color: #8c81d8; -} - -.css-Heading-1ybg51t-91l5qw { + height: 64px; background-color: #ffd43b; } <span - className="css-Heading-1ybg51t-91l5qw css-Heading-1ybg51t css-H1-iwhfi5-s8bsgs css-H1-iwhfi5 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-cssA-cy897j-1p4ppuj css-cssA-cy897j css-H2-idm3bz-361gww css-H2-idm3bz" + className="css-5q2sb2" scale={2} > hello world diff --git a/test/babel/__snapshots__/css-prop.test.js.snap b/test/babel/__snapshots__/css-prop.test.js.snap index 496ae76a0..a4b9d054e 100644 --- a/test/babel/__snapshots__/css-prop.test.js.snap +++ b/test/babel/__snapshots__/css-prop.test.js.snap @@ -1,60 +1,83 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel css prop StringLiteral css prop value 1`] = ` -"<div className={css([\\"css-jf1v9l\\"], [], function createEmotionRules() { - return [\`.css-jf1v9l { color: brown; }\`]; +"<div className={css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; })}></div>;" `; exports[`babel css prop basic 1`] = ` -"import \\"./css-prop.test.emotion.css\\"; -<div className={\\"a\\" + \\" \\" + \`\${\\"css-jf1v9l\\"}\`}></div>;" +"<div className={\\"a\\" + \\" \\" + css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; +})}></div>;" `; exports[`babel css prop basic 2`] = `".css-jf1v9l { color: brown; }"`; exports[`babel css prop basic inline 1`] = ` -"<div className={\\"a\\" + \\" \\" + css([\\"css-jf1v9l\\"], [], function createEmotionRules() { - return [\`.css-jf1v9l { color: brown; }\`]; +"<div className={\\"a\\" + \\" \\" + css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; })}></div>;" `; exports[`babel css prop className as expression 1`] = ` -"<div className={variable + \\" \\" + css([\\"css-jf1v9l\\"], [], function createEmotionRules() { - return [\`.css-jf1v9l { color: brown; }\`]; +"<div className={variable + \\" \\" + css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; })}></div>;" `; exports[`babel css prop className as expression string 1`] = ` -"<div className={\`test__class\` + \\" \\" + css([\\"css-jf1v9l\\"], [], function createEmotionRules() { - return [\`.css-jf1v9l { color: brown; }\`]; +"<div className={\`test__class\` + \\" \\" + css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; })} this={\`hello\`}></div>;" `; -exports[`babel css prop css empty 1`] = `"<div className={css([\\"css-0\\"], [])}></div>;"`; +exports[`babel css prop css empty 1`] = ` +"<div className={css([], [], function createEmotionStyledRules() { + return [{}]; +})}></div>;" +`; exports[`babel css prop dynamic inline 1`] = ` -"<div className={\\"a\\" + \\" \\" + css([\\"css-jsqoaa\\"], [color], function createEmotionRules(x0) { - return [\`.css-jsqoaa { color: \${x0}; }\`]; +"<div className={\\"a\\" + \\" \\" + css([], [color], function createEmotionStyledRules(x0) { + return [{ + \\"color\\": x0 + }]; })}></div>;" `; exports[`babel css prop emptyClassName 1`] = ` -"<div className={\\"\\" + \\" \\" + css([\\"css-jf1v9l\\"], [], function createEmotionRules() { - return [\`.css-jf1v9l { color: brown; }\`]; +"<div className={\\"\\" + \\" \\" + css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; })}></div>;" `; exports[`babel css prop no css attr 1`] = `"<div></div>;"`; exports[`babel css prop noClassName 1`] = ` -"<div className={css([\\"css-jf1v9l\\"], [], function createEmotionRules() { - return [\`.css-jf1v9l { color: brown; }\`]; +"<div className={css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; })}></div>;" `; exports[`babel css prop with spread arg in jsx opening tag 1`] = ` -"<div className={\\"a\\" + \\" \\" + css([\\"css-jf1v9l\\"], [], function createEmotionRules() { - return [\`.css-jf1v9l { color: brown; }\`]; +"<div className={\\"a\\" + \\" \\" + css([], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"brown\\" + }]; })} {...rest}></div>;" `; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index cfe169922..7c148e4d4 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -45,83 +45,30 @@ exports[`babel styled component extract no dynamic 2`] = `".css-14ksm7b { color: exports[`babel styled component extract no use 1`] = `"\\"h1\\";"`; -exports[`babel styled component inline attr 1`] = ` -"styled('input', ['css-yj7w7r'], [props => props.height * props.scale, flex, function getMargin(props) { - return props.margin; -}, function getWidth(props) { - return props.width; -}], function createEmotionStyledRules(x0, x1, x2, x3) { - return [\`.css-yj7w7r { margin: \${x2}; - color: #ffffff; - height: \${x0}; - width: \${x3}; - color: blue; - display: \${x1}; }\`]; -});" -`; - -exports[`babel styled component inline attr kitchen sink 1`] = ` -"styled('input', ['css-y6q35k'], [alignItemsUnit, defaultValue, thing, function getMargin(props) { - return (props.margin ? props.margin : '16') + 'px'; -}, function getPadding(props) { - return (props.padding ? props.padding : '16') + 'em'; -}, function getFontSize(props) { - return (props.fontSize ? props.fontSize : '8') + 'ch'; -}, function getWidth(props) { - return (props.width ? props.width : '95') + '%'; -}, function getHeight(props) { - return (props.height ? props.height : '90') + 'vw'; -}, function getDisplay(props) { - return (props.display ? props.display : 'flex') + ''; -}, function getAlignItems(props) { - return (props.alignItems ? props.alignItems : 'fallback') + alignItemsUnit; -}, function getThing(props) { - return (props.thing ? props.thing : defaultValue) + 'px'; -}, function getProp(props) { - return props[thing] + 'px'; -}], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) { - return [\`.css-y6q35k { margin: \${x3}; - padding: \${x4}; - font-size: \${x5}; - width: \${x6}; - height: \${x7}; - display: \${x8}; - position: \${x9}; - position: \${x10}; - position: \${x11}; }\`]; -});" -`; - -exports[`babel styled component inline attr with default value 1`] = ` -"styled('input', ['css-fj5c3o'], [function getMargin(props) { - return (props.margin ? props.margin : '16') + ''; -}], function createEmotionStyledRules(x0) { - return [\`.css-fj5c3o { margin: \${x0}; }\`]; -});" -`; - -exports[`babel styled component inline attr with value type 1`] = ` -"styled('input', ['css-3jy6co'], [function getMargin(props) { - return props.margin + 'px'; -}], function createEmotionStyledRules(x0) { - return [\`.css-3jy6co { margin: \${x0}; }\`]; -});" -`; - -exports[`babel styled component inline attr with value type and default value 1`] = ` -"styled('input', ['css-vb5gjx'], [function getMargin(props) { - return (props.margin ? props.margin : '16') + 'px'; -}], function createEmotionStyledRules(x0) { - return [\`.css-vb5gjx { margin: \${x0}; }\`]; -});" -`; - exports[`babel styled component inline basic 1`] = ` "const H1 = styled('h1', ['css-H1-1t8i2zo'], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [\`.css-H1-1t8i2zo { font-size: \${x0}; }\`]; });" `; +exports[`babel styled component inline composes based on props 1`] = ` +"const cls1 = css([], [], function createEmotionStyledRules() { + return [{ + 'width': '20px' + }]; +}); +const H1 = styled('h1', [props => { + return props.a ? cssA : cssB; +}], [fontSize + 'px', props => props.translateX], function createEmotionStyledRules(x0, x1) { + return [{ + 'fontSize': x0, + 'height': '20px', + 'transform': \`translateX(\${x1})\`, + 'WebkitTransform': \`translateX(\${x1})\` + }]; +});" +`; + exports[`babel styled component inline function call 1`] = ` "styled(MyComponent, ['css-1t8i2zo'], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [\`.css-1t8i2zo { font-size: \${x0}; }\`]; @@ -143,77 +90,6 @@ const H1 = styled('h1', [{ }]);" `; -exports[`babel styled component inline lots of attrs with interpolated values 1`] = ` -"styled('input', ['css-1kdt7pt'], [marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, displayProp, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[displayProp]; -}], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35) { - return [\`.css-1kdt7pt { margin: \${x18}; - margin: \${x19}; - margin: \${x20}; - margin: \${x21}; - margin: \${x22}; - margin: \${x23}; - margin: \${x24}; - margin: \${x25}; - margin: \${x26}; - margin: \${x27}; - margin: \${x28}; - margin: \${x29}; - margin: \${x30}; - margin: \${x31}; - margin: \${x32}; - margin: \${x33}; - margin: \${x34}; - display: \${x35}; }\`]; -});" -`; - -exports[`babel styled component inline match works on multiple 1`] = ` -"styled('input', ['css-1vvfgof'], [function getMargin(props) { - return (props.margin ? props.margin : '16') + 'px'; -}, function getPadding(props) { - return (props.padding ? props.padding : '16') + 'em'; -}], function createEmotionStyledRules(x0, x1) { - return [\`.css-1vvfgof { margin: \${x0}; - color: blue; - padding: \${x1}; }\`]; -});" -`; - exports[`babel styled component inline media query 1`] = ` "const H1 = styled(\\"h1\\", [\\"css-H1-162s3sk\\"], [], function createEmotionStyledRules() { return [\`@media print { @@ -301,16 +177,7 @@ styled(\\"h1\\", [\\"css-byjn67\\"], [SomeComponent], function createEmotionStyl });" `; -exports[`babel styled component inline styled. objects 1`] = ` -" -const H1 = styled(\\"h1\\", [{ - padding: \\"10px\\" -}, props => ({ - display: props.display -})]);" -`; - -exports[`babel styled component inline styled. objects prefixed 1`] = ` +exports[`babel styled component inline styled objects prefixed 1`] = ` " const H1 = styled('h1', [{ borderRadius: '50%', @@ -327,3 +194,12 @@ const H1 = styled('h1', [{ display: props.display })]);" `; + +exports[`babel styled component inline styled. objects 1`] = ` +" +const H1 = styled(\\"h1\\", [{ + padding: \\"10px\\" +}, props => ({ + display: props.display +})]);" +`; diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index 0fc9cdac0..c0b0704fe 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -120,6 +120,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('objects based on props', () => { const basic = ` const H1 = styled('h1')({ padding: 10 },props => ({ @@ -130,6 +131,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('object composes with classes', () => { const basic = ` const H1 = styled('h1')('some-class',props => ({ @@ -140,6 +142,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('objects prefixed', () => { const basic = ` const H1 = styled('h1')({ @@ -158,6 +161,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('styled. objects', () => { const basic = ` const H1 = styled.h1({ padding: 10 },props => ({ @@ -168,7 +172,8 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) - test('styled. objects prefixed', () => { + + test('styled objects prefixed', () => { const basic = ` const H1 = styled.h1({ borderRadius: '50%', @@ -186,7 +191,25 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + + test('composes based on props', () => { + const basic = `const cls1 = css\` width: 20px; \` + const H1 = styled.h1\` + composes: $\{props => { + return props.a ? cssA : cssB + }}; + font-size: \${fontSize + 'px'}; + height: 20px; + transform: translateX(\${(props) => props.translateX}); + \`` + const {code} = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) }) + + describe('extract', () => { test('no use', () => { const basic = 'styled.h1``' diff --git a/test/css.test.js b/test/css.test.js index 13aeacf9e..d8e429661 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -1,6 +1,12 @@ /* eslint-env jest */ +import React from 'react' +import renderer from 'react-test-renderer' +import { matcher, serializer } from '../jest-utils' import { sheet, css } from '../src/index' +expect.addSnapshotSerializer(serializer) +expect.extend(matcher) + describe('css', () => { test('handles more than 10 dynamic properties', () => { const cls1 = css` @@ -17,10 +23,8 @@ describe('css', () => { border: ${'solid 1px red'}; ` - expect(cls1).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls1}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('composes with undefined values', () => { @@ -28,10 +32,8 @@ describe('css', () => { composes: ${undefined}; justifyContent: center; ` - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('composes', () => { @@ -42,16 +44,14 @@ describe('css', () => { composes: ${cls1}; justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('handles objects', () => { const cls1 = css({ display: 'flex' }) - expect(cls1).toMatchSnapshot() + const tree = renderer.create(<div className={cls1}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('composes with objects', () => { @@ -73,10 +73,7 @@ describe('css', () => { justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) }) diff --git a/test/extract/__snapshots__/extract.test.js.snap b/test/extract/__snapshots__/extract.test.js.snap index df273dc7f..d04c8b5e8 100644 --- a/test/extract/__snapshots__/extract.test.js.snap +++ b/test/extract/__snapshots__/extract.test.js.snap @@ -1,27 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`styled another attr 1`] = ` -.css-PlaygroundWrapper-1shjoz3-tra3vx { - color: #343a40; -} - -<div - className="css-PlaygroundWrapper-1shjoz3-tra3vx css-PlaygroundWrapper-1shjoz3" -/> -`; - -exports[`styled attr 1`] = ` -.css-H1-nt9mdf-vddukf { - font-size: 48; - margin: 4rem; -} - -<h1 - className="css-H1-nt9mdf-vddukf css-H1-nt9mdf" - fontSize={48} -/> -`; - exports[`styled basic render 1`] = ` .vars-ch9j2 { --css-H1-1t8i2zo-0: 20px; diff --git a/test/macro/__snapshots__/css.test.js.snap b/test/macro/__snapshots__/css.test.js.snap index c4a23a783..0510de90e 100644 --- a/test/macro/__snapshots__/css.test.js.snap +++ b/test/macro/__snapshots__/css.test.js.snap @@ -1,83 +1,86 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`css composes 1`] = `"css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; +exports[`css macro composes 1`] = ` +.css-1746rll { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; + justify-content: center; + -webkit-box-pack: center; + -webkit-justify-content: center; +} -exports[`css composes 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; - -exports[`css composes 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }" +<div + className="css-1746rll" +/> `; -exports[`css composes with objects 1`] = `"css-q8izmm"`; +exports[`css macro composes with objects 1`] = ` +.css-1ko8dz { + display: flex,block; + width: 30px; + height: calc(40vw - 50px); + justify-content: center; + -webkit-box-pack: center; + -webkit-justify-content: center; +} + +.css-1ko8dz:hover { + color: blue; +} + +.css-1ko8dz:after { + content: " "; + color: red; +} -exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-q8izmm"`; +@media (min-width: 420px) { + .css-1ko8dz { + color: green; + } +} -exports[`css composes with objects 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-1fe3owl{display:flex}.css-q8izmm{display:flex;display:block;width:30px;height:calc(40vw - 50px)}.css-q8izmm:hover{color:blue}.css-q8izmm:after{content:\\" \\";color:red}@media(min-width: 420px){.css-q8izmm{color:green}}" +<div + className="css-1ko8dz" +/> `; -exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-15famh2"`; +exports[`css macro composes with undefined values 1`] = ` +.css-9mu7jm { + justify-content: center; + -webkit-box-pack: center; + -webkit-justify-content: center; +} -exports[`css composes with undefined values 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }" +<div + className="css-9mu7jm" +/> `; -exports[`css handles more than 10 dynamic properties 1`] = `"css-cls1-1gi569l-b877w5 css-cls1-1gi569l"`; +exports[`css macro handles more than 10 dynamic properties 1`] = ` +.css-550542 { + background: white; + color: black; + text-decoration: underline; + display: block; + border-radius: 3px; + padding: 25px; + width: 500px; + z-index: 100; + font-size: 18px; + text-align: center; + border: solid 1px red; +} -exports[`css handles more than 10 dynamic properties 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }" +<div + className="css-550542" +/> `; -exports[`css handles objects 1`] = `"css-1fe3owl"`; +exports[`css macro handles objects 1`] = ` +.css-1fe3owl { + display: flex; +} + +<div + className="css-1fe3owl" +/> +`; diff --git a/test/macro/__snapshots__/react.test.js.snap b/test/macro/__snapshots__/react.test.js.snap index 2a5f93f18..6cb806a6f 100644 --- a/test/macro/__snapshots__/react.test.js.snap +++ b/test/macro/__snapshots__/react.test.js.snap @@ -1,25 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`styled attr 1`] = ` -.css-H1-l44jbj-1u4tp6a { - font-size: 48; - margin: 4rem; - position: absolute; -} - -<h1 - className="css-H1-l44jbj-1u4tp6a css-H1-l44jbj" - fontSize={48} -/> -`; - exports[`styled basic render 1`] = ` -.css-H1-8xpzga-1e133rd { +.css-1vj1sv9 { font-size: 20px; } <h1 - className="css-H1-8xpzga-1e133rd css-H1-8xpzga" + className="css-1vj1sv9" > hello world </h1> @@ -38,40 +25,32 @@ exports[`styled basic render with object as style 1`] = ` `; exports[`styled call expression 1`] = ` -.css-H1-13wdnau-xy96vj { +.css-1vj1sv9 { font-size: 20px; } <h1 - className="css-H1-13wdnau-xy96vj css-H1-13wdnau legacy__class" + className="css-1vj1sv9" > hello world </h1> `; exports[`styled component as selector 1`] = ` -.css-H1-rs9k70-qz486c { +.css-1vj1sv9 { font-size: 20px; } -.css-Thing-1kdnbhf-d995d2 { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; -} - -.css-Thing-1kdnbhf-d995d2 .css-H1-rs9k70 { - color: green; +.css-sgyhpk { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; } <div - className="css-Thing-1kdnbhf-d995d2 css-Thing-1kdnbhf" + className="css-sgyhpk" > hello <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70" + className="css-1vj1sv9" > This will be green </h1> @@ -80,24 +59,14 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-cssA-cy897j-1p4ppuj { +.css-1rkkfxd { color: blue; -} - -.css-cssB-ob3sm8-fwh0v { height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-H1-zejj71-ovrrqc { font-size: 20px; } <h1 - className="css-H1-zejj71-ovrrqc css-H1-zejj71 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-cssA-cy897j-1p4ppuj css-cssA-cy897j css-H2-idm3bz-361gww css-H2-idm3bz legacy__class" + className="css-1rkkfxd" scale={2} > hello world @@ -105,45 +74,35 @@ exports[`styled composes 1`] = ` `; exports[`styled composes based on props 1`] = ` -.css-cssA-cy897j-1p4ppuj { +.css-bithoy { color: blue; } <h1 a={true} - className="css-H1-16dw226 css-cssA-cy897j-1p4ppuj css-cssA-cy897j" + className="css-bithoy" > hello world </h1> `; exports[`styled composes based on props 2`] = ` -.css-cssB-7o6h5j-dcpp5a { +.css-45352 { color: green; } <h1 - className="css-H1-16dw226 css-cssB-7o6h5j-dcpp5a css-cssB-7o6h5j" + className="css-45352" > hello world </h1> `; exports[`styled composes with objects 1`] = ` -.css-cssB-ob3sm8-fwh0v { - height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-1olphq9 { +.css-1yxo5lh { color: #333; font-size: 1.333em; -} - -.css-H1-zejj71-1ym7xv { + height: 64px; font-size: 3.157334518321em; } @@ -152,13 +111,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-1olphq9 { + .css-1yxo5lh { font-size: 1.4323121856191332em; } } <h1 - className="css-H1-zejj71-1ym7xv css-H1-zejj71 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-1olphq9 css-H2-idm3bz-361gww css-H2-idm3bz legacy__class" + className="css-1yxo5lh" scale={2} > hello world @@ -166,32 +125,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-H2-vxb7tq-1gn1gp5 { - font-size: 13.333333333333334; -} - -.css-H1-rs9k70-qz486c { +.css-1vj1sv9 { font-size: 20px; } <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70 css-H2-vxb7tq-1gn1gp5 css-H2-vxb7tq legacy__class" + className="css-1vj1sv9" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-H1-rs9k70-qz486c { +.css-1vj1sv9 { font-size: 20px; } -.css-H2-vxb7tq-1pd0qgw { - font-size: 40px; -} - <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70 css-H2-vxb7tq-1pd0qgw css-H2-vxb7tq legacy__class" + className="css-1vj1sv9" scale={2} > hello world @@ -199,75 +150,65 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-squirtle-blue-bg-135ox65-8g3djl { - background-color: #7FC8D6; -} - -.css-onyx-165zlfj-sv1xya { +.css-1y1mvcu { background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; + flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; - flex-direction: column; -} - -.css-Content-13wdnau-1z6hk6 { + -webkit-flex-direction: column; font-size: 20px; } <div - className="css-Content-13wdnau-1z6hk6 css-Content-13wdnau css-onyx-165zlfj-sv1xya css-onyx-165zlfj css-squirtle-blue-bg-135ox65-8g3djl css-squirtle-blue-bg-135ox65" + className="css-1y1mvcu" /> `; exports[`styled innerRef 1`] = ` -.css-H1-ijh7uz-10q50rl { +.css-f8g05s { font-size: 12px; } <h1 - className="css-H1-ijh7uz-10q50rl css-H1-ijh7uz" + className="css-f8g05s" > hello world </h1> `; exports[`styled name 1`] = ` -.css-FancyH1-1azfbv1-1fzd8kz { +.css-1i0qtj6 { + name: FancyH1; font-size: 20px; } <h1 - className="css-FancyH1-1azfbv1-1fzd8kz css-FancyH1-1azfbv1" + className="css-1i0qtj6" > hello world </h1> `; exports[`styled no dynamic 1`] = ` -.css-H1-ijh7uz-10q50rl { +.css-f8g05s { font-size: 12px; } <h1 - className="css-H1-ijh7uz-10q50rl css-H1-ijh7uz" + className="css-f8g05s" > hello world </h1> `; exports[`styled objects 1`] = ` -.css-1viuxsa { +.css-1j0hret { padding: 10px; -} - -.css-1fe3owl { display: flex; } <h1 - className="some-class css-1viuxsa css-1fe3owl" + className="some-class css-1j0hret" display="flex" > hello world @@ -275,29 +216,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-cssA-cy897j-1p4ppuj { - color: blue; -} - -.css-cssB-ob3sm8-fwh0v { +.css-13nrqw7 { + color: #8c81d8; height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-H1-iwhfi5-s8bsgs { font-size: 20px; - color: #8c81d8; -} - -.css-Heading-1ybg51t-91l5qw { background-color: #ffd43b; } <span - className="css-Heading-1ybg51t-91l5qw css-Heading-1ybg51t css-H1-iwhfi5-s8bsgs css-H1-iwhfi5 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-cssA-cy897j-1p4ppuj css-cssA-cy897j css-H2-idm3bz-361gww css-H2-idm3bz" + className="css-13nrqw7" scale={2} > hello world diff --git a/test/macro/css.test.js b/test/macro/css.test.js index b8df5ec6e..257d89a61 100644 --- a/test/macro/css.test.js +++ b/test/macro/css.test.js @@ -1,8 +1,14 @@ /* eslint-env jest */ import { css } from '../../src/macro' import { sheet } from '../../src' +import React from 'react' +import renderer from 'react-test-renderer' +import { matcher, serializer } from '../../jest-utils' -describe('css', () => { +expect.addSnapshotSerializer(serializer) +expect.extend(matcher) + +describe('css macro', () => { test('handles more than 10 dynamic properties', () => { const cls1 = css` background: ${'white'}; @@ -18,10 +24,8 @@ describe('css', () => { border: ${'solid 1px red'}; ` - expect(cls1).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls1}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('composes with undefined values', () => { @@ -29,10 +33,8 @@ describe('css', () => { composes: ${undefined}; justifyContent: center; ` - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('composes', () => { @@ -43,16 +45,14 @@ describe('css', () => { composes: ${cls1}; justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('handles objects', () => { - const cls1 = css({ display: 'flex' }) - expect(cls1).toMatchSnapshot() + const cls1 = css({display: 'flex'}) + const tree = renderer.create(<div className={cls1}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) test('composes with objects', () => { @@ -74,10 +74,7 @@ describe('css', () => { justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2}/>).toJSON() + expect(tree).toMatchSnapshotWithEmotion() }) }) diff --git a/test/macro/react.test.js b/test/macro/react.test.js index 318e35777..28a111474 100644 --- a/test/macro/react.test.js +++ b/test/macro/react.test.js @@ -55,9 +55,7 @@ describe('styled', () => { font-size: ${fontSize}px; ` - const tree = renderer - .create(<H1 className={'legacy__class'}>hello world</H1>) - .toJSON() + const tree = renderer.create(<H1>hello world</H1>).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -70,9 +68,7 @@ describe('styled', () => { const H2 = styled(H1)`font-size: ${fontSize * 2 / 3}` - const tree = renderer - .create(<H2 className={'legacy__class'}>hello world</H2>) - .toJSON() + const tree = renderer.create(<H2>hello world</H2>).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -107,7 +103,7 @@ describe('styled', () => { const tree = renderer .create( - <H2 scale={2} className={'legacy__class'}> + <H2 scale={2}> hello world </H2> ) @@ -137,7 +133,7 @@ describe('styled', () => { const tree = renderer .create( - <H2 scale={2} className={'legacy__class'}> + <H2 scale={2}> hello world </H2> ) @@ -169,7 +165,7 @@ describe('styled', () => { const tree = renderer .create( - <H2 scale={2} className={'legacy__class'}> + <H2 scale={2}> hello world </H2> ) @@ -241,14 +237,12 @@ describe('styled', () => { ` const squirtleBlueBackground = css` - name: squirtle-blue-bg; background-color: #7FC8D6; ` const flexColumn = Component => { const NewComponent = styled(Component)` - composes: ${squirtleBlueBackground} - name: onyx; + composes: ${squirtleBlueBackground}; background-color: '#343a40'; flex-direction: column; ` @@ -275,7 +269,10 @@ describe('styled', () => { ` const H1 = styled('h1')` - composes: ${props => (props.a ? cssA : cssB)} + composes: ${props => { + console.log(props) + return props.a ? cssA : cssB + }}; ` const tree = renderer From 7436946dfea76709ead7a50515ae5396b64309cf Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Tue, 18 Jul 2017 00:17:54 -0600 Subject: [PATCH 21/57] Update snapshots --- test/babel/__snapshots__/css.test.js.snap | 22 ++-- test/babel/__snapshots__/styled.test.js.snap | 114 +++++++++++-------- 2 files changed, 77 insertions(+), 59 deletions(-) diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index e12cf308d..26c041e7d 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -146,19 +146,15 @@ const cls2 = css(['css-cls2-v9djpl', 'one-class', 'another-class', cls1], ['cent exports[`babel css inline css basic 1`] = ` " -css([\\"css-cu78iu\\"], [widthVar], function createEmotionRules(x0) { - return [\`.css-cu78iu { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: \${x0}; }\`]; +css([], [widthVar], function createEmotionStyledRules(x0) { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"color\\": [\\"#ffffff\\", \\"blue\\"], + \\"display\\": [\\"-webkit-box\\", \\"-moz-box\\", \\"-ms-flexbox\\", \\"-webkit-flex\\", \\"flex\\"], + \\"flex\\": \\"1 0 auto\\", + \\"width\\": x0, + \\"WebkitFlex\\": \\"1 0 auto\\" + }]; });" `; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 7c148e4d4..b9e28a9d8 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -46,8 +46,10 @@ exports[`babel styled component extract no dynamic 2`] = `".css-14ksm7b { color: exports[`babel styled component extract no use 1`] = `"\\"h1\\";"`; exports[`babel styled component inline basic 1`] = ` -"const H1 = styled('h1', ['css-H1-1t8i2zo'], [fontSize + 'px'], function createEmotionStyledRules(x0) { - return [\`.css-H1-1t8i2zo { font-size: \${x0}; }\`]; +"const H1 = styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { + return [{ + 'fontSize': x0 + }]; });" `; @@ -70,69 +72,82 @@ const H1 = styled('h1', [props => { `; exports[`babel styled component inline function call 1`] = ` -"styled(MyComponent, ['css-1t8i2zo'], [fontSize + 'px'], function createEmotionStyledRules(x0) { - return [\`.css-1t8i2zo { font-size: \${x0}; }\`]; +"styled(MyComponent, [], [fontSize + 'px'], function createEmotionStyledRules(x0) { + return [{ + 'fontSize': x0 + }]; });" `; exports[`babel styled component inline interpolation in different places 1`] = ` " -const H1 = styled('h1', [{ - 'fontSize': x0, - 'height': '20px', - 'WebkitTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], - 'msTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], - 'transform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], - 'height1': \`\${x2}wow\`, - 'width': \`w\${x3}ow\`, - 'transform1': \`translateX(\${x6}) translateY(\${x7})\`, - 'transform2': \`translateX(\${x8}) translateY(\${x9}\` -}]);" +const H1 = styled('h1', [], [fontSize + 'px', props => props.translateX, something, something, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) { + return [{ + 'fontSize': x0, + 'height': '20px', + 'transform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], + 'height1': \`\${x2}wow\`, + 'width': \`w\${x3}ow\`, + 'transform1': \`translateX(\${x6}) translateY(\${x7})\`, + 'transform2': \`translateX(\${x8}) translateY(\${x9}\` + }]; +});" `; exports[`babel styled component inline media query 1`] = ` -"const H1 = styled(\\"h1\\", [\\"css-H1-162s3sk\\"], [], function createEmotionStyledRules() { - return [\`@media print { - .css-H1-162s3sk body { - font-size: 10pt - } -}\`, \`@media screen { - .css-H1-162s3sk body { - font-size: 13px - } -}\`, \`@media screen, print { - .css-H1-162s3sk body { - line-height: 1.2 - } -}\`, \`@media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) { - .css-H1-162s3sk body { - line-height: 1.4 +"const H1 = styled(\\"h1\\", [], [], function createEmotionStyledRules() { + return [{ + \\"@media print\\": { + \\"fontSize\\": \\"10pt\\" + }, + \\"@media screen\\": { + \\".child-selector\\": { + \\"fontSize\\": \\"13px\\" + } + }, + \\"@media screen, print\\": { + \\"&:hover + &\\": { + \\"lineHeight\\": \\"1.2\\" + } + }, + \\"@media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2)\\": { + \\".child-selector\\": { + \\"lineHeight\\": \\"1.4\\" + } } -}\`]; + }]; });" `; exports[`babel styled component inline name is correct with no identifier 1`] = ` " -css([\\"css-1cppx8p\\"], [], function createEmotionRules() { - return [\`.css-1cppx8p { margin: 12px 48px; - color: #ffffff; }\`]; +css([], [], function createEmotionStyledRules() { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"color\\": \\"#ffffff\\" + }]; });" `; exports[`babel styled component inline no dynamic 1`] = ` -"styled(\\"h1\\", [{ - \\"color\\": \\"blue\\" -}]);" +"styled(\\"h1\\", [], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"blue\\" + }]; +});" `; -exports[`babel styled component inline no use 1`] = `"\\"h1\\";"`; +exports[`babel styled component inline no use 1`] = ` +"styled(\\"h1\\", [], [], function createEmotionStyledRules() { + return [{}]; +});" +`; exports[`babel styled component inline object composes with classes 1`] = ` " const H1 = styled('h1', ['some-class', props => ({ display: props.display -})], []);" +})]);" `; exports[`babel styled component inline objects based on props 1`] = ` @@ -141,14 +156,14 @@ const H1 = styled('h1', [{ padding: '10px' }, props => ({ display: props.display -})], []);" +})]);" `; exports[`babel styled component inline objects fn call 1`] = ` " const H1 = styled('h1', [{ display: ['-webkit-box', '-ms-flexbox', 'flex'] -}], []);" +}]);" `; exports[`babel styled component inline objects prefixed 1`] = ` @@ -166,14 +181,21 @@ const H1 = styled('h1', [{ } }, props => { padding: props.padding; -}], []);" +}]);" `; exports[`babel styled component inline styled component as selector 1`] = ` " -const SomeComponent = styled(\\"div\\", [\\"css-SomeComponent-1rk3iho\\"], []); -styled(\\"h1\\", [\\"css-byjn67\\"], [SomeComponent], function createEmotionStyledRules(x0) { - return [\`.css-byjn67 { color: blue; }\`, \`.css-byjn67 \${x0} { color: green; }\`]; +const SomeComponent = styled(\\"div\\", [], [], function createEmotionStyledRules() { + return [{}]; +}); +styled(\\"h1\\", [], [SomeComponent], function createEmotionStyledRules(x0) { + return [{ + \\"color\\": \\"blue\\", + [\`.\${x0}\`]: { + \\"color\\": \\"green\\" + } + }]; });" `; From 708ccb99b5cdd227b87a9177fedb309a7d380a85 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Tue, 18 Jul 2017 01:01:38 -0600 Subject: [PATCH 22/57] Work on composing --- src/index.js | 7 +------ src/parser.js | 1 + src/react/index.js | 27 +++++++++++++++++---------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/index.js b/src/index.js index 098beecc9..e8cdb6086 100644 --- a/src/index.js +++ b/src/index.js @@ -262,7 +262,7 @@ let ruleCache = {} function toRule (spec) { register(spec) insert(spec) - + console.log(spec) if (ruleCache[spec.id]) { return ruleCache[spec.id] } @@ -353,11 +353,6 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { build(dest, { selector, mq, supp, src: _src.composes }) } Object.keys(_src || {}).forEach(key => { - // if (key.indexOf('css') === 0) { - // console.log('fuck') - // key = '.' + key - // } - if (isSelector(key)) { if (key === '::placeholder') { build(dest, { diff --git a/src/parser.js b/src/parser.js index 139f8807a..982631d49 100644 --- a/src/parser.js +++ b/src/parser.js @@ -64,3 +64,4 @@ export function parseCSS ( composesCount: composes } } + diff --git a/src/react/index.js b/src/react/index.js index aa8803d3e..f3aa68c90 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -16,7 +16,6 @@ export { } from '../index' export default function (tag, objs, vars, content) { - console.log(vars) if (!tag) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' @@ -64,26 +63,34 @@ export default function (tag, objs, vars, content) { v.__emotion_spec.content ) } - console.log(JSON.stringify(v, null, 2)) return v(mergedProps) } return v } + let finalObjs = [] + + if (tag.__emotion_spec) { + finalObjs = Array.prototype.concat.call( + finalObjs, + map(tag.__emotion_spec.objs, getValue), + tag.__emotion_spec.content(map(tag.__emotion_spec.vars, getValue)) + ) + } + + if (mergedProps.className) { + Array.prototype.push.apply(finalObjs, mergedProps.className.split(' ')) + } + + Array.prototype.push.apply(finalObjs, objs) + const className = css( - map( - mergedProps.className - ? objs.concat(mergedProps.className.split(' ')) - : objs, - getValue - ), + map(finalObjs, getValue), map(vars, getValue), content ) - console.log(className) - return h( tag, omit( From f3ade2c594917d174639218c85812dc8147bcdbe Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Tue, 18 Jul 2017 12:19:30 -0600 Subject: [PATCH 23/57] composes is working with props & component --- example/src/main.js | 35 ++++++++++++++++++---------------- package.json | 1 + src/index.js | 2 +- src/react/index.js | 46 +++++++++++++++++++++++++++++++-------------- 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/example/src/main.js b/example/src/main.js index 82b8a7f74..92c0f267a 100755 --- a/example/src/main.js +++ b/example/src/main.js @@ -8,14 +8,17 @@ import logoUrl from '../../emotion.png' const cssA = css` - color: green; - ` + color: green; + padding: 12; + border: 1px solid #e67700; +` const cssB = css` - composes: ${cssA} - color: red; - font-size: 48px; - ` + composes: ${cssA} + color: red; + font-size: 48; + border-radius: 5; +` const BlueH1 = styled('h1')` composes: ${cssB}; @@ -23,10 +26,19 @@ const BlueH1 = styled('h1')` ` const FinalH2 = styled(BlueH1)` - font-size:32px; + font-size:32; color: ${p => p.block ? '#EA33C3' : '#e67700'} ` + +class App extends React.Component { + render () { + return ( + <FinalH2 className="legacy" block>Hello <span>World</span></FinalH2> + ) + } +} + // const Avatar = styled('div')` // composes: ${prettyStyles} ${blue}; // @@ -36,13 +48,4 @@ const FinalH2 = styled(BlueH1)` // borderRadius: 50%; // } // ` - -class App extends React.Component { - render () { - return ( - <FinalH2 block>Hello</FinalH2> - ) - } -} - render(<App />, document.getElementById('app')) diff --git a/package.json b/package.json index cae59536f..a9767cc58 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@arr/filter.mutate": "^1.0.0", "@arr/foreach": "^1.0.0", "@arr/map": "^1.0.0", + "@arr/reduce": "^1.0.0", "autoprefixer": "^7.1.2", "babel-plugin-syntax-jsx": "^6.18.0", "fbjs": "^0.8.12", diff --git a/src/index.js b/src/index.js index e8cdb6086..704926ed3 100644 --- a/src/index.js +++ b/src/index.js @@ -61,11 +61,11 @@ function buildStyles (objs) { // This needs to be moved into the core forEach(objs, (cls): void => { - computedClassName && (computedClassName += ' ') if (typeof cls === 'string') { if (cls.trim().indexOf('css-') === 0) { objectStyles.push(getEmotionStylesFromClassName(cls)) } else { + computedClassName && (computedClassName += ' ') computedClassName += cls } } else { diff --git a/src/react/index.js b/src/react/index.js index f3aa68c90..2834492b9 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -1,6 +1,7 @@ import { Component, createElement as h } from 'react' import PropTypes from 'prop-types' import map from '@arr/map' +import reduce from '@arr/reduce' import { css } from '../index' import { omit } from '../utils' import { CHANNEL } from './constants' @@ -15,6 +16,12 @@ export { objStyle } from '../index' +class ComponentStyleSpec { + constructor (spec) { + this.spec = spec + } +} + export default function (tag, objs, vars, content) { if (!tag) { throw new Error( @@ -48,7 +55,7 @@ export default function (tag, objs, vars, content) { } render () { - const { props, state } = this + const { props, state, context } = this const mergedProps = { ...props, theme: state.theme @@ -57,13 +64,15 @@ export default function (tag, objs, vars, content) { const getValue = v => { if (v && typeof v === 'function') { if (v.__emotion_spec) { - return css( + const css2 = css( map(v.__emotion_spec.objs, getValue), map(v.__emotion_spec.vars, getValue), v.__emotion_spec.content ) + console.log('computed style based on tag spec', css2) + return css2 } - return v(mergedProps) + return v(mergedProps, context) } return v @@ -72,24 +81,30 @@ export default function (tag, objs, vars, content) { let finalObjs = [] if (tag.__emotion_spec) { - finalObjs = Array.prototype.concat.call( + Array.prototype.push.apply( finalObjs, - map(tag.__emotion_spec.objs, getValue), - tag.__emotion_spec.content(map(tag.__emotion_spec.vars, getValue)) + reduce( + tag.__emotion_spec, + (accum, spec) => { + Array.prototype.push.apply(accum, spec.objs) + Array.prototype.push.apply(accum, spec.content(map(spec.vars, getValue))) + return accum + }, + [] + ) ) } + Array.prototype.push.apply(finalObjs, objs) + + Array.prototype.push.apply(finalObjs, content(map(vars, getValue))) + if (mergedProps.className) { Array.prototype.push.apply(finalObjs, mergedProps.className.split(' ')) } - Array.prototype.push.apply(finalObjs, objs) - - const className = css( - map(finalObjs, getValue), - map(vars, getValue), - content - ) + console.log(finalObjs) + const className = css(map(finalObjs, getValue)) return h( tag, @@ -111,10 +126,13 @@ export default function (tag, objs, vars, content) { } Styled.displayName = `styled(${componentTag})` - Styled.__emotion_spec = { + const spec = { vars, content, objs } + Styled.__emotion_spec = tag.__emotion_spec + ? tag.__emotion_spec.concat(spec) + : [spec] return Styled } From f85d3223e135389cf0ca6e4cf198b5f18a4ef9d3 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Tue, 18 Jul 2017 20:06:35 -0600 Subject: [PATCH 24/57] copy over reduce --- src/react/index.js | 7 +++++-- src/utils.js | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/react/index.js b/src/react/index.js index fad86d4f9..0e949b24b 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -1,7 +1,7 @@ import { Component, createElement as h } from 'react' import PropTypes from 'prop-types' import { css } from '../index' -import { map, omit } from '../utils' +import { map, omit, reduce } from '../utils' import { CHANNEL } from './constants' export { @@ -79,7 +79,10 @@ export default function (tag, cls, vars = [], content) { tag.__emotion_spec, (accum, spec) => { Array.prototype.push.apply(accum, spec.objs) - Array.prototype.push.apply(accum, spec.content(map(spec.vars, getValue))) + Array.prototype.push.apply( + accum, + spec.content(map(spec.vars, getValue)) + ) return accum }, [] diff --git a/src/utils.js b/src/utils.js index 27decbeae..00febe4ac 100644 --- a/src/utils.js +++ b/src/utils.js @@ -49,3 +49,28 @@ export function map ( return out } + +export function reduce ( + arr: Array<any>, + fn: (out: Array<any>, item: any, index: number, arr: Array<any>) => any, + val: any +) { + if (arr == null) { + return [] + } + + let i = 0 + let len = arr.length + let out = val + + if (out === void 0) { + out = arr[0] + i = 1 + } + + for (; i < len; i++) { + out = fn(out, arr[i], i, arr) + } + + return out +} From c24742072b3a80ff9d6699def8b7c42fcdcc16b8 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Tue, 18 Jul 2017 23:21:13 -0600 Subject: [PATCH 25/57] font face working. Not sure how to do do injectGlobal though. --- package.json | 1 + src/babel.js | 152 +----------------- src/index.js | 97 ++++++----- src/inline.js | 60 +++---- src/macro.js | 16 +- src/parser.js | 3 +- src/react/index.js | 7 +- test/__snapshots__/keyframes.test.js.snap | 62 ++----- test/__snapshots__/react.test.js.snap | 62 +++---- .../__snapshots__/keyframes.test.js.snap | 83 ++++++---- test/babel/__snapshots__/macro.test.js.snap | 140 ++++++++-------- test/keyframes.test.js | 14 +- .../__snapshots__/keyframes.test.js.snap | 66 +++----- test/macro/__snapshots__/react.test.js.snap | 56 +++---- test/macro/keyframes.test.js | 13 +- test/macro/react.test.js | 5 +- 16 files changed, 349 insertions(+), 488 deletions(-) diff --git a/package.json b/package.json index 1f18e71f9..78642b03a 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@arr/map": "^1.0.0", "@arr/reduce": "^1.0.0", "autoprefixer": "^7.1.2", + "babel-errors": "^1.1.1", "babel-plugin-syntax-jsx": "^6.18.0", "fbjs": "^0.8.12", "inline-style-prefixer": "^3.0.6", diff --git a/src/babel.js b/src/babel.js index a8a32e7c7..3f1013139 100644 --- a/src/babel.js +++ b/src/babel.js @@ -2,6 +2,7 @@ import fs from 'fs' import { basename } from 'path' import { touchSync } from 'touch' +import { BabelError, prettyError, buildCodeFrameError, createErrorWithLoc } from 'babel-errors'; import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' import forEach from '@arr/foreach' @@ -20,92 +21,6 @@ function joinExpressionsWithSpaces (expressions, t) { return t.templateLiteral(quasis, expressions) } -function parseDynamicValues (styles, t, options) { - if (options.composes === undefined) options.composes = 0 - return rules.map(rule => { - const re = /xxx(\d+)xxx|attr\(([^,\s)]+)(?:\s*([^,)]*)?)(?:,\s*(\S+))?\)/gm - const VAR = 'VAR' - const ATTR = 'ATTR' - let match - const matches = [] - while ((match = re.exec(rule)) !== null) { - if (match[1]) { - matches.push({ - value: match[0], - p1: match[1], - index: match.index, - type: VAR - }) - } else { - matches.push({ - value: match[0], - propName: match[2], - valueType: match[3], - defaultValue: match[4], - index: match.index, - type: ATTR - }) - } - } - - let cursor = 0 - const [quasis, expressions] = matches.reduce( - (accum, match, i) => { - const [quasis, expressions] = accum - const preMatch = rule.substring(cursor, match.index) - cursor = cursor + preMatch.length + match.value.length - if (preMatch) { - quasis.push(t.templateElement({ raw: preMatch, cooked: preMatch })) - } - if (match.type === VAR) { - if (options.inputExpressions) { - expressions.push( - options.inputExpressions[match.p1 - options.composes] - ) - } else { - expressions.push(t.identifier(`x${match.p1 - options.composes}`)) - } - } - if (match.type === ATTR) { - const expr = createAttrExpression( - match, - options.vars, - options.composes, - t - ) - options.vars.push(expr) - expressions.push(t.identifier(`x${options.vars.length - 1}`)) - } - - if (i === matches.length - 1 && cursor <= rule.length) { - const postMatch = rule.substring(cursor) - - quasis.push( - t.templateElement( - { - raw: postMatch, - cooked: postMatch - }, - true - ) - ) - } - return accum - }, - [[], []] - ) - - if (!matches.length) { - return t.templateLiteral( - [t.templateElement({ raw: rule, cooked: rule }, true)], - [] - ) - } - - return t.templateLiteral(quasis, expressions) - }) -} - export function replaceCssWithCallExpression (path, identifier, state, t) { try { const { styles, isStaticBlock, composesCount } = inline( @@ -147,7 +62,10 @@ export function replaceCssWithCallExpression (path, identifier, state, t) { ]) ) } catch (e) { - throw path.buildCodeFrameError(e) + console.log("throwing here") + // let {line, column} = path.loc.start; + // throw prettyError(createErrorWithLoc('Error at this position', line, column)); + throw buildCodeFrameError(path, e.message || 'Error'); } } @@ -196,58 +114,6 @@ export function buildStyledObjectCallExpression (path, identifier, t) { ]) } -export function replaceGlobalWithCallExpression ( - identifier, - processQuasi, - path, - state, - t -) { - const { rules, hasInterpolation } = processQuasi(path.node.quasi) - if (!hasInterpolation && !state.inline) { - state.insertStaticRules(rules) - if (t.isExpressionStatement(path.parent)) { - path.parentPath.remove() - } else { - path.replaceWith(t.identifier('undefined')) - } - } else { - path.replaceWith( - t.callExpression(identifier, [ - t.arrayExpression( - parseDynamicValues(rules, t, { - inputExpressions: path.node.quasi.expressions - }) - ) - ]) - ) - } -} - -export function replaceKeyframesWithCallExpression (path, identifier, state, t) { - const { hash, name, rules, hasInterpolation } = keyframes( - path.node.quasi, - getIdentifierName(path, t), - 'animation' - ) - const animationName = `${name}-${hash}` - if (!hasInterpolation && !state.inline) { - state.insertStaticRules([`@keyframes ${animationName} ${rules.join('')}`]) - path.replaceWith(t.stringLiteral(animationName)) - } else { - path.replaceWith( - t.callExpression(identifier, [ - t.stringLiteral(animationName), - t.arrayExpression( - parseDynamicValues(rules, t, { - inputExpressions: path.node.quasi.expressions - }) - ) - ]) - ) - } -} - function prefixAst (args, t) { const prefixer = postcssJs.sync([autoprefixer]) @@ -579,24 +445,22 @@ export default function (babel) { if (path.node.tag.name === 'css') { replaceCssWithCallExpression(path, t.identifier('css'), state, t) } else if (path.node.tag.name === 'keyframes') { - replaceKeyframesWithCallExpression( + replaceCssWithCallExpression( path, t.identifier('keyframes'), state, t ) } else if (path.node.tag.name === 'fontFace') { - replaceGlobalWithCallExpression( + replaceCssWithCallExpression( t.identifier('fontFace'), - fontFace, path, state, t ) } else if (path.node.tag.name === 'injectGlobal') { - replaceGlobalWithCallExpression( + replaceCssWithCallExpression( t.identifier('injectGlobal'), - injectGlobal, path, state, t diff --git a/src/index.js b/src/index.js index cf332852c..5b747fde0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,16 +1,17 @@ // @flow import { StyleSheet } from './sheet' -import { hashArray, hashObject } from './hash' -import { forEach } from './utils' +import { forEach, map } from './utils' import { hashString as hash, hashArray, 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]: boolean | void } = {} +export let inserted: { [string | number]: boolean | void } = {} type inputVar = string | number @@ -70,9 +71,7 @@ function buildStyles (objs) { computedClassName += cls } } else { - objectStyles.push( - cls - ) + objectStyles.push(cls) } }) @@ -84,7 +83,9 @@ export function css (objs: any, vars: Array<any>, content: () => Array<any>) { objs = [objs] } - let { computedClassName = '', objectStyles = [] } = buildStyles(content ? objs.concat(content.apply(null, vars)) : objs) + let { computedClassName = '', objectStyles = [] } = buildStyles( + content ? objs.concat(content.apply(null, vars)) : objs + ) if (objectStyles.length) { computedClassName += ' ' + objStyle.apply(null, objectStyles).toString() } @@ -92,24 +93,62 @@ export function css (objs: any, vars: Array<any>, content: () => Array<any>) { return computedClassName.trim() } -export function injectGlobal (src: string[]) { - const hash = hashArray(src) - if (!inserted[hash]) { - inserted[hash] = true - forEach(src, r => sheet.insert(r)) +function insert (css: string) { + let spec = { + id: hash(css, css.length), + css, + type: 'raw' + } + register(spec) + if (!inserted[spec.id]) { + sheet.insert(spec.css) + inserted[spec.id] = true } } +export function injectGlobal ( + objs: Array<any>, + vars: Array<any>, + content: () => Array<any> +) { + // ??? +} + export const fontFace = injectGlobal -export function keyframes (kfm: string, src: string[]) { - const hash = hashArray(src) - const animationName = `${kfm}-${hash}` - if (!inserted[hash]) { - inserted[hash] = true - forEach(src, r => sheet.insert(`@keyframes ${animationName} ${r}`)) +function insertKeyframe (spec) { + if (!inserted[spec.id]) { + const inner = map( + Object.keys(spec.keyframes), + kf => `${kf} {${createMarkupForStyles(spec.keyframes[kf])}}` + ).join('') + + forEach(['-webkit-', ''], prefix => + sheet.insert(`@${prefix}keyframes ${spec.name + '_' + spec.id}{${inner}}`) + ) + + inserted[spec.id] = true + } +} + +export function keyframes ( + objs: any, + vars: Array<any>, + content: () => Array<any> +) { + const [kfs] = content.apply(null, vars) + const name = 'animation' + + let spec = { + id: hashObject(kfs), + type: 'keyframes', + name, + keyframes: kfs } - return animationName + + register(spec) + insertKeyframe(spec) + return `${name}_${spec.id}` } export function hydrate (ids: string[]) { @@ -117,7 +156,6 @@ export function hydrate (ids: string[]) { } // 🍩 -// https://github.com/jxnblk/cxs/blob/master/src/monolithic/index.js type EmotionRule = { [string]: any } type CSSRuleList = Array<EmotionRule> @@ -131,6 +169,7 @@ let cachedCss: (rules: CSSRuleList) => EmotionClassName = typeof WeakMap !== ? multiIndexCache(_css) : _css +// https://github.com/threepointone/glamor export function objStyle (...rules: CSSRuleList): EmotionClassName { rules = clean(rules) if (!rules) { @@ -152,11 +191,6 @@ function _css (rules) { return toRule(spec) } -// define some constants - -const isDev = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV -const isTest = process.env.NODE_ENV === 'test' - // takes a string, converts to lowercase, strips out nonalphanumeric. function simple (str) { return str.toLowerCase().replace(/[^a-z0-9]/g, '') @@ -191,9 +225,7 @@ function selector (id: string, path: string = '') { .split(',') .map( x => - x.indexOf('&') >= 0 - ? x.replace(/\&/gm, `.css-${id}`) - : `.css-${id}${x}` + x.indexOf('&') >= 0 ? x.replace(/\&/gm, `.css-${id}`) : `.css-${id}${x}` ) .join(',') @@ -263,7 +295,6 @@ let ruleCache = {} function toRule (spec) { register(spec) insert(spec) - console.log(spec) if (ruleCache[spec.id]) { return ruleCache[spec.id] } @@ -279,12 +310,6 @@ function toRule (spec) { return ret } -function log () { - // eslint-disable-line no-unused-vars - console.log(this) // eslint-disable-line no-console - return this -} - function isSelector (key) { let possibles = [':', '.', '[', '>', ' '], found = false, @@ -469,7 +494,7 @@ function multiIndexCache (fn) { try { coi.set(args[ctr], value) } catch (err) { - if (isDev && !warnedWeakMapError) { + if (IS_DEV && !warnedWeakMapError) { warnedWeakMapError = true console.warn('failed setting the WeakMap cache for args:', ...args) // eslint-disable-line no-console console.warn( diff --git a/src/inline.js b/src/inline.js index c28e29cca..ae9c566a5 100644 --- a/src/inline.js +++ b/src/inline.js @@ -61,47 +61,39 @@ export function keyframes ( identifierName?: string, prefix: string ): { - hash: string, - name: string, - rules: string[], - hasInterpolation: boolean + isStaticBlock: boolean, + styles: { [string]: any }, + composesCount: number } { - const strs = quasi.quasis.map(x => x.value.cooked) - const hash = hashArray([...strs]) - const name = getName( - extractNameFromProperty(strs.join('xxx')), - identifierName, - prefix - ) - const { src, dynamicValueCount } = createRawStringFromQuasi(strs, name, hash) - let { rules, hasVar, hasOtherMatch } = parseCSS(`{ ${src} }`) - return { hash, name, rules, hasInterpolation: hasVar || hasOtherMatch } + let strs = quasi.quasis.map(x => x.value.cooked) + let { src } = createRawStringFromQuasi(strs) + return parseCSS(`{ ${src} }`) } export function fontFace ( - quasi: any -): { rules: string[], hasInterpolation: boolean } { + quasi: any, + identifierName?: string, + prefix: string +): { + isStaticBlock: boolean, + styles: { [string]: any }, + composesCount: number +} { let strs = quasi.quasis.map(x => x.value.cooked) - const { src, dynamicValueCount } = createRawStringFromQuasi(strs, 'name', 'hash') - let { rules, hasVar, hasOtherMatch } = parseCSS(`@font-face {${src}}`, { - dynamicValueCount: dynamicValueCount, - inlineMode: true, - name: 'name', - hash: 'hash' - }) - return { rules, hasInterpolation: hasVar || hasOtherMatch } + let { src } = createRawStringFromQuasi(strs) + return parseCSS(`@font-face {${src}}`) } export function injectGlobal ( - quasi: any -): { rules: string[], hasInterpolation: boolean } { + quasi: any, + identifierName?: string, + prefix: string +): { + isStaticBlock: boolean, + styles: { [string]: any }, + composesCount: number +} { let strs = quasi.quasis.map(x => x.value.cooked) - const { src, dynamicValueCount } = createRawStringFromQuasi(strs, 'name', 'hash') - let { rules, hasVar, hasOtherMatch } = parseCSS(src, { - dynamicValueCount: dynamicValueCount, - inlineMode: true, - name: 'name', - hash: 'hash' - }) - return { rules, hasInterpolation: hasVar || hasOtherMatch } + let { src } = createRawStringFromQuasi(strs) + return parseCSS(`{ ${src} }`) } diff --git a/src/macro.js b/src/macro.js index 2070bb804..e93af455a 100644 --- a/src/macro.js +++ b/src/macro.js @@ -1,7 +1,5 @@ import { - replaceGlobalWithCallExpression, - replaceCssWithCallExpression, - replaceKeyframesWithCallExpression + replaceCssWithCallExpression } from './babel' import { buildMacroRuntimeNode, addRuntimeImports } from './babel-utils' import { injectGlobal, fontFace } from './inline' @@ -18,15 +16,14 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isIdentifier(path.node.tag) && t.isTemplateLiteral(path.node.quasi) ) { - replaceGlobalWithCallExpression( + replaceCssWithCallExpression( + path, buildMacroRuntimeNode( injectGlobalReference, state, 'injectGlobal', t ), - injectGlobal, - path, state, t ) @@ -39,10 +36,9 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isIdentifier(path.node.tag) && t.isTemplateLiteral(path.node.quasi) ) { - replaceGlobalWithCallExpression( - buildMacroRuntimeNode(fontFaceReference, state, 'fontFace', t), - fontFace, + replaceCssWithCallExpression( path, + buildMacroRuntimeNode(fontFaceReference, state, 'fontFace', t), state, t ) @@ -68,7 +64,7 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isIdentifier(path.node.tag) && t.isTemplateLiteral(path.node.quasi) ) { - replaceKeyframesWithCallExpression( + replaceCssWithCallExpression( path, buildMacroRuntimeNode(keyframesReference, state, 'keyframes', t), state, diff --git a/src/parser.js b/src/parser.js index 982631d49..e89d1fe0b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -35,7 +35,8 @@ export function parseCSS ( let composes: number = 0 root.walkRules((rule: Rule) => { - if (/^xxx\d+xxx/.exec(rule.selector)) { + // TODO: do this everywhere except `,xxx9xxx` + if (/\bxxx\d+xxx/.exec(rule.selector)) { rule.selector = `.${rule.selector}` } }) diff --git a/src/react/index.js b/src/react/index.js index 0e949b24b..a2c9629a6 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -14,7 +14,7 @@ export { objStyle } from '../index' -export default function (tag, cls, vars = [], content) { +export default function (tag, objs, vars = [], content) { if (!tag) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' @@ -92,13 +92,14 @@ export default function (tag, cls, vars = [], content) { Array.prototype.push.apply(finalObjs, objs) - Array.prototype.push.apply(finalObjs, content(map(vars, getValue))) + if (content) { + Array.prototype.push.apply(finalObjs, content(map(vars, getValue))) + } if (mergedProps.className) { Array.prototype.push.apply(finalObjs, mergedProps.className.split(' ')) } - console.log(finalObjs) const className = css(map(finalObjs, getValue)) return h( diff --git a/test/__snapshots__/keyframes.test.js.snap b/test/__snapshots__/keyframes.test.js.snap index 832a5245b..101dec25e 100644 --- a/test/__snapshots__/keyframes.test.js.snap +++ b/test/__snapshots__/keyframes.test.js.snap @@ -1,58 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes keyframes with interpolation 1`] = ` -"@keyframes animation-bounce-10a3qiv-cmo0tx { from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - -ms-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - -ms-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - -ms-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - -ms-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } }.css-H1-o0kcx2-fy8ana { font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; }@keyframes animation-1fpnjxj-1mmv2re { from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }" +.css-dg8t7k { + animation: animation_r3plcn 2s linear infinite; + -webkit-animation: animation_r3plcn 2s linear infinite; +} + +<h1 + className="css-dg8t7k" +> + hello world +</h1> `; +exports[`keyframes keyframes with interpolation 2`] = `"@-webkit-keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}@keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}.css-y1pikg{animation:animation_sozpa3 2s linear infinite;-webkit-animation:animation_sozpa3 2s linear infinite;}@-webkit-keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}@keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}.css-dg8t7k{animation:animation_r3plcn 2s linear infinite;-webkit-animation:animation_r3plcn 2s linear infinite;}"`; + exports[`keyframes renders 1`] = ` -.css-H1-o0kcx2-fy8ana { - font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; +.css-y1pikg { + animation: animation_sozpa3 2s linear infinite; + -webkit-animation: animation_sozpa3 2s linear infinite; } <h1 - className="css-H1-o0kcx2-fy8ana css-H1-o0kcx2" + className="css-y1pikg" > hello world </h1> diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index ac19c0fdf..5ab24870a 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`styled basic render 1`] = ` -.css-16m5m6t { +.css-vrq9pr { color: blue; font-size: 20px; } <h1 - className="css-16m5m6t" + className="css-vrq9pr" > hello world </h1> @@ -31,27 +31,27 @@ exports[`styled call expression 1`] = ` } <h1 - className="legacy__class css-1vj1sv9" + className="legacy__class css-1vj1sv9" > hello world </h1> `; exports[`styled component as selector 1`] = ` -.css-1vj1sv9 { - font-size: 20px; +.css-1eki6f4 { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; } -.css-sgyhpk { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +.css-1ikz3g9 { + font-size: 20px; } <div - className="css-sgyhpk" + className="css-1eki6f4" > hello <h1 - className="css-1vj1sv9" + className="css-1ikz3g9" > This will be green </h1> @@ -60,13 +60,13 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-z9nuho { - font-size: 20px; +.css-135lost { color: blue; + font-size: 32px; } <h1 - className="legacy__class css-z9nuho" + className="legacy__class css-135lost" scale={2} > hello world @@ -99,9 +99,9 @@ exports[`styled composes based on props 2`] = ` `; exports[`styled composes with objects 1`] = ` -.css-3f34ux { - font-size: 3.157334518321em; +.css-1at6569 { color: #333; + font-size: 32px; height: 64px; } @@ -110,13 +110,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-3f34ux { + .css-1at6569 { font-size: 1.4323121856191332em; } } <h1 - className="legacy__class css-3f34ux" + className="legacy__class css-1at6569" scale={2} > hello world @@ -124,24 +124,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-1vj1sv9 { - font-size: 20px; +.css-1lyjzdi { + font-size: 13.333333333333334px; } <h1 - className="legacy__class css-1vj1sv9" + className="legacy__class css-1lyjzdi" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-1vj1sv9 { - font-size: 20px; +.css-1lmko4z { + font-size: 40px; } <h1 - className="legacy__class css-1vj1sv9" + className="legacy__class css-1lmko4z" scale={2} > hello world @@ -149,18 +149,18 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-dlwa03 { +.css-14qd58u { + font-size: 20px; name: onyx; background-color: '#343a40'; flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; - font-size: 20px; } <div - className="css-dlwa03" + className="css-14qd58u" /> `; @@ -229,7 +229,7 @@ exports[`styled objects 1`] = ` } <h1 - className="some-class css-1j0hret" + className="some-class css-1j0hret" display="flex" > hello world @@ -237,15 +237,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-5q2sb2 { - font-size: 20px; - color: #8c81d8; - height: 64px; +.css-1vectwv { background-color: #ffd43b; + color: blue; + height: 64px; + font-size: 32px; } <span - className="css-5q2sb2" + className="css-1vectwv" scale={2} > hello world diff --git a/test/babel/__snapshots__/keyframes.test.js.snap b/test/babel/__snapshots__/keyframes.test.js.snap index ef2be43d4..3e9c0f8da 100644 --- a/test/babel/__snapshots__/keyframes.test.js.snap +++ b/test/babel/__snapshots__/keyframes.test.js.snap @@ -1,9 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel keyframes extract keyframes basic 1`] = ` -"import \\"./keyframes.test.emotion.css\\"; - -const rotate360 = \\"animation-rotate360-f35ahc\\";" +" +const rotate360 = keyframes([], [], function createEmotionStyledRules() { + return [{ + \\"from\\": { + \\"transform\\": \\"rotate(0deg)\\", + \\"WebkitTransform\\": \\"rotate(0deg)\\" + }, + \\"to\\": { + \\"transform\\": \\"rotate(360deg)\\", + \\"WebkitTransform\\": \\"rotate(360deg)\\" + } + }]; +});" `; exports[`babel keyframes extract keyframes basic 2`] = ` @@ -22,43 +32,48 @@ exports[`babel keyframes extract keyframes basic 2`] = ` exports[`babel keyframes extract keyframes with interpolation 1`] = ` " -const rotate360 = keyframes(\\"animation-rotate360-9bsj7q\\", [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(\${endingRotation}); - -ms-transform: rotate(\${endingRotation}); - transform: rotate(\${endingRotation}); - } }\`]);" +const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRules(x0) { + return [{ + \\"from\\": { + \\"transform\\": \\"rotate(0deg)\\", + \\"WebkitTransform\\": \\"rotate(0deg)\\" + }, + \\"to\\": { + \\"transform\\": \`rotate(\${x0})\`, + \\"WebkitTransform\\": \`rotate(\${x0})\` + } + }]; +});" `; exports[`babel keyframes inline keyframes basic 1`] = ` " -const rotate360 = keyframes(\\"animation-rotate360-10gy9ar\\", [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }\`]);" +const rotate360 = keyframes([], [], function createEmotionStyledRules() { + return [{ + \\"from\\": { + \\"transform\\": \\"rotate(0deg)\\", + \\"WebkitTransform\\": \\"rotate(0deg)\\" + }, + \\"to\\": { + \\"transform\\": \\"rotate(360deg)\\", + \\"WebkitTransform\\": \\"rotate(360deg)\\" + } + }]; +});" `; exports[`babel keyframes inline keyframes with interpolation 1`] = ` " -const rotate360 = keyframes(\\"animation-rotate360-r2494l\\", [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - to { - -webkit-transform: rotate(\${endingRotation}); - -ms-transform: rotate(\${endingRotation}); - transform: rotate(\${endingRotation}); - } }\`]);" +const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRules(x0) { + return [{ + \\"from\\": { + \\"transform\\": \\"rotate(0deg)\\", + \\"WebkitTransform\\": \\"rotate(0deg)\\" + }, + \\"to\\": { + \\"transform\\": \`rotate(\${x0})\`, + \\"WebkitTransform\\": \`rotate(\${x0})\` + } + }]; +});" `; diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index cde705b36..87b12c16d 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -3,19 +3,15 @@ exports[`babel macro css 1`] = ` "import { css as _css } from '../../src'; -_css(['css-kgvccl'], [widthVar], function createEmotionRules(x0) { - return [\`.css-kgvccl { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: \${x0}; }\`]; +_css([], [widthVar], function createEmotionStyledRules(x0) { + return [{ + 'margin': '12px 48px', + 'color': ['#ffffff', 'blue'], + 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'], + 'flex': '1 0 auto', + 'width': x0, + 'WebkitFlex': '1 0 auto' + }]; });" `; @@ -34,11 +30,13 @@ const someOtherVar = _flush;" exports[`babel macro fontFace 1`] = ` "import { fontFace as _fontFace } from '../../src'; -_fontFace([\`@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +_fontFace([], [], function createEmotionStyledRules() { + return [{ + 'fontFamily': 'MyHelvetica', + 'src': 'local(\\"Helvetica Neue Bold\\"),\\\\n local(\\"HelveticaNeue-Bold\\"),\\\\n url(MgOpenModernaBold.ttf)', + 'fontWeight': 'bold' + }]; +});" `; exports[`babel macro hydrate 1`] = ` @@ -50,57 +48,63 @@ const someOtherVar = _hydrate;" exports[`babel macro injectGlobal 1`] = ` "import { injectGlobal as _injectGlobal } from '../../src'; -_injectGlobal([\`body { - margin: 0; - padding: 0; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +_injectGlobal([], [], function createEmotionStyledRules() { + return [{ + 'body': { + 'margin': '0', + 'padding': '0', + '& > div': { + 'display': 'none' + } + }, + 'html': { + 'background': 'green' + } + }]; +});" `; exports[`babel macro keyframes 1`] = ` "import { keyframes as _keyframes } from '../../src'; -const rotate360 = _keyframes('animation-rotate360-1e2ipf5', [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); +const rotate360 = _keyframes([], [], function createEmotionStyledRules() { + return [{ + 'from': { + 'transform': 'rotate(0deg)', + 'WebkitTransform': 'rotate(0deg)' + }, + 'to': { + 'transform': 'rotate(360deg)', + 'WebkitTransform': 'rotate(360deg)' } - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }\`]);" + }]; +});" `; exports[`babel macro multiple imports 1`] = ` "import { keyframes as _keyframes, css as _css } from '../../src'; -const rotate360 = _keyframes('animation-rotate360-1e2ipf5', [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); +const rotate360 = _keyframes([], [], function createEmotionStyledRules() { + return [{ + 'from': { + 'transform': 'rotate(0deg)', + 'WebkitTransform': 'rotate(0deg)' + }, + 'to': { + 'transform': 'rotate(360deg)', + 'WebkitTransform': 'rotate(360deg)' } - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }\`]); -const thing = _css(['css-thing-dn301t'], [widthVar], function createEmotionRules(x0) { - return [\`.css-thing-dn301t { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: \${x0}; }\`]; + }]; +}); +const thing = _css([], [widthVar], function createEmotionStyledRules(x0) { + return [{ + 'margin': '12px 48px', + 'color': ['#ffffff', 'blue'], + 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'], + 'flex': '1 0 auto', + 'width': x0, + 'WebkitFlex': '1 0 auto' + }]; });" `; @@ -113,8 +117,10 @@ const someOtherVar = _thisDoesNotExist;" exports[`babel macro styled css from react 1`] = ` "import { css as _css } from '../../src/react'; -const someCls = _css(['css-someCls-ymvpej'], [], function createEmotionRules() { - return [\`.css-someCls-ymvpej { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const someCls = _css([], [], function createEmotionStyledRules() { + return [{ + 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + }]; });" `; @@ -123,7 +129,7 @@ exports[`babel macro styled object function 1`] = ` const SomeComponent = _styled('div', [{ display: ['-webkit-box', '-ms-flexbox', 'flex'] -}], []);" +}]);" `; exports[`babel macro styled object member 1`] = ` @@ -131,7 +137,7 @@ exports[`babel macro styled object member 1`] = ` const SomeComponent = _styled('div', [{ display: ['-webkit-box', '-ms-flexbox', 'flex'] -}], []);" +}]);" `; exports[`babel macro styled some import that does not exist 1`] = ` @@ -143,15 +149,19 @@ const someOtherVar = _thisDoesNotExist;" exports[`babel macro styled tagged template literal function 1`] = ` "import _styled from '../../src/react'; -const SomeComponent = _styled('div', ['css-SomeComponent-1q8jsgx'], [], function createEmotionStyledRules() { - return [\`.css-SomeComponent-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const SomeComponent = _styled('div', [], [], function createEmotionStyledRules() { + return [{ + 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + }]; });" `; exports[`babel macro styled tagged template literal member 1`] = ` "import _styled from '../../src/react'; -const SomeComponent = _styled('div', ['css-SomeComponent-1q8jsgx'], [], function createEmotionStyledRules() { - return [\`.css-SomeComponent-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const SomeComponent = _styled('div', [], [], function createEmotionStyledRules() { + return [{ + 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + }]; });" `; diff --git a/test/keyframes.test.js b/test/keyframes.test.js index 569d3b08d..21d12c046 100644 --- a/test/keyframes.test.js +++ b/test/keyframes.test.js @@ -8,6 +8,9 @@ import styled from '../src/react' expect.addSnapshotSerializer(serializer) expect.extend(matcher) +expect.addSnapshotSerializer(serializer) +expect.extend(matcher) + describe('keyframes', () => { test('renders', () => { const fontSize = 20 @@ -33,7 +36,6 @@ describe('keyframes', () => { ` const H1 = styled.h1` - font-size: ${fontSize}px; animation: ${bounce} 2s linear infinite; ` @@ -43,7 +45,9 @@ describe('keyframes', () => { }) test('keyframes with interpolation', () => { const endingRotation = '360deg' - keyframes` + + const H1 = styled.h1` + animation: ${keyframes` from { transform: rotate(0deg); } @@ -51,7 +55,13 @@ describe('keyframes', () => { to { transform: rotate(${endingRotation}); } + `} 2s linear infinite; ` + + const tree = renderer.create(<H1>hello world</H1>).toJSON() + + expect(tree).toMatchSnapshotWithEmotion() + expect( sheet.tags.map(tag => tag.textContent || '').join('') ).toMatchSnapshot() diff --git a/test/macro/__snapshots__/keyframes.test.js.snap b/test/macro/__snapshots__/keyframes.test.js.snap index 832a5245b..b4eea5f73 100644 --- a/test/macro/__snapshots__/keyframes.test.js.snap +++ b/test/macro/__snapshots__/keyframes.test.js.snap @@ -1,58 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`keyframes keyframes with interpolation 1`] = ` -"@keyframes animation-bounce-10a3qiv-cmo0tx { from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - -ms-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - -ms-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - -ms-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - -ms-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } }.css-H1-o0kcx2-fy8ana { font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; }@keyframes animation-1fpnjxj-1mmv2re { from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }" +exports[`keyframes - macro keyframes with interpolation 1`] = ` +.css-dg8t7k { + animation: animation_r3plcn 2s linear infinite; + -webkit-animation: animation_r3plcn 2s linear infinite; +} + +<h1 + className="css-dg8t7k" +> + hello world +</h1> `; -exports[`keyframes renders 1`] = ` -.css-H1-o0kcx2-fy8ana { - font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; +exports[`keyframes - macro keyframes with interpolation 2`] = `"@-webkit-keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}@keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}.css-y1pikg{animation:animation_sozpa3 2s linear infinite;-webkit-animation:animation_sozpa3 2s linear infinite;}@-webkit-keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}@keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}.css-dg8t7k{animation:animation_r3plcn 2s linear infinite;-webkit-animation:animation_r3plcn 2s linear infinite;}"`; + +exports[`keyframes - macro renders 1`] = ` +.css-y1pikg { + animation: animation_sozpa3 2s linear infinite; + -webkit-animation: animation_sozpa3 2s linear infinite; } <h1 - className="css-H1-o0kcx2-fy8ana css-H1-o0kcx2" + className="css-y1pikg" > hello world </h1> diff --git a/test/macro/__snapshots__/react.test.js.snap b/test/macro/__snapshots__/react.test.js.snap index 6cb806a6f..23ba3fdab 100644 --- a/test/macro/__snapshots__/react.test.js.snap +++ b/test/macro/__snapshots__/react.test.js.snap @@ -37,20 +37,20 @@ exports[`styled call expression 1`] = ` `; exports[`styled component as selector 1`] = ` -.css-1vj1sv9 { - font-size: 20px; +.css-1eki6f4 { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; } -.css-sgyhpk { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +.css-1ikz3g9 { + font-size: 20px; } <div - className="css-sgyhpk" + className="css-1eki6f4" > hello <h1 - className="css-1vj1sv9" + className="css-1ikz3g9" > This will be green </h1> @@ -59,14 +59,14 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-1rkkfxd { +.css-1baty9 { color: blue; height: 64px; - font-size: 20px; + font-size: 32px; } <h1 - className="css-1rkkfxd" + className="css-1baty9" scale={2} > hello world @@ -99,11 +99,11 @@ exports[`styled composes based on props 2`] = ` `; exports[`styled composes with objects 1`] = ` -.css-1yxo5lh { +.css-k8dg7v { color: #333; font-size: 1.333em; height: 64px; - font-size: 3.157334518321em; + font-size: 32px; } @media only screen and (-webkit-min-device-pixel-ratio: 1.5), @@ -111,13 +111,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-1yxo5lh { + .css-k8dg7v { font-size: 1.4323121856191332em; } } <h1 - className="css-1yxo5lh" + className="css-k8dg7v" scale={2} > hello world @@ -125,24 +125,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-1vj1sv9 { - font-size: 20px; +.css-1lyjzdi { + font-size: 13.333333333333334px; } <h1 - className="css-1vj1sv9" + className="css-1lyjzdi" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-1vj1sv9 { - font-size: 20px; +.css-1j40edd { + font-size: 40px; } <h1 - className="css-1vj1sv9" + className="css-1j40edd" scale={2} > hello world @@ -150,17 +150,17 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-1y1mvcu { +.css-87x20i { + font-size: 20px; background-color: '#343a40'; flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; - font-size: 20px; } <div - className="css-1y1mvcu" + className="css-87x20i" /> `; @@ -208,7 +208,7 @@ exports[`styled objects 1`] = ` } <h1 - className="some-class css-1j0hret" + className="some-class css-1j0hret" display="flex" > hello world @@ -216,15 +216,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-13nrqw7 { - color: #8c81d8; - height: 64px; - font-size: 20px; +.css-1vectwv { background-color: #ffd43b; + color: blue; + height: 64px; + font-size: 32px; } <span - className="css-13nrqw7" + className="css-1vectwv" scale={2} > hello world diff --git a/test/macro/keyframes.test.js b/test/macro/keyframes.test.js index 039e94d46..57393d15d 100644 --- a/test/macro/keyframes.test.js +++ b/test/macro/keyframes.test.js @@ -8,7 +8,7 @@ import styled from '../../src/react/macro' expect.addSnapshotSerializer(serializer) expect.extend(matcher) -describe('keyframes', () => { +describe('keyframes - macro', () => { test('renders', () => { const fontSize = 20 const bounce = keyframes` @@ -33,7 +33,6 @@ describe('keyframes', () => { ` const H1 = styled.h1` - font-size: ${fontSize}px; animation: ${bounce} 2s linear infinite; ` @@ -43,7 +42,9 @@ describe('keyframes', () => { }) test('keyframes with interpolation', () => { const endingRotation = '360deg' - keyframes` + + const H1 = styled.h1` + animation: ${keyframes` from { transform: rotate(0deg); } @@ -51,7 +52,13 @@ describe('keyframes', () => { to { transform: rotate(${endingRotation}); } + `} 2s linear infinite; ` + + const tree = renderer.create(<H1>hello world</H1>).toJSON() + + expect(tree).toMatchSnapshotWithEmotion() + expect( sheet.tags.map(tag => tag.textContent || '').join('') ).toMatchSnapshot() diff --git a/test/macro/react.test.js b/test/macro/react.test.js index 28a111474..97f61c21e 100644 --- a/test/macro/react.test.js +++ b/test/macro/react.test.js @@ -96,10 +96,10 @@ describe('styled', () => { test('function in expression', () => { const fontSize = 20 const H1 = styled('h1')` - font-size: ${fontSize + 'px'}; + font-size: ${fontSize}; ` - const H2 = styled(H1)`font-size: ${({ scale }) => fontSize * scale + 'px'}` + const H2 = styled(H1)`font-size: ${({ scale }) => fontSize * scale};` const tree = renderer .create( @@ -270,7 +270,6 @@ describe('styled', () => { const H1 = styled('h1')` composes: ${props => { - console.log(props) return props.a ? cssA : cssB }}; ` From c18043aaf251df3e220a7aeff7a8495793aaa9a8 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Wed, 19 Jul 2017 12:25:30 -0600 Subject: [PATCH 26/57] =?UTF-8?q?-=20injectGlobal=20=E2=9C=85=20-=20fontFa?= =?UTF-8?q?ce=20=E2=9C=85=20-=20prefixing=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/blocks/css-prop.example | 2 +- example/src/blocks/nested.example | 2 +- example/src/main.js | 2 +- example/src/markdown/index.js | 7 +- example/src/playground/index.js | 52 ++++----- src/babel.js | 18 +-- src/index.js | 30 ++++- src/inline.js | 2 +- src/parser.js | 9 +- src/utils.js | 2 +- test/__snapshots__/css-prop.test.js.snap | 20 ++-- test/__snapshots__/css.test.js.snap | 33 +++--- test/__snapshots__/font-face.test.js.snap | 20 +--- test/__snapshots__/inject-global.test.js.snap | 12 +- test/__snapshots__/keyframes.test.js.snap | 18 +-- test/__snapshots__/parser.test.js.snap | 38 ++++--- test/__snapshots__/react.test.js.snap | 47 ++++++-- test/__snapshots__/server.test.js.snap | 60 ++-------- test/babel/__snapshots__/css.test.js.snap | 5 +- .../__snapshots__/font-face.test.js.snap | 58 ++++++---- .../__snapshots__/inject-global.test.js.snap | 106 +++++++++++++----- .../__snapshots__/keyframes.test.js.snap | 40 ++++--- test/babel/__snapshots__/macro.test.js.snap | 47 +++++--- test/babel/__snapshots__/styled.test.js.snap | 21 +++- test/babel/macro.test.js | 12 ++ test/babel/styled.test.js | 12 ++ test/macro/__snapshots__/css.test.js.snap | 33 +++--- .../__snapshots__/inject-global.test.js.snap | 12 +- .../__snapshots__/keyframes.test.js.snap | 18 +-- test/macro/__snapshots__/react.test.js.snap | 9 +- test/react.test.js | 26 +++++ 31 files changed, 464 insertions(+), 309 deletions(-) diff --git a/example/src/blocks/css-prop.example b/example/src/blocks/css-prop.example index 815388e39..c26787a56 100644 --- a/example/src/blocks/css-prop.example +++ b/example/src/blocks/css-prop.example @@ -12,7 +12,7 @@ render( background-color: #8c81d8; border-radius: 4px; - img { + & img { width: 96px; height: 96px; border-radius: 50%; diff --git a/example/src/blocks/nested.example b/example/src/blocks/nested.example index 06ebdd237..b3c1e9c8a 100644 --- a/example/src/blocks/nested.example +++ b/example/src/blocks/nested.example @@ -1,5 +1,5 @@ const Avatar = styled('div')` - img { + & img { width: 96px; height: 96px; border-radius: 50%; diff --git a/example/src/main.js b/example/src/main.js index fabb6a03a..b5f983ae1 100755 --- a/example/src/main.js +++ b/example/src/main.js @@ -61,7 +61,7 @@ const theme = { const PlaygroundWrapper = styled('div')` font-family: 'Oxygen', sans-serif; flex:1; - color: attr(color, #343a40); + color: #343a40; background: #f8f9fa; & .inner { diff --git a/example/src/markdown/index.js b/example/src/markdown/index.js index 2defd9dd6..3127d4f7c 100644 --- a/example/src/markdown/index.js +++ b/example/src/markdown/index.js @@ -6,6 +6,7 @@ import styles from './index.css' const MarkdownContainer = styled('div')` composes: ${styles.markdownContainer}; + h1, h2, h3, h4, h5 { margin: 16px 0 8px 0; letter-spacing: 1px; @@ -19,7 +20,7 @@ const Link = styled('a')` text-decoration: none; color: ${p => p.theme.purple}; - p & { + & p & { margin: 0; } @@ -34,7 +35,7 @@ const Paragraph = styled('p')` font-size: 0.85rem; color: ${colors.gray[8]}; - a { + & a { font-size: 0.85rem; } @@ -47,7 +48,7 @@ const Code = styled('code')` background-color: ${colors.gray[1]}; padding: 1px; - p & { + & p & { font-size: 0.99rem; } ` diff --git a/example/src/playground/index.js b/example/src/playground/index.js index e95a608dd..a4683614e 100644 --- a/example/src/playground/index.js +++ b/example/src/playground/index.js @@ -11,105 +11,105 @@ import styled from 'emotion/react' import colors from 'open-color' const dracula = css` - .cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters { + & .cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters { background-color: #282a36 !important; color: #f8f8f2 !important; border: none; } - .cm-s-dracula .CodeMirror-gutters { + & .cm-s-dracula .CodeMirror-gutters { color: #282a36; } - .cm-s-dracula .CodeMirror-cursor { + & .cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f2 !important; } - .cm-s-dracula .CodeMirror-linenumber { + & .cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; } - .cm-s-dracula .CodeMirror-selected { + & .cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } - .cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { + & .cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } - .cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { + & .cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } - .cm-s-dracula span.cm-comment { + & .cm-s-dracula span.cm-comment { color: #6272a4; } - .cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { + & .cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; } - .cm-s-dracula span.cm-number { + & .cm-s-dracula span.cm-number { color: #bd93f9; } - .cm-s-dracula span.cm-variable { + & .cm-s-dracula span.cm-variable { color: #50fa7b; } - .cm-s-dracula span.cm-variable-2 { + & .cm-s-dracula span.cm-variable-2 { color: white; } - .cm-s-dracula span.cm-def { + & .cm-s-dracula span.cm-def { color: #50fa7b; } - .cm-s-dracula span.cm-operator { + & .cm-s-dracula span.cm-operator { color: #ff79c6; } - .cm-s-dracula span.cm-keyword { + & .cm-s-dracula span.cm-keyword { color: #ff79c6; } - .cm-s-dracula span.cm-atom { + & .cm-s-dracula span.cm-atom { color: #bd93f9; } - .cm-s-dracula span.cm-meta { + & .cm-s-dracula span.cm-meta { color: #f8f8f2; } - .cm-s-dracula span.cm-tag { + & .cm-s-dracula span.cm-tag { color: #ff79c6; } - .cm-s-dracula span.cm-attribute { + & .cm-s-dracula span.cm-attribute { color: #50fa7b; } - .cm-s-dracula span.cm-qualifier { + & .cm-s-dracula span.cm-qualifier { color: #50fa7b; } - .cm-s-dracula span.cm-property { + & .cm-s-dracula span.cm-property { color: #66d9ef; } - .cm-s-dracula span.cm-builtin { + & .cm-s-dracula span.cm-builtin { color: #50fa7b; } - .cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { + & .cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; } - .cm-s-dracula .CodeMirror-activeline-background { + & .cm-s-dracula .CodeMirror-activeline-background { background: rgba(255, 255, 255, 0.1); } - .cm-s-dracula .CodeMirror-matchingbracket { + & .cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } @@ -128,7 +128,7 @@ const PlaygroundContent = styled('div')` border-radius: 4px; } - .CodeMirror { + & .CodeMirror { height: auto; border-radius: 4px; } diff --git a/src/babel.js b/src/babel.js index 3f1013139..e47942e87 100644 --- a/src/babel.js +++ b/src/babel.js @@ -2,7 +2,6 @@ import fs from 'fs' import { basename } from 'path' import { touchSync } from 'touch' -import { BabelError, prettyError, buildCodeFrameError, createErrorWithLoc } from 'babel-errors'; import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' import forEach from '@arr/foreach' @@ -56,16 +55,19 @@ export function replaceCssWithCallExpression (path, identifier, state, t) { t.arrayExpression(path.node.quasi.expressions.slice(composesCount)), t.functionExpression( t.identifier('createEmotionStyledRules'), - path.node.quasi.expressions.slice(composesCount).map((x, i) => t.identifier(`x${i}`)), + path.node.quasi.expressions + .slice(composesCount) + .map((x, i) => t.identifier(`x${i}`)), t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) ) ]) ) } catch (e) { - console.log("throwing here") + console.log('throwing here', e) // let {line, column} = path.loc.start; // throw prettyError(createErrorWithLoc('Error at this position', line, column)); - throw buildCodeFrameError(path, e.message || 'Error'); + throw e + } } @@ -92,7 +94,9 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { t.arrayExpression(path.node.quasi.expressions.slice(composesCount)), t.functionExpression( t.identifier('createEmotionStyledRules'), - path.node.quasi.expressions.slice(composesCount).map((x, i) => t.identifier(`x${i}`)), + path.node.quasi.expressions + .slice(composesCount) + .map((x, i) => t.identifier(`x${i}`)), t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) ) ] @@ -453,15 +457,15 @@ export default function (babel) { ) } else if (path.node.tag.name === 'fontFace') { replaceCssWithCallExpression( - t.identifier('fontFace'), path, + t.identifier('fontFace'), state, t ) } else if (path.node.tag.name === 'injectGlobal') { replaceCssWithCallExpression( - t.identifier('injectGlobal'), path, + t.identifier('injectGlobal'), state, t ) diff --git a/src/index.js b/src/index.js index 5b747fde0..b1c92ea02 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ // @flow import { StyleSheet } from './sheet' -import { forEach, map } from './utils' +import { forEach, map, reduce } from './utils' import { hashString as hash, hashArray, hashObject } from './hash' import { createMarkupForStyles } from './glamor/CSSPropertyOperations' import clean from './glamor/clean.js' @@ -93,13 +93,15 @@ export function css (objs: any, vars: Array<any>, content: () => Array<any>) { return computedClassName.trim() } -function insert (css: string) { +function insertRawRule (css: string) { let spec = { id: hash(css, css.length), css, type: 'raw' } + register(spec) + if (!inserted[spec.id]) { sheet.insert(spec.css) inserted[spec.id] = true @@ -111,10 +113,30 @@ export function injectGlobal ( vars: Array<any>, content: () => Array<any> ) { - // ??? + const combined = content ? objs.concat(content.apply(null, vars)) : objs + + // injectGlobal is flattened by postcss + // if using objects we don't support nested + forEach(combined, obj => { + forEach(Object.keys(obj), selector => { + insertRawRule(`${selector} {${createMarkupForStyles(obj[selector])}}`) + }) + }) } -export const fontFace = injectGlobal +export function fontFace ( + objs: Array<any>, + vars: Array<any>, + content: () => Array<any> +) { + const combined = reduce( + content ? objs.concat(content.apply(null, vars)) : objs, + (accum, item, i) => Object.assign(accum, item), + {} + ) + + insertRawRule(`@font-face{${createMarkupForStyles(combined)}}`) +} function insertKeyframe (spec) { if (!inserted[spec.id]) { diff --git a/src/inline.js b/src/inline.js index ae9c566a5..1db25ba2c 100644 --- a/src/inline.js +++ b/src/inline.js @@ -95,5 +95,5 @@ export function injectGlobal ( } { let strs = quasi.quasis.map(x => x.value.cooked) let { src } = createRawStringFromQuasi(strs) - return parseCSS(`{ ${src} }`) + return parseCSS(src) } diff --git a/src/parser.js b/src/parser.js index e89d1fe0b..95ad549a6 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,14 +1,15 @@ // @flow import { t } from 'babel-types' -import prefixAll from 'inline-style-prefixer/static' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' -// import autoprefix from 'styled-components/lib/utils/autoprefix' +import autoprefix from 'styled-components/lib/utils/autoprefix' // import camelizeStyleName from 'fbjs/lib/camelizeStyleName' import postcssJs from 'postcss-js' import { objStyle } from './index' +let prefixer = postcssJs.sync([autoprefix ]); + type Rule = { parent: { selector: string, nodes: Array<mixed> }, selector: string, @@ -34,6 +35,8 @@ export function parseCSS ( let vars = 0 let composes: number = 0 + postcssNested(root) + root.walkRules((rule: Rule) => { // TODO: do this everywhere except `,xxx9xxx` if (/\bxxx\d+xxx/.exec(rule.selector)) { @@ -57,7 +60,7 @@ export function parseCSS ( } }) - const styles = prefixAll(postcssJs.objectify(root)) + const styles = prefixer(postcssJs.objectify(root)) return { styles, diff --git a/src/utils.js b/src/utils.js index 00febe4ac..89751be3a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -13,7 +13,7 @@ export function omit (obj: { [string]: any }, keys: Array<string>) { export function keys (obj: { [string]: any }) { let k: string let out: Array<string> = [] - for (k in obj) { +for (k in obj) { out.push(k) } return out diff --git a/test/__snapshots__/css-prop.test.js.snap b/test/__snapshots__/css-prop.test.js.snap index c21d0bb25..24b4dc62b 100644 --- a/test/__snapshots__/css-prop.test.js.snap +++ b/test/__snapshots__/css-prop.test.js.snap @@ -14,15 +14,17 @@ exports[`css prop react basic 1`] = ` `; exports[`css prop react kitchen sink 1`] = ` -.css-tocw18 { +.css-ggycvc { display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; font-weight: bold; - justify-content: center; - align-items: center; - -webkit-box-pack: center; -webkit-justify-content: center; - -webkit-box-align: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; -webkit-align-items: center; + -ms-flex-align: center; + -webkit-box-align: center; + align-items: center; } .css-2sukuz { @@ -34,8 +36,8 @@ exports[`css prop react kitchen sink 1`] = ` color: blue; } -.css-1pyxnyx { - display: -webkit-inline-box,-moz-inline-box,-ms-inline-flexbox,-webkit-inline-flex,inline-flex; +.css-1gq9vfz { + display: -webkit-box,-moz-box,-ms-inline-flexbox,-webkit-inline-flex,inline-flex; } .css-c5xqxn { @@ -49,7 +51,7 @@ exports[`css prop react kitchen sink 1`] = ` } <div - className="css__legacy-stuff css-tocw18" + className="css__legacy-stuff css-ggycvc" > <h1 className="css-2sukuz" @@ -62,7 +64,7 @@ exports[`css prop react kitchen sink 1`] = ` Hello </p> <p - className="test_class1 test___class45 css-1pyxnyx" + className="test_class1 test___class45 css-1gq9vfz" > World </p> diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 26db9b55c..f3d6d84d8 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -1,57 +1,60 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css composes 1`] = ` -.css-1746rll { +.css-1cjz2s1 { display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; - justify-content: center; - -webkit-box-pack: center; -webkit-justify-content: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; } <div - className="css-1746rll" + className="css-1cjz2s1" /> `; exports[`css composes with objects 1`] = ` -.css-v0v3tq { +.css-xcpja8 { display: -webkit-box,-ms-flexbox,flex,block; width: 30px; height: calc(40vw - 50px); - justify-content: center; - -webkit-box-pack: center; -webkit-justify-content: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; } -.css-v0v3tq:hover { +.css-xcpja8:hover { color: blue; } -.css-v0v3tq:after { +.css-xcpja8:after { content: " "; color: red; } @media (min-width: 420px) { - .css-v0v3tq { + .css-xcpja8 { color: green; } } <div - className="css-v0v3tq" + className="css-xcpja8" /> `; exports[`css composes with undefined values 1`] = ` -.css-9mu7jm { - justify-content: center; - -webkit-box-pack: center; +.css-1rsyrbq { -webkit-justify-content: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; } <div - className="css-9mu7jm" + className="css-1rsyrbq" /> `; diff --git a/test/__snapshots__/font-face.test.js.snap b/test/__snapshots__/font-face.test.js.snap index 5dc1c8505..2964673ce 100644 --- a/test/__snapshots__/font-face.test.js.snap +++ b/test/__snapshots__/font-face.test.js.snap @@ -1,21 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`font-face adds font-face to sheet 1`] = ` -"@font-face {font-family: 'Patrick Hand SC'; - font-style: normal; - font-weight: 400; - src: local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}" -` +exports[`font-face adds font-face to sheet 1`] = `"@font-face{font-family:'Patrick Hand SC';font-style:normal;font-weight:400;src:local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2');unicode-range:U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}"`; exports[`font-face adds font-face to sheet with interpolation 1`] = ` -"@font-face {font-family: 'Patrick Hand SC'; - font-style: normal; - font-weight: 400; - src: local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), +"@font-face{font-family:'Patrick Hand SC';font-style:normal;font-weight:400;src:local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2');unicode-range:U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:MyHelvetica;src:local(\\"Helvetica Neue Bold\\"), local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}" -` + url(MgOpenModernaBold.ttf);font-weight:bold;}" +`; diff --git a/test/__snapshots__/inject-global.test.js.snap b/test/__snapshots__/inject-global.test.js.snap index 3d8a6aaa3..afc879b40 100644 --- a/test/__snapshots__/inject-global.test.js.snap +++ b/test/__snapshots__/inject-global.test.js.snap @@ -1,13 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`injectGlobal 1`] = ` -"html { - background: pink; - }html.active { - background: red; - }body { - color: yellow; - margin: 0; - padding: 0; - }" -`; +exports[`injectGlobal 1`] = `"html {background:pink;}html.active {background:red;}body {color:yellow;margin:0px;padding:0px;}"`; diff --git a/test/__snapshots__/keyframes.test.js.snap b/test/__snapshots__/keyframes.test.js.snap index 101dec25e..c05f3f1c5 100644 --- a/test/__snapshots__/keyframes.test.js.snap +++ b/test/__snapshots__/keyframes.test.js.snap @@ -1,28 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes keyframes with interpolation 1`] = ` -.css-dg8t7k { - animation: animation_r3plcn 2s linear infinite; - -webkit-animation: animation_r3plcn 2s linear infinite; +.css-1ufgv90 { + -webkit-animation: animation_k1mnnl 2s linear infinite; + animation: animation_k1mnnl 2s linear infinite; } <h1 - className="css-dg8t7k" + className="css-1ufgv90" > hello world </h1> `; -exports[`keyframes keyframes with interpolation 2`] = `"@-webkit-keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}@keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}.css-y1pikg{animation:animation_sozpa3 2s linear infinite;-webkit-animation:animation_sozpa3 2s linear infinite;}@-webkit-keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}@keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}.css-dg8t7k{animation:animation_r3plcn 2s linear infinite;-webkit-animation:animation_r3plcn 2s linear infinite;}"`; +exports[`keyframes keyframes with interpolation 2`] = `"@-webkit-keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-xgcxry{-webkit-animation:animation_1w7bh5k 2s linear infinite;animation:animation_1w7bh5k 2s linear infinite;}@-webkit-keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}.css-1ufgv90{-webkit-animation:animation_k1mnnl 2s linear infinite;animation:animation_k1mnnl 2s linear infinite;}"`; exports[`keyframes renders 1`] = ` -.css-y1pikg { - animation: animation_sozpa3 2s linear infinite; - -webkit-animation: animation_sozpa3 2s linear infinite; +.css-xgcxry { + -webkit-animation: animation_1w7bh5k 2s linear infinite; + animation: animation_1w7bh5k 2s linear infinite; } <h1 - className="css-y1pikg" + className="css-xgcxry" > hello world </h1> diff --git a/test/__snapshots__/parser.test.js.snap b/test/__snapshots__/parser.test.js.snap index 9cbaca289..0c3743916 100644 --- a/test/__snapshots__/parser.test.js.snap +++ b/test/__snapshots__/parser.test.js.snap @@ -16,6 +16,7 @@ Object { "flex", ], "justifyContent": "center", + "msFlexPack": "center", "width": "var(--css-hash-0)", }, }, @@ -28,22 +29,6 @@ Object { "isStaticBlock": true, "styles": Object { ".some-selector": Object { - "&::before": Object { - "color": "blue", - "content": "'pseudo'", - "height": "20px", - "width": "20px", - }, - "&:hover": Object { - "backgroundColor": "green", - }, - "@media (max-width: 500px)": Object { - "height": "var(--css-hash-1)", - "position": "fixed", - }, - "@media print": Object { - "display": "none", - }, "WebkitBoxPack": "center", "WebkitJustifyContent": "center", "display": Array [ @@ -54,8 +39,29 @@ Object { "flex", ], "justifyContent": "center", + "msFlexPack": "center", "width": "var(--css-hash-0)", }, + ".some-selector::before": Object { + "color": "blue", + "content": "'pseudo'", + "height": "20px", + "width": "20px", + }, + ".some-selector:hover": Object { + "backgroundColor": "green", + }, + "@media (max-width: 500px)": Object { + ".some-selector": Object { + "height": "var(--css-hash-1)", + "position": "fixed", + }, + }, + "@media print": Object { + ".some-selector": Object { + "display": "none", + }, + }, }, } `; diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 5ab24870a..9036f2227 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -38,14 +38,14 @@ exports[`styled call expression 1`] = ` `; exports[`styled component as selector 1`] = ` -.css-1eki6f4 { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; -} - .css-1ikz3g9 { font-size: 20px; } +.css-1eki6f4 { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +} + <div className="css-1eki6f4" > @@ -149,18 +149,19 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-14qd58u { +.css-15zlqsx { font-size: 20px; name: onyx; background-color: '#343a40'; - flex-direction: column; + -webkit-flex-direction: column; + -ms-flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; - -webkit-flex-direction: column; + flex-direction: column; } <div - className="css-14qd58u" + className="css-15zlqsx" /> `; @@ -189,6 +190,36 @@ exports[`styled name 1`] = ` </h1> `; +exports[`styled nested 1`] = ` +.css-1msxvyz { + display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +} + +.css-1msxvyz div { + color: green; +} + +.css-1msxvyz div span { + color: red; +} + +.css-1ikz3g9 { + font-size: 20px; +} + +<div + className="css-1msxvyz" +> + hello + <h1 + className="css-1ikz3g9" + > + This will be green + </h1> + world +</div> +`; + exports[`styled no dynamic 1`] = ` .css-f8g05s { font-size: 12px; diff --git a/test/__snapshots__/server.test.js.snap b/test/__snapshots__/server.test.js.snap index a1135c476..29121a411 100644 --- a/test/__snapshots__/server.test.js.snap +++ b/test/__snapshots__/server.test.js.snap @@ -2,67 +2,21 @@ exports[`extractCritical returns static css 1`] = ` Object { - "css": ".no-prefix { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - }.css-Main-17vxl0k-1g2c78b { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-Image-1d2rnu3-1tqe2cj { border-radius: 50%; - height: 30px; - width: 30px; - background-color: red }.css-Image-1d2rnu3-1rejdxz { border-radius: 50%; - height: 100px; - width: 100px; - background-color: red }.css-Image-1d2rnu3-1d9ph5s { border-radius: 50%; - height: 50px; - width: 50px; - background-color: red }", - "html": "<main class=\\"css-Main-17vxl0k-1g2c78b css-Main-17vxl0k\\" data-reactroot=\\"\\" data-reactid=\\"1\\" data-react-checksum=\\"-2053673081\\"><img size=\\"30\\" class=\\"css-Image-1d2rnu3-1tqe2cj css-Image-1d2rnu3\\" data-reactid=\\"2\\"/><img size=\\"100\\" class=\\"css-Image-1d2rnu3-1rejdxz css-Image-1d2rnu3\\" data-reactid=\\"3\\"/><img class=\\"css-Image-1d2rnu3-1d9ph5s css-Image-1d2rnu3\\" data-reactid=\\"4\\"/></main>", + "css": ".no-prefix {display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;-webkit-justify-content:center;-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;}.css-1666ym{display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;}.css-hp1yas{border-radius:50%;height:50px;width:50px;background-color:red;}", + "html": "<main class=\\"css-1666ym\\" data-reactroot=\\"\\" data-reactid=\\"1\\" data-react-checksum=\\"1172063568\\"><img size=\\"30\\" class=\\"css-hp1yas\\" data-reactid=\\"2\\"/><img size=\\"100\\" class=\\"css-hp1yas\\" data-reactid=\\"3\\"/><img class=\\"css-hp1yas\\" data-reactid=\\"4\\"/></main>", "ids": Array [ - "1g2c78b", - "1tqe2cj", - "1rejdxz", - "1d9ph5s", + "1666ym", + "hp1yas", ], "rules": Array [ Object { - "cssText": ".no-prefix { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - }", + "cssText": ".no-prefix {display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;-webkit-justify-content:center;-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;}", }, Object { - "cssText": ".css-Main-17vxl0k-1g2c78b { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }", + "cssText": ".css-1666ym{display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;}", }, Object { - "cssText": ".css-Image-1d2rnu3-1tqe2cj { border-radius: 50%; - height: 30px; - width: 30px; - background-color: red }", - }, - Object { - "cssText": ".css-Image-1d2rnu3-1rejdxz { border-radius: 50%; - height: 100px; - width: 100px; - background-color: red }", - }, - Object { - "cssText": ".css-Image-1d2rnu3-1d9ph5s { border-radius: 50%; - height: 50px; - width: 50px; - background-color: red }", + "cssText": ".css-hp1yas{border-radius:50%;height:50px;width:50px;background-color:red;}", }, ], } diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index 26c041e7d..66794c8e5 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -151,9 +151,10 @@ css([], [widthVar], function createEmotionStyledRules(x0) { \\"margin\\": \\"12px 48px\\", \\"color\\": [\\"#ffffff\\", \\"blue\\"], \\"display\\": [\\"-webkit-box\\", \\"-moz-box\\", \\"-ms-flexbox\\", \\"-webkit-flex\\", \\"flex\\"], + \\"WebkitFlex\\": \\"1 0 auto\\", + \\"msFlex\\": \\"1 0 auto\\", \\"flex\\": \\"1 0 auto\\", - \\"width\\": x0, - \\"WebkitFlex\\": \\"1 0 auto\\" + \\"width\\": x0 }]; });" `; diff --git a/test/babel/__snapshots__/font-face.test.js.snap b/test/babel/__snapshots__/font-face.test.js.snap index 3f2864075..6887c1f6f 100644 --- a/test/babel/__snapshots__/font-face.test.js.snap +++ b/test/babel/__snapshots__/font-face.test.js.snap @@ -1,6 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`fontFace babel extract basic 1`] = `"import \\"./font-face.test.emotion.css\\";"`; +exports[`fontFace babel extract basic 1`] = ` +" +fontFace([], [], function createEmotionStyledRules() { + return [{ + \\"fontFamily\\": \\"MyHelvetica\\", + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" +`; exports[`fontFace babel extract basic 2`] = ` "@font-face {font-family: MyHelvetica; @@ -11,9 +20,14 @@ exports[`fontFace babel extract basic 2`] = ` `; exports[`fontFace babel extract basic assign to variable 1`] = ` -"import \\"./font-face.test.emotion.css\\"; - -const thisWillBeUndefined = undefined;" +" +const thisWillBeUndefined = fontFace([], [], function createEmotionStyledRules() { + return [{ + \\"fontFamily\\": \\"MyHelvetica\\", + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" `; exports[`fontFace babel extract basic assign to variable 2`] = ` @@ -26,27 +40,33 @@ exports[`fontFace babel extract basic assign to variable 2`] = ` exports[`fontFace babel extract interpolation 1`] = ` " -fontFace([\`@font-face {font-family: \${fontFamilyName}; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +fontFace([], [fontFamilyName], function createEmotionStyledRules(x0) { + return [{ + \\"fontFamily\\": x0, + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" `; exports[`fontFace babel inline basic 1`] = ` " -fontFace([\`@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +fontFace([], [], function createEmotionStyledRules() { + return [{ + \\"fontFamily\\": \\"MyHelvetica\\", + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" `; exports[`fontFace babel inline interpolation 1`] = ` " -fontFace([\`@font-face {font-family: \${fontFamilyName}; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +fontFace([], [fontFamilyName], function createEmotionStyledRules(x0) { + return [{ + \\"fontFamily\\": x0, + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" `; diff --git a/test/babel/__snapshots__/inject-global.test.js.snap b/test/babel/__snapshots__/inject-global.test.js.snap index e20412de4..aebe37fe9 100644 --- a/test/babel/__snapshots__/inject-global.test.js.snap +++ b/test/babel/__snapshots__/inject-global.test.js.snap @@ -1,9 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel injectGlobal extract injectGlobal assign to variable 1`] = ` -"import \\"./inject-global.test.emotion.css\\"; - -const thisWillBeUndefined = undefined;" +" +const thisWillBeUndefined = injectGlobal([], [], function createEmotionStyledRules() { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\" + }, + \\"body > div\\": { + \\"display\\": \\"none\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" `; exports[`babel injectGlobal extract injectGlobal assign to variable 2`] = ` @@ -19,7 +31,23 @@ html { }" `; -exports[`babel injectGlobal extract injectGlobal basic 1`] = `"import \\"./inject-global.test.emotion.css\\";"`; +exports[`babel injectGlobal extract injectGlobal basic 1`] = ` +" +injectGlobal([], [], function createEmotionStyledRules() { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\" + }, + \\"body > div\\": { + \\"display\\": \\"none\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" +`; exports[`babel injectGlobal extract injectGlobal basic 2`] = ` "body { @@ -36,38 +64,56 @@ html { exports[`babel injectGlobal extract injectGlobal with interpolation 1`] = ` " -injectGlobal([\`body { - margin: 0; - padding: 0; - display: \${display}; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +injectGlobal([], [display], function createEmotionStyledRules(x0) { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\", + \\"display\\": x0 + }, + \\"body > div\\": { + \\"display\\": \\"none\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" `; exports[`babel injectGlobal inline injectGlobal basic 1`] = ` " -injectGlobal([\`body { - margin: 0; - padding: 0; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +injectGlobal([], [], function createEmotionStyledRules() { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\" + }, + \\"body > div\\": { + \\"display\\": \\"none\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" `; exports[`babel injectGlobal inline injectGlobal with interpolation 1`] = ` " -injectGlobal([\`body { - margin: 0; - padding: 0; - display: \${display}; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +injectGlobal([], [display], function createEmotionStyledRules(x0) { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\", + \\"display\\": x0 + }, + \\"body > div\\": { + \\"display\\": \\"none\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" `; diff --git a/test/babel/__snapshots__/keyframes.test.js.snap b/test/babel/__snapshots__/keyframes.test.js.snap index 3e9c0f8da..29699fbe6 100644 --- a/test/babel/__snapshots__/keyframes.test.js.snap +++ b/test/babel/__snapshots__/keyframes.test.js.snap @@ -5,12 +5,14 @@ exports[`babel keyframes extract keyframes basic 1`] = ` const rotate360 = keyframes([], [], function createEmotionStyledRules() { return [{ \\"from\\": { - \\"transform\\": \\"rotate(0deg)\\", - \\"WebkitTransform\\": \\"rotate(0deg)\\" + \\"WebkitTransform\\": \\"rotate(0deg)\\", + \\"msTransform\\": \\"rotate(0deg)\\", + \\"transform\\": \\"rotate(0deg)\\" }, \\"to\\": { - \\"transform\\": \\"rotate(360deg)\\", - \\"WebkitTransform\\": \\"rotate(360deg)\\" + \\"WebkitTransform\\": \\"rotate(360deg)\\", + \\"msTransform\\": \\"rotate(360deg)\\", + \\"transform\\": \\"rotate(360deg)\\" } }]; });" @@ -35,12 +37,14 @@ exports[`babel keyframes extract keyframes with interpolation 1`] = ` const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRules(x0) { return [{ \\"from\\": { - \\"transform\\": \\"rotate(0deg)\\", - \\"WebkitTransform\\": \\"rotate(0deg)\\" + \\"WebkitTransform\\": \\"rotate(0deg)\\", + \\"msTransform\\": \\"rotate(0deg)\\", + \\"transform\\": \\"rotate(0deg)\\" }, \\"to\\": { - \\"transform\\": \`rotate(\${x0})\`, - \\"WebkitTransform\\": \`rotate(\${x0})\` + \\"WebkitTransform\\": \`rotate(\${x0})\`, + \\"msTransform\\": \`rotate(\${x0})\`, + \\"transform\\": \`rotate(\${x0})\` } }]; });" @@ -51,12 +55,14 @@ exports[`babel keyframes inline keyframes basic 1`] = ` const rotate360 = keyframes([], [], function createEmotionStyledRules() { return [{ \\"from\\": { - \\"transform\\": \\"rotate(0deg)\\", - \\"WebkitTransform\\": \\"rotate(0deg)\\" + \\"WebkitTransform\\": \\"rotate(0deg)\\", + \\"msTransform\\": \\"rotate(0deg)\\", + \\"transform\\": \\"rotate(0deg)\\" }, \\"to\\": { - \\"transform\\": \\"rotate(360deg)\\", - \\"WebkitTransform\\": \\"rotate(360deg)\\" + \\"WebkitTransform\\": \\"rotate(360deg)\\", + \\"msTransform\\": \\"rotate(360deg)\\", + \\"transform\\": \\"rotate(360deg)\\" } }]; });" @@ -67,12 +73,14 @@ exports[`babel keyframes inline keyframes with interpolation 1`] = ` const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRules(x0) { return [{ \\"from\\": { - \\"transform\\": \\"rotate(0deg)\\", - \\"WebkitTransform\\": \\"rotate(0deg)\\" + \\"WebkitTransform\\": \\"rotate(0deg)\\", + \\"msTransform\\": \\"rotate(0deg)\\", + \\"transform\\": \\"rotate(0deg)\\" }, \\"to\\": { - \\"transform\\": \`rotate(\${x0})\`, - \\"WebkitTransform\\": \`rotate(\${x0})\` + \\"WebkitTransform\\": \`rotate(\${x0})\`, + \\"msTransform\\": \`rotate(\${x0})\`, + \\"transform\\": \`rotate(\${x0})\` } }]; });" diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index 87b12c16d..70fc166c3 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -8,9 +8,10 @@ _css([], [widthVar], function createEmotionStyledRules(x0) { 'margin': '12px 48px', 'color': ['#ffffff', 'blue'], 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'], + 'WebkitFlex': '1 0 auto', + 'msFlex': '1 0 auto', 'flex': '1 0 auto', - 'width': x0, - 'WebkitFlex': '1 0 auto' + 'width': x0 }]; });" `; @@ -52,10 +53,19 @@ _injectGlobal([], [], function createEmotionStyledRules() { return [{ 'body': { 'margin': '0', - 'padding': '0', - '& > div': { - 'display': 'none' - } + 'padding': '0' + }, + 'body > div': { + 'display': 'none' + }, + 'body > div:hover': { + 'color': 'green' + }, + 'body > div:hover span': { + 'color': 'red' + }, + 'body > div:hover span:after': { + 'content': '\\"end of line\\"' }, 'html': { 'background': 'green' @@ -70,12 +80,14 @@ exports[`babel macro keyframes 1`] = ` const rotate360 = _keyframes([], [], function createEmotionStyledRules() { return [{ 'from': { - 'transform': 'rotate(0deg)', - 'WebkitTransform': 'rotate(0deg)' + 'WebkitTransform': 'rotate(0deg)', + 'msTransform': 'rotate(0deg)', + 'transform': 'rotate(0deg)' }, 'to': { - 'transform': 'rotate(360deg)', - 'WebkitTransform': 'rotate(360deg)' + 'WebkitTransform': 'rotate(360deg)', + 'msTransform': 'rotate(360deg)', + 'transform': 'rotate(360deg)' } }]; });" @@ -87,12 +99,14 @@ exports[`babel macro multiple imports 1`] = ` const rotate360 = _keyframes([], [], function createEmotionStyledRules() { return [{ 'from': { - 'transform': 'rotate(0deg)', - 'WebkitTransform': 'rotate(0deg)' + 'WebkitTransform': 'rotate(0deg)', + 'msTransform': 'rotate(0deg)', + 'transform': 'rotate(0deg)' }, 'to': { - 'transform': 'rotate(360deg)', - 'WebkitTransform': 'rotate(360deg)' + 'WebkitTransform': 'rotate(360deg)', + 'msTransform': 'rotate(360deg)', + 'transform': 'rotate(360deg)' } }]; }); @@ -101,9 +115,10 @@ const thing = _css([], [widthVar], function createEmotionStyledRules(x0) { 'margin': '12px 48px', 'color': ['#ffffff', 'blue'], 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'], + 'WebkitFlex': '1 0 auto', + 'msFlex': '1 0 auto', 'flex': '1 0 auto', - 'width': x0, - 'WebkitFlex': '1 0 auto' + 'width': x0 }]; });" `; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index b9e28a9d8..7b491a813 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -65,8 +65,9 @@ const H1 = styled('h1', [props => { return [{ 'fontSize': x0, 'height': '20px', - 'transform': \`translateX(\${x1})\`, - 'WebkitTransform': \`translateX(\${x1})\` + 'WebkitTransform': \`translateX(\${x1})\`, + 'msTransform': \`translateX(\${x1})\`, + 'transform': \`translateX(\${x1})\` }]; });" `; @@ -85,6 +86,8 @@ const H1 = styled('h1', [], [fontSize + 'px', props => props.translateX, somethi return [{ 'fontSize': x0, 'height': '20px', + 'WebkitTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], + 'msTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], 'transform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], 'height1': \`\${x2}wow\`, 'width': \`w\${x3}ow\`, @@ -129,6 +132,20 @@ css([], [], function createEmotionStyledRules() { });" `; +exports[`babel styled component inline nested 1`] = ` +"const H1 = styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { + return [{ + 'fontSize': x0, + '& div': { + 'color': 'blue' + }, + '& div span': { + 'color': 'red' + } + }]; +});" +`; + exports[`babel styled component inline no dynamic 1`] = ` "styled(\\"h1\\", [], [], function createEmotionStyledRules() { return [{ diff --git a/test/babel/macro.test.js b/test/babel/macro.test.js index 2118ce76e..d8da9368d 100644 --- a/test/babel/macro.test.js +++ b/test/babel/macro.test.js @@ -109,6 +109,18 @@ describe('babel macro', () => { padding: 0; & > div { display: none; + + &:hover { + color: green; + + & span { + color: red; + + &:after { + content: "end of line" + } + } + } } } html { diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index c0b0704fe..098fd0770 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -48,6 +48,18 @@ describe('babel styled component', () => { expect(code).toMatchSnapshot() }) + test('nested', () => { + const basic = "const H1 = styled.h1`" + + "font-size: ${fontSize + 'px'};" + + "& div { color: blue;" + + "& span { color: red } }" + + "`" + const {code} = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + test('interpolation in different places', () => { const basic = ` const H1 = styled.h1\` diff --git a/test/macro/__snapshots__/css.test.js.snap b/test/macro/__snapshots__/css.test.js.snap index 0510de90e..77bebdf75 100644 --- a/test/macro/__snapshots__/css.test.js.snap +++ b/test/macro/__snapshots__/css.test.js.snap @@ -1,57 +1,60 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css macro composes 1`] = ` -.css-1746rll { +.css-1cjz2s1 { display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; - justify-content: center; - -webkit-box-pack: center; -webkit-justify-content: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; } <div - className="css-1746rll" + className="css-1cjz2s1" /> `; exports[`css macro composes with objects 1`] = ` -.css-1ko8dz { +.css-1vx1y9j { display: flex,block; width: 30px; height: calc(40vw - 50px); - justify-content: center; - -webkit-box-pack: center; -webkit-justify-content: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; } -.css-1ko8dz:hover { +.css-1vx1y9j:hover { color: blue; } -.css-1ko8dz:after { +.css-1vx1y9j:after { content: " "; color: red; } @media (min-width: 420px) { - .css-1ko8dz { + .css-1vx1y9j { color: green; } } <div - className="css-1ko8dz" + className="css-1vx1y9j" /> `; exports[`css macro composes with undefined values 1`] = ` -.css-9mu7jm { - justify-content: center; - -webkit-box-pack: center; +.css-1rsyrbq { -webkit-justify-content: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; } <div - className="css-9mu7jm" + className="css-1rsyrbq" /> `; diff --git a/test/macro/__snapshots__/inject-global.test.js.snap b/test/macro/__snapshots__/inject-global.test.js.snap index 3d8a6aaa3..afc879b40 100644 --- a/test/macro/__snapshots__/inject-global.test.js.snap +++ b/test/macro/__snapshots__/inject-global.test.js.snap @@ -1,13 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`injectGlobal 1`] = ` -"html { - background: pink; - }html.active { - background: red; - }body { - color: yellow; - margin: 0; - padding: 0; - }" -`; +exports[`injectGlobal 1`] = `"html {background:pink;}html.active {background:red;}body {color:yellow;margin:0px;padding:0px;}"`; diff --git a/test/macro/__snapshots__/keyframes.test.js.snap b/test/macro/__snapshots__/keyframes.test.js.snap index b4eea5f73..c61672940 100644 --- a/test/macro/__snapshots__/keyframes.test.js.snap +++ b/test/macro/__snapshots__/keyframes.test.js.snap @@ -1,28 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes - macro keyframes with interpolation 1`] = ` -.css-dg8t7k { - animation: animation_r3plcn 2s linear infinite; - -webkit-animation: animation_r3plcn 2s linear infinite; +.css-1ufgv90 { + -webkit-animation: animation_k1mnnl 2s linear infinite; + animation: animation_k1mnnl 2s linear infinite; } <h1 - className="css-dg8t7k" + className="css-1ufgv90" > hello world </h1> `; -exports[`keyframes - macro keyframes with interpolation 2`] = `"@-webkit-keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}@keyframes animation_sozpa3{from, 20%, 53%, 80%, to {animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);transform:translate3d(0,0,0);-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);}40%, 43% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -30px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);}70% {animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);transform:translate3d(0, -15px, 0);-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);}90% {transform:translate3d(0,-4px,0);-webkit-transform:translate3d(0,-4px,0);}}.css-y1pikg{animation:animation_sozpa3 2s linear infinite;-webkit-animation:animation_sozpa3 2s linear infinite;}@-webkit-keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}@keyframes animation_r3plcn{from {transform:rotate(0deg);-webkit-transform:rotate(0deg);}to {transform:rotate(360deg);-webkit-transform:rotate(360deg);}}.css-dg8t7k{animation:animation_r3plcn 2s linear infinite;-webkit-animation:animation_r3plcn 2s linear infinite;}"`; +exports[`keyframes - macro keyframes with interpolation 2`] = `"@-webkit-keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-xgcxry{-webkit-animation:animation_1w7bh5k 2s linear infinite;animation:animation_1w7bh5k 2s linear infinite;}@-webkit-keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}.css-1ufgv90{-webkit-animation:animation_k1mnnl 2s linear infinite;animation:animation_k1mnnl 2s linear infinite;}"`; exports[`keyframes - macro renders 1`] = ` -.css-y1pikg { - animation: animation_sozpa3 2s linear infinite; - -webkit-animation: animation_sozpa3 2s linear infinite; +.css-xgcxry { + -webkit-animation: animation_1w7bh5k 2s linear infinite; + animation: animation_1w7bh5k 2s linear infinite; } <h1 - className="css-y1pikg" + className="css-xgcxry" > hello world </h1> diff --git a/test/macro/__snapshots__/react.test.js.snap b/test/macro/__snapshots__/react.test.js.snap index 23ba3fdab..a1b074ba0 100644 --- a/test/macro/__snapshots__/react.test.js.snap +++ b/test/macro/__snapshots__/react.test.js.snap @@ -150,17 +150,18 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-87x20i { +.css-lid1wd { font-size: 20px; background-color: '#343a40'; - flex-direction: column; + -webkit-flex-direction: column; + -ms-flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; - -webkit-flex-direction: column; + flex-direction: column; } <div - className="css-87x20i" + className="css-lid1wd" /> `; diff --git a/test/react.test.js b/test/react.test.js index 5a498c23e..fef6de6b5 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -66,6 +66,32 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) + test('nested', () => { + const fontSize = '20px' + const H1 = styled.h1`font-size: ${fontSize};` + + const Thing = styled.div` + display: flex; + & div { + color: green; + + & span { + color: red; + } + } + ` + + const tree = renderer + .create( + <Thing> + hello <H1>This will be green</H1> world + </Thing> + ) + .toJSON() + + expect(tree).toMatchSnapshotWithEmotion() + }) + test('composition', () => { const fontSize = 20 const H1 = styled('h1')` From d73affe1ae1b81d34e99d6542ebce6ad4cd21782 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Thu, 20 Jul 2017 00:24:19 -0600 Subject: [PATCH 27/57] =?UTF-8?q?=F0=9F=8E=89=20All=20tests=20passing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/src/main.js | 4 +- example/src/preview/index.js | 4 +- example/webpack.config.js | 44 ++-- src/babel-utils.js | 4 +- src/babel.js | 133 ++++++----- src/glamor/clean.js | 2 +- src/index.js | 4 +- src/inline.js | 69 +++--- src/macro.js | 10 +- src/parser.js | 20 +- src/react/index.js | 4 +- src/utils.js | 2 +- test/__snapshots__/parser.test.js.snap | 12 +- .../babel/__snapshots__/css-prop.test.js.snap | 13 +- test/babel/__snapshots__/css.test.js.snap | 210 ++++++++++-------- .../__snapshots__/font-face.test.js.snap | 38 ++-- test/babel/__snapshots__/fs.test.js.snap | 58 ++--- .../__snapshots__/inject-global.test.js.snap | 60 ++--- .../__snapshots__/keyframes.test.js.snap | 41 ++-- test/babel/__snapshots__/styled.test.js.snap | 78 +++---- test/babel/css-prop.test.js | 10 +- test/babel/css.test.js | 57 ++--- test/babel/font-face.test.js | 6 +- test/babel/fs.test.js | 7 +- test/babel/inject-global.test.js | 9 +- test/babel/keyframes.test.js | 4 +- test/babel/macro.test.js | 12 +- test/babel/styled.test.js | 67 +----- test/css-prop.test.js | 8 +- test/css.test.js | 12 +- test/extract/.babelrc | 2 +- .../__snapshots__/extract.test.js.snap | 134 +++-------- test/extract/extract.test.emotion.css | 56 +++-- test/extract/extract.test.js | 130 ++--------- test/macro/css.test.js | 14 +- test/parser.test.js | 2 +- test/react.test.js | 20 +- test/server.test.js | 4 +- 38 files changed, 561 insertions(+), 803 deletions(-) diff --git a/example/src/main.js b/example/src/main.js index b5f983ae1..db47fb4d6 100755 --- a/example/src/main.js +++ b/example/src/main.js @@ -1,7 +1,7 @@ import React from 'react' import { render } from 'react-dom' import { ThemeProvider } from 'emotion/react/theming' -import styled, { css, fontFace, keyframes, injectGlobal } from 'emotion/react' +import styled, { css, fontFace, keyframes, injectGlobal } from 'emotion/react' import Markdown from './markdown' import Playground from './playground' import logoUrl from '../../emotion.png' @@ -225,7 +225,7 @@ class App extends React.Component { {/* }} */} {/* /> */} - <Markdown markdown={require('../../docs/theming.md')}/> + <Markdown markdown={require('../../docs/theming.md')} /> <Playground maxHeight={180} noRender={false} diff --git a/example/src/preview/index.js b/example/src/preview/index.js index 7e1263407..51b290c2b 100644 --- a/example/src/preview/index.js +++ b/example/src/preview/index.js @@ -5,9 +5,7 @@ import PropTypes from 'prop-types' import { registerPlugin, transform } from 'babel-standalone' import styled from 'emotion/react' -const ric = - window.requestIdleCallback || - window.requestAnimationFrame +const ric = window.requestIdleCallback || window.requestAnimationFrame registerPlugin('emotion/babel', require('emotion/babel')) diff --git a/example/webpack.config.js b/example/webpack.config.js index f7431c79b..e3e7b8b22 100755 --- a/example/webpack.config.js +++ b/example/webpack.config.js @@ -14,34 +14,32 @@ module.exports = env => { { test: /\.css$/, exclude: /emotion\.css$/, - use: PROD ? ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - options: { - sourceMap: true, - modules: true + use: PROD + ? ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: { + loader: 'css-loader', + options: { + sourceMap: true, + modules: true + } } - } - }) : [ - 'style-loader', - { loader: 'css-loader', options: { modules: true } } - ] + }) + : ['style-loader', { loader: 'css-loader', options: { modules: true } }] }, { test: /emotion\.css$/, - use: PROD ? ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - options: { - sourceMap: true + use: PROD + ? ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: { + loader: 'css-loader', + options: { + sourceMap: true + } } - } - }) : [ - 'style-loader', - { loader: 'css-loader' } - ] + }) + : ['style-loader', { loader: 'css-loader' }] }, { test: /\.(jpg|png|svg)$/, diff --git a/src/babel-utils.js b/src/babel-utils.js index 8d60eacd2..2687a2786 100644 --- a/src/babel-utils.js +++ b/src/babel-utils.js @@ -20,7 +20,9 @@ export function getRuntimeImportPath (path, t) { export function buildMacroRuntimeNode (path, state, importName, t) { const runtimeImportPath = getRuntimeImportPath(path, t) if (state.emotionImports === undefined) state.emotionImports = {} - if (state.emotionImports[runtimeImportPath] === undefined) { state.emotionImports[runtimeImportPath] = {} } + if (state.emotionImports[runtimeImportPath] === undefined) { + state.emotionImports[runtimeImportPath] = {} + } if (state.emotionImports[runtimeImportPath][importName] === undefined) { state.emotionImports[runtimeImportPath][ importName diff --git a/src/babel.js b/src/babel.js index e47942e87..34139ea48 100644 --- a/src/babel.js +++ b/src/babel.js @@ -4,8 +4,9 @@ import { basename } from 'path' import { touchSync } from 'touch' import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' -import forEach from '@arr/foreach' -import { inline, keyframes, fontFace, injectGlobal } from './inline' +import { forEach, map } from './utils' +import { inline } from './inline' +import { hashArray } from './hash' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' @@ -20,69 +21,93 @@ function joinExpressionsWithSpaces (expressions, t) { return t.templateLiteral(quasis, expressions) } -export function replaceCssWithCallExpression (path, identifier, state, t) { +export function replaceCssWithCallExpression ( + path, + identifier, + state, + t, + staticCSSTextCreator = (name, hash, src) => `.${name}-${hash} { ${src} }`, + removePath = false +) { try { - const { styles, isStaticBlock, composesCount } = inline( + const { name, hash, src, parser } = inline( path.node.quasi, - getIdentifierName(path, t) + getIdentifierName(path, t), + 'css' ) + ;('foo') + if (state.extractStatic && !path.node.quasi.expressions.length) { + const cssText = staticCSSTextCreator(name, hash, src) + const { styles, staticCSSRules } = parser(cssText, true) + + state.insertStaticRules(staticCSSRules) + return removePath + ? path.remove() + : path.replaceWith(t.stringLiteral(`${name}-${hash}`)) + } - const inputClasses = [] + const { styles, composesCount } = parser(src, false) - for (var i = 0; i < composesCount; i++) { - inputClasses.push(path.node.quasi.expressions[i]) + const inputClasses = [] + const composeValues = [] + for (let i = 0; i < composesCount; i++) { + composeValues.push(path.node.quasi.expressions[i]) } inputClasses.push(createAstObj(styles, false, composesCount, t)) - const thing = createAstObj( - styles, - path.node.quasi.expressions, - composesCount, - t - ) - - // console.log(thing) - if (state.extractStatic && isStaticBlock) { - // state.insertStaticRules(rules) - // if (!hasVar) { - // return path.replaceWith(t.stringLiteral(`${name}-${hash}`)) - // } - } - return path.replaceWith( + const objs = path.node.quasi.expressions.slice(0, composesCount) + const vars = path.node.quasi.expressions.slice(composesCount) + path.replaceWith( t.callExpression(identifier, [ - t.arrayExpression(path.node.quasi.expressions.slice(0, composesCount)), - t.arrayExpression(path.node.quasi.expressions.slice(composesCount)), + t.arrayExpression(composeValues), + t.arrayExpression(vars), t.functionExpression( t.identifier('createEmotionStyledRules'), - path.node.quasi.expressions - .slice(composesCount) - .map((x, i) => t.identifier(`x${i}`)), + vars.map((x, i) => t.identifier(`x${i}`)), t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) ) ]) ) } catch (e) { - console.log('throwing here', e) - // let {line, column} = path.loc.start; - // throw prettyError(createErrorWithLoc('Error at this position', line, column)); - throw e + if (path) { + throw path.buildCodeFrameError(e) + } + throw e } } export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - const { styles, isStaticBlock, composesCount } = inline( + + if (state.extractStatic && !path.node.quasi.expressions.length) { + const { name, hash, src, parser } = inline( + path.node.quasi, + identifierName, + 'styled' // we don't want these styles to be merged in css`` + ) + + const cssText = `.${name}-${hash} { ${src} }` + const { styles, staticCSSRules } = parser(cssText, true) + + state.insertStaticRules(staticCSSRules) + return t.callExpression(identifier, [ + tag, + t.arrayExpression([t.stringLiteral(`${name}-${hash}`)]) + ]) + } + + const { name, hash, src, parser } = inline( path.node.quasi, - identifierName + getIdentifierName(path, t), + 'css' ) - // console.log(JSON.stringify(styles, null, 2)) - + const { styles, composesCount } = parser(src, false) const inputClasses = [] const composeValues = [] - for (var i = 0; i < composesCount; i++) { + for (let i = 0; i < composesCount; i++) { composeValues.push(path.node.quasi.expressions[i]) } @@ -90,7 +115,7 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { const args = [ tag, - t.arrayExpression(path.node.quasi.expressions.slice(0, composesCount)), + t.arrayExpression(composeValues), t.arrayExpression(path.node.quasi.expressions.slice(composesCount)), t.functionExpression( t.identifier('createEmotionStyledRules'), @@ -101,10 +126,6 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { ) ] - if (state.extractStatic && isStaticBlock) { - // state.insertStaticRules(rules) - } - return t.callExpression(identifier, args) } @@ -260,12 +281,13 @@ function objKeyToAst ( expressions, composesCount: number, t -): { computed: boolean, ast: any } { +): { computed: boolean, ast: any, composes: boolean } { const matches = getDynamicMatches(key) if (matches.length) { return { computed: true, + composes: key === 'composes', ast: replacePlaceholdersWithExpressions( matches, key, @@ -306,7 +328,6 @@ function objValueToAst (value, expressions, composesCount, t) { } function createAstObj (obj, expressions, composesCount, t) { - // console.log(JSON.stringify(obj, null, 2)) const props = [] for (let key in obj) { @@ -327,7 +348,6 @@ function createAstObj (obj, expressions, composesCount, t) { props.push(t.objectProperty(keyAST, valueAST, computed)) } - // console.log(props) return t.objectExpression(props) } @@ -346,7 +366,7 @@ export default function (babel) { path.hub.file.opts.filename === 'unknown' || state.opts.inline state.extractStatic = - path.hub.file.opts.filename !== 'unknown' || + // path.hub.file.opts.filename !== 'unknown' || state.opts.extractStatic state.staticRules = [] @@ -407,16 +427,6 @@ export default function (babel) { path[visited] = true }, TaggedTemplateExpression (path, state) { - // in: - // styled.h1`color:${color};` - // - // out: - // styled('h1', "css-r1aqtk", [colorVar, heightVar], function inlineCss(x0, x1) { - // return [`.css-r1aqtk { - // margin: 12px; - // color: ${x0}; - // height: ${x1}; }`]; - // }); if ( // styled.h1`color:${color};` t.isMemberExpression(path.node.tag) && @@ -453,21 +463,26 @@ export default function (babel) { path, t.identifier('keyframes'), state, - t + t, + (name, hash, src) => `@animation ${name}-${hash} { ${src} }` ) } else if (path.node.tag.name === 'fontFace') { replaceCssWithCallExpression( path, t.identifier('fontFace'), state, - t + t, + (name, hash, src) => `@font-face {${src}}`, + true ) } else if (path.node.tag.name === 'injectGlobal') { replaceCssWithCallExpression( path, t.identifier('injectGlobal'), state, - t + t, + (name, hash, src) => src, + true ) } } diff --git a/src/glamor/clean.js b/src/glamor/clean.js index 608f962fb..139621eb3 100644 --- a/src/glamor/clean.js +++ b/src/glamor/clean.js @@ -47,7 +47,7 @@ function cleanArray (rules) { // If there is no styles left after filtration returns null export default function clean (input) { if (typeof input === 'string') { - return input.trim() + return input.trim() } return Array.isArray(input) ? cleanArray(input) : cleanObject(input) diff --git a/src/index.js b/src/index.js index b1c92ea02..48b1ed2f8 100644 --- a/src/index.js +++ b/src/index.js @@ -116,7 +116,7 @@ export function injectGlobal ( const combined = content ? objs.concat(content.apply(null, vars)) : objs // injectGlobal is flattened by postcss - // if using objects we don't support nested + // we don't support nested selectors on objects forEach(combined, obj => { forEach(Object.keys(obj), selector => { insertRawRule(`${selector} {${createMarkupForStyles(obj[selector])}}`) @@ -397,7 +397,6 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { } _src = clean(_src) if (_src && _src.composes) { - console.log('found composes') build(dest, { selector, mq, supp, src: _src.composes }) } Object.keys(_src || {}).forEach(key => { @@ -445,7 +444,6 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { }) } else if (key === 'composes') { // ignore, we already dealth with it - console.log('key === composes') } else { let _dest = dest if (supp) { diff --git a/src/inline.js b/src/inline.js index 1db25ba2c..910d2ea21 100644 --- a/src/inline.js +++ b/src/inline.js @@ -44,56 +44,37 @@ function createRawStringFromQuasi ( } export function inline ( - quasi: any, - identifierName?: string -): { - isStaticBlock: boolean, - styles: { [string]: any }, - composesCount: number -} { - let strs = quasi.quasis.map(x => x.value.cooked) - let { src } = createRawStringFromQuasi(strs) - return parseCSS(src) -} - -export function keyframes ( - quasi: any, - identifierName?: string, - prefix: string -): { - isStaticBlock: boolean, - styles: { [string]: any }, - composesCount: number -} { - let strs = quasi.quasis.map(x => x.value.cooked) - let { src } = createRawStringFromQuasi(strs) - return parseCSS(`{ ${src} }`) -} - -export function fontFace ( quasi: any, identifierName?: string, prefix: string ): { - isStaticBlock: boolean, - styles: { [string]: any }, - composesCount: number + name: string, + hash: string, + src: string, + parser: ( + css: string, + extractStatic: boolean + ) => { + staticCSSRules: Array<string>, + styles: { [string]: any }, + composesCount: number + } } { let strs = quasi.quasis.map(x => x.value.cooked) + let hash = hashArray([...strs]) // todo - add current filename? + let name = getName( + extractNameFromProperty(strs.join('xxx')), + identifierName, + prefix + ) let { src } = createRawStringFromQuasi(strs) - return parseCSS(`@font-face {${src}}`) -} -export function injectGlobal ( - quasi: any, - identifierName?: string, - prefix: string -): { - isStaticBlock: boolean, - styles: { [string]: any }, - composesCount: number -} { - let strs = quasi.quasis.map(x => x.value.cooked) - let { src } = createRawStringFromQuasi(strs) - return parseCSS(src) + return { + name, + hash, + src, + parser (css, extractStatic = false) { + return parseCSS(css, extractStatic) + } + } } diff --git a/src/macro.js b/src/macro.js index e93af455a..6b286fce9 100644 --- a/src/macro.js +++ b/src/macro.js @@ -1,14 +1,12 @@ -import { - replaceCssWithCallExpression -} from './babel' +import { replaceCssWithCallExpression } from './babel' import { buildMacroRuntimeNode, addRuntimeImports } from './babel-utils' -import { injectGlobal, fontFace } from './inline' +import { fontFace } from './inline' import { forEach } from './utils' import { keys } from './utils' module.exports = function macro ({ references, state, babel: { types: t } }) { if (!state.inline) state.inline = true - forEach(keys(references), (referenceKey) => { + forEach(keys(references), referenceKey => { if (referenceKey === 'injectGlobal') { references.injectGlobal.forEach(injectGlobalReference => { const path = injectGlobalReference.parentPath @@ -73,7 +71,7 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { } }) } else { - references[referenceKey].forEach((reference) => { + references[referenceKey].forEach(reference => { reference.replaceWith( buildMacroRuntimeNode(reference, state, referenceKey, t) ) diff --git a/src/parser.js b/src/parser.js index 95ad549a6..f4da122d0 100644 --- a/src/parser.js +++ b/src/parser.js @@ -8,7 +8,7 @@ import autoprefix from 'styled-components/lib/utils/autoprefix' import postcssJs from 'postcss-js' import { objStyle } from './index' -let prefixer = postcssJs.sync([autoprefix ]); +let prefixer = postcssJs.sync([autoprefix]) type Rule = { parent: { selector: string, nodes: Array<mixed> }, @@ -24,9 +24,10 @@ type Decl = { } export function parseCSS ( - css: string + css: string, + extractStatic: boolean ): { - isStaticBlock: boolean, + staticCSSRules: Array<string>, styles: { [string]: any }, composesCount: number } { @@ -64,8 +65,19 @@ export function parseCSS ( return { styles, - isStaticBlock: vars === 0, + staticCSSRules: vars === 0 && extractStatic + ? stringifyCSSRoot(postcssJs.parse(styles)) + : [], composesCount: composes } } +function stringifyCSSRoot (root) { + return root.nodes.map((node, i) => { + let str = '' + stringify(node, x => { + str += x + }) + return str + }) +} diff --git a/src/react/index.js b/src/react/index.js index a2c9629a6..b39d4912f 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -56,13 +56,11 @@ export default function (tag, objs, vars = [], content) { const getValue = v => { if (v && typeof v === 'function') { if (v.__emotion_spec) { - const css2 = css( + return css( map(v.__emotion_spec.objs, getValue), map(v.__emotion_spec.vars, getValue), v.__emotion_spec.content ) - console.log('computed style based on tag spec', css2) - return css2 } return v(mergedProps, context) } diff --git a/src/utils.js b/src/utils.js index 89751be3a..00febe4ac 100644 --- a/src/utils.js +++ b/src/utils.js @@ -13,7 +13,7 @@ export function omit (obj: { [string]: any }, keys: Array<string>) { export function keys (obj: { [string]: any }) { let k: string let out: Array<string> = [] -for (k in obj) { + for (k in obj) { out.push(k) } return out diff --git a/test/__snapshots__/parser.test.js.snap b/test/__snapshots__/parser.test.js.snap index 0c3743916..3af89aa6c 100644 --- a/test/__snapshots__/parser.test.js.snap +++ b/test/__snapshots__/parser.test.js.snap @@ -3,7 +3,7 @@ exports[`parser basic 1`] = ` Object { "composesCount": 0, - "isStaticBlock": true, + "staticCSSRules": Array [], "styles": Object { ".thing": Object { "WebkitBoxPack": "center", @@ -26,7 +26,7 @@ Object { exports[`parser fancy 1`] = ` Object { "composesCount": 0, - "isStaticBlock": true, + "staticCSSRules": Array [], "styles": Object { ".some-selector": Object { "WebkitBoxPack": "center", @@ -69,7 +69,13 @@ Object { exports[`parser static 1`] = ` Object { "composesCount": 0, - "isStaticBlock": true, + "staticCSSRules": Array [ + ".thing { + display: block; + height: 50px; + width: 30px +}", + ], "styles": Object { ".thing": Object { "display": "block", diff --git a/test/babel/__snapshots__/css-prop.test.js.snap b/test/babel/__snapshots__/css-prop.test.js.snap index a4b9d054e..dce9524fd 100644 --- a/test/babel/__snapshots__/css-prop.test.js.snap +++ b/test/babel/__snapshots__/css-prop.test.js.snap @@ -9,14 +9,15 @@ exports[`babel css prop StringLiteral css prop value 1`] = ` `; exports[`babel css prop basic 1`] = ` -"<div className={\\"a\\" + \\" \\" + css([], [], function createEmotionStyledRules() { - return [{ - \\"color\\": \\"brown\\" - }]; -})}></div>;" +"import \\"./css-prop.test.emotion.css\\"; +<div className={\\"a\\" + \\" \\" + \\"css-jf1v9l\\"}></div>;" `; -exports[`babel css prop basic 2`] = `".css-jf1v9l { color: brown; }"`; +exports[`babel css prop basic 2`] = ` +".css-jf1v9l { + color: brown +}" +`; exports[`babel css prop basic inline 1`] = ` "<div className={\\"a\\" + \\" \\" + css([], [], function createEmotionStyledRules() { diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index 66794c8e5..ca3d0380e 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -9,88 +9,105 @@ exports[`babel css extract basic object support 1`] = ` exports[`babel css extract composes 1`] = ` "import './css.test.emotion.css'; -const cls1 = \`\${'css-cls1-1q8jsgx'}\`; -const cls2 = css(['css-cls2-1fsihoz', ['one-class', 'another-class', cls1]], ['center']);" +const cls1 = 'css-cls1-1q8jsgx'; +const cls2 = css([['one-class', 'another-class', cls1]], ['center'], function createEmotionStyledRules(x0) { + return [{ + 'WebkitJustifyContent': 'center', + 'msFlexPack': 'center', + 'WebkitBoxPack': 'center', + 'justifyContent': 'center', + 'WebkitAlignItems': x0, + 'msFlexAlign': x0, + 'WebkitBoxAlign': x0, + 'alignItems': x0 + }]; +});" `; exports[`babel css extract composes 2`] = ` -".css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; } -.css-cls2-1fsihoz { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: var(--css-cls2-1fsihoz-0); - -ms-flex-align: var(--css-cls2-1fsihoz-0); - -webkit-box-align: var(--css-cls2-1fsihoz-0); - align-items: var(--css-cls2-1fsihoz-0) }" +".css-cls1-1q8jsgx { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex +}" `; exports[`babel css extract composes no dynamic 1`] = ` "import './css.test.emotion.css'; -const cls1 = \`\${'css-cls1-1q8jsgx'}\`; -const cls2 = \`\${'css-cls2-m25v9m'} \${'one-class'} \${'another-class'} \${cls1}\`;" +const cls1 = 'css-cls1-1q8jsgx'; +const cls2 = css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { + return [{ + 'WebkitJustifyContent': 'center', + 'msFlexPack': 'center', + 'WebkitBoxPack': 'center', + 'justifyContent': 'center', + 'WebkitAlignItems': 'center', + 'msFlexAlign': 'center', + 'WebkitBoxAlign': 'center', + 'alignItems': 'center' + }]; +});" `; exports[`babel css extract composes no dynamic 2`] = ` -".css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; } -.css-cls2-m25v9m { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; - -webkit-box-align: center; - align-items: center }" +".css-cls1-1q8jsgx { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex +}" `; exports[`babel css extract composes with objects 1`] = ` -"import './css.test.emotion.css'; - +" const cls1 = css({ display: ['-webkit-box', '-ms-flexbox', 'flex'] }); -const cls2 = \`\${'css-cls2-k8jueb'} \${cls1}\`;" +const cls2 = css([cls1], [], function createEmotionStyledRules() { + return [{ + 'height': '20', + 'WebkitJustifyContent': 'center', + 'msFlexPack': 'center', + 'WebkitBoxPack': 'center', + 'justifyContent': 'center' + }]; +});" `; exports[`babel css extract composes with objects 2`] = ` -".css-cls2-k8jueb { - height: 20; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }" +".css-cls1-1q8jsgx { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex +}" `; exports[`babel css extract css basic 1`] = ` "import \\"./css.test.emotion.css\\"; -css([\\"css-cu78iu\\"], [widthVar]);" +\\"css-153l48f\\";" `; exports[`babel css extract css basic 2`] = ` -".css-cu78iu { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: var(--css-cu78iu-0); }" -`; - -exports[`babel css extract interpolation in selector 1`] = ` -" -const cls2 = css([\\"css-cls2-16o34vq\\"], [className], function createEmotionRules(x0) { - return [\`.css-cls2-16o34vq { margin: 12px 48px; - color: #ffffff; }\`, \`.css-cls2-16o34vq .\${x0} { display: none; }\`]; -});" +".css-153l48f { + margin: 12px 48px; + color: #ffffff; + color: blue; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto +}" `; exports[`babel css extract prefixed array of objects 1`] = ` @@ -128,19 +145,22 @@ css({ exports[`babel css inline composes 1`] = ` " -const cls1 = css(['css-cls1-1q8jsgx'], [], function createEmotionRules() { - return [\`.css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const cls1 = css([], [], function createEmotionStyledRules() { + return [{ + 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + }]; }); -const cls2 = css(['css-cls2-v9djpl', 'one-class', 'another-class', cls1], ['center'], function createEmotionRules(x0) { - return [\`.css-cls2-v9djpl { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: \${x0}; - -ms-flex-align: \${x0}; - -webkit-box-align: \${x0}; - align-items: \${x0} }\`]; +const cls2 = css(['one-class', 'another-class', cls1], ['center'], function createEmotionStyledRules(x0) { + return [{ + 'WebkitJustifyContent': 'center', + 'msFlexPack': 'center', + 'WebkitBoxPack': 'center', + 'justifyContent': 'center', + 'WebkitAlignItems': x0, + 'msFlexAlign': x0, + 'WebkitBoxAlign': x0, + 'alignItems': x0 + }]; });" `; @@ -161,44 +181,58 @@ css([], [widthVar], function createEmotionStyledRules(x0) { exports[`babel css inline interpolation in selector 1`] = ` " -const cls2 = css([\\"css-cls2-16o34vq\\"], [className], function createEmotionRules(x0) { - return [\`.css-cls2-16o34vq { margin: 12px 48px; - color: #ffffff; }\`, \`.css-cls2-16o34vq .\${x0} { display: none; }\`]; +const cls2 = css([], [className], function createEmotionStyledRules(x0) { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"color\\": \\"#ffffff\\", + [\`.\${x0}\`]: { + \\"display\\": \\"none\\" + } + }]; });" `; exports[`babel css inline lots of composes 1`] = ` " -const cls2 = css(['css-cls2-hal2lb', 'one-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class'], ['center'], function createEmotionRules(x0) { - return [\`.css-cls2-hal2lb { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: \${x0}; - -ms-flex-align: \${x0}; - -webkit-box-align: \${x0}; - align-items: \${x0} }\`]; +const cls2 = css(['one-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class'], ['center'], function createEmotionStyledRules(x0) { + return [{ + 'WebkitJustifyContent': 'center', + 'msFlexPack': 'center', + 'WebkitBoxPack': 'center', + 'justifyContent': 'center', + 'WebkitAlignItems': x0, + 'msFlexAlign': x0, + 'WebkitBoxAlign': x0, + 'alignItems': x0 + }]; });" `; exports[`babel css inline only composes 1`] = ` " -const cls1 = css(['css-cls1-1q8jsgx'], [], function createEmotionRules() { - return [\`.css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const cls1 = css([], [], function createEmotionStyledRules() { + return [{ + 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + }]; }); -const cls2 = css(['css-cls2-64ycl', 'one-class', 'another-class', cls1], []);" +const cls2 = css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { + return [{}]; +});" `; exports[`babel css inline only styles on nested selector 1`] = ` " -const cls1 = css([\\"css-cls1-1q8jsgx\\"], [], function createEmotionRules() { - return [\`.css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const cls1 = css([], [], function createEmotionStyledRules() { + return [{ + \\"display\\": [\\"-webkit-box\\", \\"-moz-box\\", \\"-ms-flexbox\\", \\"-webkit-flex\\", \\"flex\\"] + }]; }); -const cls2 = css([\\"css-cls2-1nbi349\\"], [], function createEmotionRules() { - return [\`.css-cls2-1nbi349:hover { - background: pink; -}\`]; +const cls2 = css([], [], function createEmotionStyledRules() { + return [{ + \\"&:hover\\": { + \\"background\\": \\"pink\\" + } + }]; });" `; diff --git a/test/babel/__snapshots__/font-face.test.js.snap b/test/babel/__snapshots__/font-face.test.js.snap index 6887c1f6f..a41226771 100644 --- a/test/babel/__snapshots__/font-face.test.js.snap +++ b/test/babel/__snapshots__/font-face.test.js.snap @@ -1,41 +1,31 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`fontFace babel extract basic 1`] = ` -" -fontFace([], [], function createEmotionStyledRules() { - return [{ - \\"fontFamily\\": \\"MyHelvetica\\", - \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", - \\"fontWeight\\": \\"bold\\" - }]; -});" -`; +exports[`fontFace babel extract basic 1`] = `"import \\"./font-face.test.emotion.css\\";"`; exports[`fontFace babel extract basic 2`] = ` -"@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), +"@font-face { + font-family: MyHelvetica; + src: local(\\"Helvetica Neue Bold\\"), local(\\"HelveticaNeue-Bold\\"), url(MgOpenModernaBold.ttf); - font-weight: bold;}" + font-weight: bold +}" `; exports[`fontFace babel extract basic assign to variable 1`] = ` -" -const thisWillBeUndefined = fontFace([], [], function createEmotionStyledRules() { - return [{ - \\"fontFamily\\": \\"MyHelvetica\\", - \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", - \\"fontWeight\\": \\"bold\\" - }]; -});" +"import \\"./font-face.test.emotion.css\\"; + +const thisWillBeUndefined;" `; exports[`fontFace babel extract basic assign to variable 2`] = ` -"@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), +"@font-face { + font-family: MyHelvetica; + src: local(\\"Helvetica Neue Bold\\"), local(\\"HelveticaNeue-Bold\\"), url(MgOpenModernaBold.ttf); - font-weight: bold;}" + font-weight: bold +}" `; exports[`fontFace babel extract interpolation 1`] = ` diff --git a/test/babel/__snapshots__/fs.test.js.snap b/test/babel/__snapshots__/fs.test.js.snap index 76948692f..175e55161 100644 --- a/test/babel/__snapshots__/fs.test.js.snap +++ b/test/babel/__snapshots__/fs.test.js.snap @@ -1,49 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel plugin fs creates and writes to the css file when it does not exist 1`] = ` -".css-class-lk4ur6 { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: var(--css-class-lk4ur6-0); }" +".css-class-1yfv4zm { + margin: 12px 48px; + color: #ffffff; + color: blue; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + name: class +}" `; exports[`babel plugin fs creates and writes to the css file when it does not exist 2`] = ` "import \\"./fs.test.emotion.css\\"; -css([\\"css-class-lk4ur6\\"], [widthVar]);" +\\"css-class-1yfv4zm\\";" `; exports[`babel plugin fs does not write to the css file when it is the same as is already written 1`] = ` "import \\"./fs.test.emotion.css\\"; -css([\\"css-class-lk4ur6\\"], [widthVar]);" +\\"css-class-1yfv4zm\\";" `; exports[`babel plugin fs writes to the css file when it does exist 1`] = ` -".css-class-lk4ur6 { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: var(--css-class-lk4ur6-0); }" +".css-class-1yfv4zm { + margin: 12px 48px; + color: #ffffff; + color: blue; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + name: class +}" `; exports[`babel plugin fs writes to the css file when it does exist 2`] = ` "import \\"./fs.test.emotion.css\\"; -css([\\"css-class-lk4ur6\\"], [widthVar]);" +\\"css-class-1yfv4zm\\";" `; diff --git a/test/babel/__snapshots__/inject-global.test.js.snap b/test/babel/__snapshots__/inject-global.test.js.snap index aebe37fe9..6833725ec 100644 --- a/test/babel/__snapshots__/inject-global.test.js.snap +++ b/test/babel/__snapshots__/inject-global.test.js.snap @@ -1,65 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel injectGlobal extract injectGlobal assign to variable 1`] = ` -" -const thisWillBeUndefined = injectGlobal([], [], function createEmotionStyledRules() { - return [{ - \\"body\\": { - \\"margin\\": \\"0\\", - \\"padding\\": \\"0\\" - }, - \\"body > div\\": { - \\"display\\": \\"none\\" - }, - \\"html\\": { - \\"background\\": \\"green\\" - } - }]; -});" +"import \\"./inject-global.test.emotion.css\\"; + +const thisWillBeUndefined;" `; exports[`babel injectGlobal extract injectGlobal assign to variable 2`] = ` "body { - margin: 0; - padding: 0; - } + margin: 0; + padding: 0 +} body > div { - display: none; + display: none } html { - background: green; - }" + background: green +}" `; -exports[`babel injectGlobal extract injectGlobal basic 1`] = ` -" -injectGlobal([], [], function createEmotionStyledRules() { - return [{ - \\"body\\": { - \\"margin\\": \\"0\\", - \\"padding\\": \\"0\\" - }, - \\"body > div\\": { - \\"display\\": \\"none\\" - }, - \\"html\\": { - \\"background\\": \\"green\\" - } - }]; -});" -`; +exports[`babel injectGlobal extract injectGlobal basic 1`] = `"import \\"./inject-global.test.emotion.css\\";"`; exports[`babel injectGlobal extract injectGlobal basic 2`] = ` "body { - margin: 0; - padding: 0; - } + margin: 0; + padding: 0 +} body > div { - display: none; + display: none } html { - background: green; - }" + background: green +}" `; exports[`babel injectGlobal extract injectGlobal with interpolation 1`] = ` diff --git a/test/babel/__snapshots__/keyframes.test.js.snap b/test/babel/__snapshots__/keyframes.test.js.snap index 29699fbe6..a8888adbf 100644 --- a/test/babel/__snapshots__/keyframes.test.js.snap +++ b/test/babel/__snapshots__/keyframes.test.js.snap @@ -1,35 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel keyframes extract keyframes basic 1`] = ` -" -const rotate360 = keyframes([], [], function createEmotionStyledRules() { - return [{ - \\"from\\": { - \\"WebkitTransform\\": \\"rotate(0deg)\\", - \\"msTransform\\": \\"rotate(0deg)\\", - \\"transform\\": \\"rotate(0deg)\\" - }, - \\"to\\": { - \\"WebkitTransform\\": \\"rotate(360deg)\\", - \\"msTransform\\": \\"rotate(360deg)\\", - \\"transform\\": \\"rotate(360deg)\\" - } - }]; -});" +"import \\"./keyframes.test.emotion.css\\"; + +const rotate360 = \\"css-rotate360-f35ahc\\";" `; exports[`babel keyframes extract keyframes basic 2`] = ` -"@keyframes animation-rotate360-f35ahc { from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }" +"@animation css-rotate360-f35ahc { + from { + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg) + } + to { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg) + } +}" `; exports[`babel keyframes extract keyframes with interpolation 1`] = ` diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 7b491a813..3b768532c 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -1,49 +1,51 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`babel styled component extract based on props 1`] = ` -"import './styled.test.emotion.css'; -const H1 = styled('h1', ['css-H1-1kwokni'], [fontSize + 'px', props => props.translateX]);" -`; - -exports[`babel styled component extract based on props 2`] = ` -".css-H1-1kwokni { font-size: var(--css-H1-1kwokni-0); - height: 20px; - -webkit-transform: translateX(var(--css-H1-1kwokni-1)); - -ms-transform: translateX(var(--css-H1-1kwokni-1)); - transform: translateX(var(--css-H1-1kwokni-1)); }" -`; - exports[`babel styled component extract basic 1`] = ` -"import './styled.test.emotion.css'; -const H1 = styled('h1', ['css-H1-1mh2el5'], [fontSize + 'px']);" -`; - -exports[`babel styled component extract basic 2`] = `".css-H1-1mh2el5 { font-size: var(--css-H1-1mh2el5-0); height: 20px }"`; - -exports[`babel styled component extract composes 1`] = ` -"import './styled.test.emotion.css'; -const cls1 = \`\${'css-cls1-1ltut9y'}\`; -const H1 = styled('h1', ['css-H1-13ogl0z', cls1], [fontSize + 'px', props => props.translateX]);" -`; - -exports[`babel styled component extract composes 2`] = ` -".css-cls1-1ltut9y { width: 20px; } -.css-H1-13ogl0z { - font-size: var(--css-H1-13ogl0z-0); - height: 20px; - -webkit-transform: translateX(var(--css-H1-13ogl0z-1)); - -ms-transform: translateX(var(--css-H1-13ogl0z-1)); - transform: translateX(var(--css-H1-13ogl0z-1)); }" +"import \\"./styled.test.emotion.css\\"; +const H1 = styled(\\"h1\\", [\\"styled-H1-10x82eg\\"]);" +`; + +exports[`babel styled component extract basic 2`] = ` +".styled-H1-10x82eg { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-justify-content: center; + -ms-flex-pack: center; + -webkit-box-pack: center; + justify-content: center; + width: var(--css-hash-0) +} +.styled-H1-10x82eg:hover { + background-color: green +} +@media (max-width: 500px) { + .styled-H1-10x82eg { + height: var(--css-hash-1); + position: fixed + } +} +@media print { + .styled-H1-10x82eg { + display: none + } +} +.styled-H1-10x82eg::before { + color: blue; + width: 20px; + height: 20px; + content: 'pseudo' +}" `; -exports[`babel styled component extract no dynamic 1`] = ` +exports[`babel styled component extract no use 1`] = ` "import \\"./styled.test.emotion.css\\"; -styled(\\"h1\\", [\\"css-14ksm7b\\"], []);" +styled(\\"h1\\", [\\"styled-0\\"]);" `; -exports[`babel styled component extract no dynamic 2`] = `".css-14ksm7b { color: blue; }"`; - -exports[`babel styled component extract no use 1`] = `"\\"h1\\";"`; +exports[`babel styled component extract no use 2`] = `".styled-0 {}"`; exports[`babel styled component inline basic 1`] = ` "const H1 = styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { diff --git a/test/babel/css-prop.test.js b/test/babel/css-prop.test.js index d6144e299..802e47a97 100644 --- a/test/babel/css-prop.test.js +++ b/test/babel/css-prop.test.js @@ -11,7 +11,7 @@ describe('babel css prop', () => { test('basic', () => { const basic = '(<div className="a" css={`color: brown;`}></div>)' const { code } = babel.transform(basic, { - plugins: [plugin], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -23,7 +23,7 @@ describe('babel css prop', () => { test('basic inline', () => { const basic = '(<div className="a" css={`color: brown;`}></div>)' const { code } = babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) expect(code).toMatchSnapshot() }) @@ -31,7 +31,7 @@ describe('babel css prop', () => { test('dynamic inline', () => { const basic = `(<div className="a" css={\`color: $\{color};\`}></div>)` const { code } = babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) expect(code).toMatchSnapshot() }) @@ -47,7 +47,7 @@ describe('babel css prop', () => { test('with spread arg in jsx opening tag', () => { const basic = '(<div className="a" css={`color: brown;`} {...rest}></div>)' const { code } = babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) expect(code).toMatchSnapshot() }) @@ -64,7 +64,7 @@ describe('babel css prop', () => { const basic = '(<div css={5}></div>)' expect(() => babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) ).toThrow() }) diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 7791f4e7e..43a1095fc 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -8,7 +8,7 @@ fs.existsSync.mockReturnValue(true) describe('babel css', () => { describe('inline', () => { - test.only('css basic', () => { + test('css basic', () => { const basic = ` css\` margin: 12px 48px; @@ -29,7 +29,7 @@ describe('babel css', () => { const cls2 = css\` margin: 12px 48px; color: #ffffff; - .\${className} { + \${className} { display: none; } \` @@ -77,7 +77,7 @@ describe('babel css', () => { display: flex; \` const cls2 = css\` - composes: \${'one-class'} \${'another-class'}\${cls1} + composes: \${'one-class'} \${'another-class'}\${cls1}; \` ` const { code } = babel.transform(basic, { @@ -137,8 +137,10 @@ describe('babel css', () => { }) ).toThrowErrorMatchingSnapshot() }) - test('throws correct error when composes is on a nested selector', () => { - const basic = ` + test.skip( + 'throws correct error when composes is on a nested selector', + () => { + const basic = ` const cls1 = css\` display: flex; \` @@ -150,12 +152,13 @@ describe('babel css', () => { } \` ` - expect(() => - babel.transform(basic, { - plugins: [[plugin]] - }) - ).toThrowErrorMatchingSnapshot() - }) + expect(() => + babel.transform(basic, { + plugins: [[plugin]] + }) + ).toThrowErrorMatchingSnapshot() + } + ) }) describe('extract', () => { test('css basic', () => { @@ -166,10 +169,9 @@ describe('babel css', () => { display: flex; flex: 1 0 auto; color: blue; - width: \${widthVar}; \`` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -178,25 +180,6 @@ describe('babel css', () => { expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() }) - test('interpolation in selector', () => { - const basic = ` - const cls2 = css\` - margin: 12px 48px; - color: #ffffff; - .\${className} { - display: none; - } - \` - ` - const { code } = babel.transform(basic, { - plugins: [[plugin]], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(1) - }) - test('composes', () => { const basic = ` const cls1 = css\` @@ -209,7 +192,7 @@ describe('babel css', () => { \` ` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -229,7 +212,7 @@ describe('babel css', () => { \` ` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -293,14 +276,14 @@ describe('babel css', () => { \` ` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(4) - expect(fs.writeFileSync.mock.calls[3][1]).toMatchSnapshot() + expect(fs.writeFileSync).toHaveBeenCalledTimes(3) + expect(fs.writeFileSync.mock.calls[2][1]).toMatchSnapshot() }) }) }) diff --git a/test/babel/font-face.test.js b/test/babel/font-face.test.js index 79cd3d88d..c69abc08f 100644 --- a/test/babel/font-face.test.js +++ b/test/babel/font-face.test.js @@ -48,7 +48,7 @@ describe('fontFace babel', () => { font-weight: bold; \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -66,7 +66,7 @@ describe('fontFace babel', () => { font-weight: bold; \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -84,7 +84,7 @@ describe('fontFace babel', () => { font-weight: bold; \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], babelrc: false, filename: __filename }) diff --git a/test/babel/fs.test.js b/test/babel/fs.test.js index 6e22fcff3..47c1c3d40 100644 --- a/test/babel/fs.test.js +++ b/test/babel/fs.test.js @@ -14,7 +14,6 @@ css\` flex: 1 0 auto; color: blue; name: class; - width: \${widthVar}; \`` let output @@ -28,7 +27,7 @@ describe('babel plugin fs', () => { test('creates and writes to the css file when it does not exist', () => { fs.existsSync.mockReturnValueOnce(false) const { code } = transform(basic, { - plugins: [emotionPlugin], + plugins: [[emotionPlugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -44,7 +43,7 @@ describe('babel plugin fs', () => { fs.existsSync.mockReturnValueOnce(true) fs.readFileSync.mockReturnValueOnce('') const { code } = transform(basic, { - plugins: [emotionPlugin], + plugins: [[emotionPlugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -59,7 +58,7 @@ describe('babel plugin fs', () => { fs.existsSync.mockReturnValueOnce(true) fs.readFileSync.mockReturnValueOnce(output) const { code } = transform(basic, { - plugins: [emotionPlugin], + plugins: [[emotionPlugin, { extractStatic: true }]], filename: __filename, babelrc: false }) diff --git a/test/babel/inject-global.test.js b/test/babel/inject-global.test.js index f9aa16769..d25cd33ee 100644 --- a/test/babel/inject-global.test.js +++ b/test/babel/inject-global.test.js @@ -64,10 +64,11 @@ describe('babel injectGlobal', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) + expect(code).toMatchSnapshot() expect(fs.writeFileSync).toHaveBeenCalledTimes(1) expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() @@ -87,7 +88,7 @@ describe('babel injectGlobal', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -111,7 +112,9 @@ describe('babel injectGlobal', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]] + plugins: [[plugin, { extractStatic: true }]], + filename: __filename, + babelrc: false }) expect(code).toMatchSnapshot() expect(fs.writeFileSync).toHaveBeenCalledTimes(2) diff --git a/test/babel/keyframes.test.js b/test/babel/keyframes.test.js index 3ed460cc5..381b9bdcb 100644 --- a/test/babel/keyframes.test.js +++ b/test/babel/keyframes.test.js @@ -52,7 +52,7 @@ describe('babel keyframes', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], babelrc: false, filename: __filename }) @@ -72,7 +72,7 @@ describe('babel keyframes', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], babelrc: false, filename: __filename }) diff --git a/test/babel/macro.test.js b/test/babel/macro.test.js index d8da9368d..72c15ca14 100644 --- a/test/babel/macro.test.js +++ b/test/babel/macro.test.js @@ -93,11 +93,13 @@ describe('babel macro', () => { display: flex; \` ` - expect(() => babel.transform(basic, { - plugins: ['babel-macros'], - filename: __filename, - babelrc: false - })).toThrowError(/the emotion macro must be imported with es modules/) + expect(() => + babel.transform(basic, { + plugins: ['babel-macros'], + filename: __filename, + babelrc: false + }) + ).toThrowError(/the emotion macro must be imported with es modules/) }) }) test('injectGlobal', () => { diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index 098fd0770..fdd07c555 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -8,7 +8,7 @@ jest.mock('fs') fs.existsSync.mockReturnValue(true) describe('babel styled component', () => { - describe.only('inline', () => { + describe('inline', () => { test('no use', () => { const basic = 'styled.h1``' const { code } = babel.transform(basic, { @@ -49,12 +49,13 @@ describe('babel styled component', () => { }) test('nested', () => { - const basic = "const H1 = styled.h1`" + + const basic = + 'const H1 = styled.h1`' + "font-size: ${fontSize + 'px'};" + - "& div { color: blue;" + - "& span { color: red } }" + - "`" - const {code} = babel.transform(basic, { + '& div { color: blue;' + + '& span { color: red } }' + + '`' + const { code } = babel.transform(basic, { plugins: [plugin] }) expect(code).toMatchSnapshot() @@ -214,30 +215,18 @@ describe('babel styled component', () => { height: 20px; transform: translateX(\${(props) => props.translateX}); \`` - const {code} = babel.transform(basic, { + const { code } = babel.transform(basic, { plugins: [plugin] }) expect(code).toMatchSnapshot() }) }) - describe('extract', () => { test('no use', () => { const basic = 'styled.h1``' const { code } = babel.transform(basic, { - plugins: [plugin], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(0) - }) - - test('no dynamic', () => { - const basic = 'styled.h1`color:blue;`' - const { code } = babel.transform(basic, { - plugins: [plugin], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -248,48 +237,16 @@ describe('babel styled component', () => { test('basic', () => { const basic = - "const H1 = styled.h1`font-size: ${fontSize + 'px'}; height: 20px`" + "const H1 = styled.h1`display: flex; justify-content: center; width: var(--css-hash-0); &:hover { background-color: green; } @media (max-width: 500px) { height: var(--css-hash-1); position: fixed; } @media print { display: none; } &::before { color: blue; width: 20px; height: 20px; content: 'pseudo' }`" const { code } = babel.transform(basic, { - plugins: [plugin], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) + expect(code).toMatchSnapshot() expect(fs.writeFileSync).toHaveBeenCalledTimes(2) expect(fs.writeFileSync.mock.calls[1][1]).toMatchSnapshot() }) - - test('based on props', () => { - const basic = `const H1 = styled.h1\` - font-size: \${fontSize + 'px'}; - height: 20px; - transform: translateX(\${(props) => props.translateX}); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(3) - expect(fs.writeFileSync.mock.calls[2][1]).toMatchSnapshot() - }) - test('composes', () => { - const basic = `const cls1 = css\` width: 20px; \` - const H1 = styled.h1\` - composes: \${cls1}; - font-size: \${fontSize + 'px'}; - height: 20px; - transform: translateX(\${(props) => props.translateX}); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(4) - expect(fs.writeFileSync.mock.calls[3][1]).toMatchSnapshot() - }) }) }) diff --git a/test/css-prop.test.js b/test/css-prop.test.js index f53071e9a..34b54399f 100644 --- a/test/css-prop.test.js +++ b/test/css-prop.test.js @@ -20,7 +20,7 @@ describe('css prop react', () => { test('string expression', () => { const tree = renderer .create( - <p css="color:red;background:blue;font-size:48px;">hello world</p> + <p css='color:red;background:blue;font-size:48px;'>hello world</p> ) .toJSON() @@ -51,7 +51,7 @@ describe('css prop react', () => { const tree = renderer .create( <div - className="css__legacy-stuff" + className='css__legacy-stuff' css={` composes: ${bold} ${flexCenter}; `} @@ -64,11 +64,11 @@ describe('css prop react', () => { > BOOM </h1> - <p className="test_class1" css={`color: blue;`}> + <p className='test_class1' css={`color: blue;`}> Hello </p> <p - className="test_class1 test___class45" + className='test_class1 test___class45' css={`display: inline-flex`} > World diff --git a/test/css.test.js b/test/css.test.js index d8e429661..75e937dce 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -23,7 +23,7 @@ describe('css', () => { border: ${'solid 1px red'}; ` - const tree = renderer.create(<div className={cls1}/>).toJSON() + const tree = renderer.create(<div className={cls1} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -32,7 +32,7 @@ describe('css', () => { composes: ${undefined}; justifyContent: center; ` - const tree = renderer.create(<div className={cls2}/>).toJSON() + const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -44,13 +44,13 @@ describe('css', () => { composes: ${cls1}; justifyContent: center; ` - const tree = renderer.create(<div className={cls2}/>).toJSON() + const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) test('handles objects', () => { const cls1 = css({ display: 'flex' }) - const tree = renderer.create(<div className={cls1}/>).toJSON() + const tree = renderer.create(<div className={cls1} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -59,7 +59,7 @@ describe('css', () => { display: ['flex', 'block'], width: 30, height: 'calc(40vw - 50px)', - ':hover': {color: 'blue'}, + ':hover': { color: 'blue' }, ':after': { content: '" "', color: 'red' @@ -73,7 +73,7 @@ describe('css', () => { justifyContent: center; ` - const tree = renderer.create(<div className={cls2}/>).toJSON() + const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) }) diff --git a/test/extract/.babelrc b/test/extract/.babelrc index 2efecbb12..c31fa42db 100644 --- a/test/extract/.babelrc +++ b/test/extract/.babelrc @@ -4,5 +4,5 @@ "env", "react" ], - "plugins": ["../babel-plugin-emotion-test"] + "plugins": [["../babel-plugin-emotion-test", {extractStatic: true}]] } diff --git a/test/extract/__snapshots__/extract.test.js.snap b/test/extract/__snapshots__/extract.test.js.snap index d04c8b5e8..1d5baa017 100644 --- a/test/extract/__snapshots__/extract.test.js.snap +++ b/test/extract/__snapshots__/extract.test.js.snap @@ -1,135 +1,55 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`styled basic render 1`] = ` -.vars-ch9j2 { - --css-H1-1t8i2zo-0: 20px; -} - +exports[`styled basic render nested 1`] = ` <h1 - className="css-H1-1t8i2zo vars-ch9j2" + className="styled-H1-1l4sfy8" > hello world </h1> `; -exports[`styled call expression 1`] = ` -.vars-57j4qc { - --css-H1-rs9k70-0: 20px; -} - +exports[`styled name 1`] = ` <h1 - className="css-H1-rs9k70 vars-57j4qc legacy__class" + className="styled-FancyH1-131pfth" > hello world </h1> `; -exports[`styled composes 1`] = ` -.vars-1m8nl4c { - --css-H1-1pnnqpk-0: 20px; -} - +exports[`styled no dynamic 1`] = ` <h1 - className="css-H1-1pnnqpk css-cls2-y5rq6p css-cls1-cy897j vars-1m8nl4c css-H2-idm3bz legacy__class" - scale={2} + className="styled-H1-ijh7uz" > hello world </h1> `; -exports[`styled composition 1`] = ` -.vars-57j4qc { - --css-H1-rs9k70-0: 20px; +exports[`styled writes the correct css 1`] = ` +".styled-H1-ijh7uz { + font-size: 12px } - -.vars-1u8m2k4 { - --css-H2-vxb7tq-0: 13.333333333333334px; +.styled-H1-1l4sfy8 { + font-size: 20px } - -<h1 - className="css-H1-rs9k70 vars-57j4qc css-H2-vxb7tq vars-1u8m2k4 legacy__class" -> - hello world -</h1> -`; - -exports[`styled function in expression 1`] = ` -.vars-57j4qc { - --css-H1-rs9k70-0: 20px; +.styled-H1-1l4sfy8 span { + color: blue } - -.vars-6khrnp { - --css-H2-vxb7tq-0: NaN; +.styled-H1-1l4sfy8 span:hover { + color: green } - -<h1 - className="css-H1-rs9k70 vars-57j4qc css-H2-vxb7tq vars-6khrnp legacy__class" - scale={2} -> - hello world -</h1> -`; - -exports[`styled higher order component 1`] = ` -.vars-12p149b { - --css-Content-rs9k70-0: 20px; +.styled-H1-1l4sfy8 span:hover:after { + content: \\"after\\" } - -<div - className="css-Content-rs9k70 vars-12p149b css-onyx-13ds1s2 css-squirtle-blue-bg-135ox65" -/> -`; - -exports[`styled name 1`] = ` -.vars-1vv3got { - --css-FancyH1-1o9p8a2-0: 20px; +.styled-FancyH1-131pfth { + name: FancyH1; + font-size: 38px } - -<h1 - className="css-FancyH1-1o9p8a2 vars-1vv3got" -> - hello world -</h1> -`; - -exports[`styled no dynamic 1`] = ` -<h1 - className="css-H1-ijh7uz" -> - hello world -</h1> -`; - -exports[`styled writes the correct css 1`] = ` -".css-H1-ijh7uz { font-size: 12px; } -.css-H1-1t8i2zo { font-size: var(--css-H1-1t8i2zo-0); } -.css-FancyH1-1o9p8a2 { - font-size: var(--css-FancyH1-1o9p8a2-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-cls1-cy897j { color: blue; } -.css-cls2-y5rq6p { - height: 64px; } -.css-H1-1pnnqpk { - font-size: var(--css-H1-1pnnqpk-0); } -.css-H2-idm3bz { font-size: 32px; } -.css-Content-rs9k70 { font-size: var(--css-Content-rs9k70-0); } -.css-squirtle-blue-bg-135ox65 { - background-color: #7FC8D6; } -.css-onyx-13ds1s2 { - background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - flex-direction: column; } html { - background: pink; - } -.css-14yvnlz { font-family: sans-serif; - color: yellow; - background-color: purple; }" + background: pink +} +.css-14yvnlz { + font-family: sans-serif; + color: yellow; + background-color: purple +}" `; diff --git a/test/extract/extract.test.emotion.css b/test/extract/extract.test.emotion.css index 9e26fe85b..00d1ee444 100644 --- a/test/extract/extract.test.emotion.css +++ b/test/extract/extract.test.emotion.css @@ -1,31 +1,27 @@ -.css-H1-ijh7uz { font-size: 12px; } -.css-H1-1t8i2zo { font-size: var(--css-H1-1t8i2zo-0); } -.css-FancyH1-1o9p8a2 { - font-size: var(--css-FancyH1-1o9p8a2-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-cls1-cy897j { color: blue; } -.css-cls2-y5rq6p { - height: 64px; } -.css-H1-1pnnqpk { - font-size: var(--css-H1-1pnnqpk-0); } -.css-H2-idm3bz { font-size: 32px; } -.css-Content-rs9k70 { font-size: var(--css-Content-rs9k70-0); } -.css-squirtle-blue-bg-135ox65 { - background-color: #7FC8D6; } -.css-onyx-13ds1s2 { - background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - flex-direction: column; } +.styled-H1-ijh7uz { + font-size: 12px +} +.styled-H1-1l4sfy8 { + font-size: 20px +} +.styled-H1-1l4sfy8 span { + color: blue +} +.styled-H1-1l4sfy8 span:hover { + color: green +} +.styled-H1-1l4sfy8 span:hover:after { + content: "after" +} +.styled-FancyH1-131pfth { + name: FancyH1; + font-size: 38px +} html { - background: pink; - } -.css-14yvnlz { font-family: sans-serif; - color: yellow; - background-color: purple; } \ No newline at end of file + background: pink +} +.css-14yvnlz { + font-family: sans-serif; + color: yellow; + background-color: purple +} \ No newline at end of file diff --git a/test/extract/extract.test.js b/test/extract/extract.test.js index 426127a59..5671385aa 100644 --- a/test/extract/extract.test.js +++ b/test/extract/extract.test.js @@ -10,7 +10,7 @@ import styled from '../../src/react' expect.addSnapshotSerializer(serializer) expect.extend(matcher) -describe.skip('styled', () => { +describe('styled', () => { test('no dynamic', () => { const H1 = styled.h1`font-size: 12px;` @@ -19,10 +19,21 @@ describe.skip('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) - test('basic render', () => { - const fontSize = '20px' - const H1 = styled.h1`font-size: ${fontSize};` - + test('basic render nested', () => { + const H1 = styled.h1` + font-size: 20px; + & span { + color: blue; + + &:hover { + color: green; + + &:after { + content: "after"; + } + } + } + ` const tree = renderer.create(<H1>hello world</H1>).toJSON() expect(tree).toMatchSnapshotWithEmotion() @@ -32,7 +43,7 @@ describe.skip('styled', () => { const fontSize = '20px' const H1 = styled.h1` name: FancyH1; - font-size: ${fontSize}; + font-size: 38px; ` const tree = renderer.create(<H1>hello world</H1>).toJSON() @@ -40,113 +51,6 @@ describe.skip('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) - test('call expression', () => { - const fontSize = '20px' - const H1 = styled('h1')` - font-size: ${fontSize}; - ` - - const tree = renderer - .create(<H1 className={'legacy__class'}>hello world</H1>) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('composition', () => { - const fontSize = 20 - const H1 = styled('h1')` - font-size: ${fontSize + 'px'}; - ` - - const H2 = styled(H1)`font-size: ${fontSize * 2 / 3 + 'px'}` - - const tree = renderer - .create(<H2 className={'legacy__class'}>hello world</H2>) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('function in expression', () => { - const fontSize = '20px' - const H1 = styled('h1')` - font-size: ${fontSize}; - ` - - const H2 = styled(H1)`font-size: ${({ scale }) => fontSize * scale}` - - const tree = renderer - .create( - <H2 scale={2} className={'legacy__class'}> - hello world - </H2> - ) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('composes', () => { - const fontSize = '20px' - - const cls1 = css` - color: blue; - ` - - const cls2 = css` - composes: ${cls1}; - height: 64px; - ` - - const H1 = styled('h1')` - composes: ${cls2}; - font-size: ${fontSize}; - ` - - const H2 = styled(H1)`font-size:32px;` - - const tree = renderer - .create( - <H2 scale={2} className={'legacy__class'}> - hello world - </H2> - ) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('higher order component', () => { - const fontSize = '20px' - const Content = styled('div')` - font-size: ${fontSize}; - ` - - const squirtleBlueBackground = css` - name: squirtle-blue-bg; - background-color: #7FC8D6; - ` - - const flexColumn = Component => { - const NewComponent = styled(Component)` - composes: ${squirtleBlueBackground}; - name: onyx; - background-color: '#343a40'; - flex-direction: column; - ` - - return NewComponent - } - - const ColumnContent = flexColumn(Content) - - // expect(ColumnContent.displayName).toMatchSnapshotWithEmotion() - - const tree = renderer.create(<ColumnContent />).toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) test('injectGlobal', () => { injectGlobal` html { diff --git a/test/macro/css.test.js b/test/macro/css.test.js index 257d89a61..ec2b6e370 100644 --- a/test/macro/css.test.js +++ b/test/macro/css.test.js @@ -24,7 +24,7 @@ describe('css macro', () => { border: ${'solid 1px red'}; ` - const tree = renderer.create(<div className={cls1}/>).toJSON() + const tree = renderer.create(<div className={cls1} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -33,7 +33,7 @@ describe('css macro', () => { composes: ${undefined}; justifyContent: center; ` - const tree = renderer.create(<div className={cls2}/>).toJSON() + const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -45,13 +45,13 @@ describe('css macro', () => { composes: ${cls1}; justifyContent: center; ` - const tree = renderer.create(<div className={cls2}/>).toJSON() + const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) test('handles objects', () => { - const cls1 = css({display: 'flex'}) - const tree = renderer.create(<div className={cls1}/>).toJSON() + const cls1 = css({ display: 'flex' }) + const tree = renderer.create(<div className={cls1} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -60,7 +60,7 @@ describe('css macro', () => { display: ['flex', 'block'], width: 30, height: 'calc(40vw - 50px)', - ':hover': {color: 'blue'}, + ':hover': { color: 'blue' }, ':after': { content: '" "', color: 'red' @@ -74,7 +74,7 @@ describe('css macro', () => { justifyContent: center; ` - const tree = renderer.create(<div className={cls2}/>).toJSON() + const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) }) diff --git a/test/parser.test.js b/test/parser.test.js index 246919314..53387ed50 100644 --- a/test/parser.test.js +++ b/test/parser.test.js @@ -44,6 +44,6 @@ describe('parser', () => { expect(parseCSS(fancyCSS)).toMatchSnapshot() }) test('static', () => { - expect(parseCSS(staticCSS)).toMatchSnapshot() + expect(parseCSS(staticCSS, true)).toMatchSnapshot() }) }) diff --git a/test/react.test.js b/test/react.test.js index fef6de6b5..7393a517a 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -115,22 +115,22 @@ describe('styled', () => { const fakeBlue = css([ { - color: "blue" + color: 'blue' } - ]); + ]) const red = css([ { - color: "red" + color: 'red' } - ]); + ]) const blue = css([ red, { - color: "blue" + color: 'blue' } - ]); + ]) const prettyStyles = css([ { @@ -140,16 +140,14 @@ describe('styled', () => { transform: 'scale(1.2)' } }, - {border: '3px solid currentColor'} + { border: '3px solid currentColor' } ]) const Avatar = styled('img')` composes: ${prettyStyles} ${imageStyles} ${blue} ` - const tree = renderer - .create(<Avatar/>) - .toJSON() + const tree = renderer.create(<Avatar />).toJSON() expect(tree).toMatchSnapshotWithEmotion() }) @@ -196,7 +194,7 @@ describe('styled', () => { }) test('composes', () => { - debugger; + debugger const fontSize = '20px' const cssA = css` diff --git a/test/server.test.js b/test/server.test.js index eb9b83677..44f7ea372 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -43,8 +43,6 @@ const Page = () => describe('extractCritical', () => { test('returns static css', () => { - expect( - extractCritical(renderToString(<Page />)) - ).toMatchSnapshot() + expect(extractCritical(renderToString(<Page />))).toMatchSnapshot() }) }) From 6882afa3ef428ebed68f3f9fee7c6e3f1053cc88 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Thu, 20 Jul 2017 21:27:21 +1000 Subject: [PATCH 28/57] Little fixes, removing unused vars and other small stuff --- src/babel.js | 13 ++++--------- src/macro.js | 4 +--- test/babel/__snapshots__/keyframes.test.js.snap | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/babel.js b/src/babel.js index 34139ea48..fbfa09ebb 100644 --- a/src/babel.js +++ b/src/babel.js @@ -35,10 +35,9 @@ export function replaceCssWithCallExpression ( getIdentifierName(path, t), 'css' ) - ;('foo') if (state.extractStatic && !path.node.quasi.expressions.length) { const cssText = staticCSSTextCreator(name, hash, src) - const { styles, staticCSSRules } = parser(cssText, true) + const { staticCSSRules } = parser(cssText, true) state.insertStaticRules(staticCSSRules) return removePath @@ -56,7 +55,6 @@ export function replaceCssWithCallExpression ( inputClasses.push(createAstObj(styles, false, composesCount, t)) - const objs = path.node.quasi.expressions.slice(0, composesCount) const vars = path.node.quasi.expressions.slice(composesCount) path.replaceWith( t.callExpression(identifier, [ @@ -89,7 +87,7 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { ) const cssText = `.${name}-${hash} { ${src} }` - const { styles, staticCSSRules } = parser(cssText, true) + const { staticCSSRules } = parser(cssText, true) state.insertStaticRules(staticCSSRules) return t.callExpression(identifier, [ @@ -98,7 +96,7 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { ]) } - const { name, hash, src, parser } = inline( + const { src, parser } = inline( path.node.quasi, getIdentifierName(path, t), 'css' @@ -362,9 +360,6 @@ export default function (babel) { visitor: { Program: { enter (path, state) { - state.inline = - path.hub.file.opts.filename === 'unknown' || state.opts.inline - state.extractStatic = // path.hub.file.opts.filename !== 'unknown' || state.opts.extractStatic @@ -464,7 +459,7 @@ export default function (babel) { t.identifier('keyframes'), state, t, - (name, hash, src) => `@animation ${name}-${hash} { ${src} }` + (name, hash, src) => `@keyframes ${name}-${hash} { ${src} }` ) } else if (path.node.tag.name === 'fontFace') { replaceCssWithCallExpression( diff --git a/src/macro.js b/src/macro.js index 6b286fce9..a25ad9154 100644 --- a/src/macro.js +++ b/src/macro.js @@ -1,8 +1,6 @@ import { replaceCssWithCallExpression } from './babel' import { buildMacroRuntimeNode, addRuntimeImports } from './babel-utils' -import { fontFace } from './inline' -import { forEach } from './utils' -import { keys } from './utils' +import { forEach, keys } from './utils' module.exports = function macro ({ references, state, babel: { types: t } }) { if (!state.inline) state.inline = true diff --git a/test/babel/__snapshots__/keyframes.test.js.snap b/test/babel/__snapshots__/keyframes.test.js.snap index a8888adbf..29da460ae 100644 --- a/test/babel/__snapshots__/keyframes.test.js.snap +++ b/test/babel/__snapshots__/keyframes.test.js.snap @@ -7,7 +7,7 @@ const rotate360 = \\"css-rotate360-f35ahc\\";" `; exports[`babel keyframes extract keyframes basic 2`] = ` -"@animation css-rotate360-f35ahc { +"@keyframes css-rotate360-f35ahc { from { -webkit-transform: rotate(0deg); -ms-transform: rotate(0deg); From 03de7b77b9c0ba3337a9def89f083b3408520b65 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Thu, 20 Jul 2017 21:55:20 +1000 Subject: [PATCH 29/57] Remove unused import and fn --- src/babel.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/babel.js b/src/babel.js index fbfa09ebb..c2e8140e5 100644 --- a/src/babel.js +++ b/src/babel.js @@ -6,21 +6,9 @@ import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' import { forEach, map } from './utils' import { inline } from './inline' -import { hashArray } from './hash' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' -function joinExpressionsWithSpaces (expressions, t) { - const quasis = [t.templateElement({ cooked: '', raw: '' }, true)] - expressions.forEach((x, i) => { - if (i === expressions.length - 1) { - return quasis.push(t.templateElement({ cooked: '', raw: '' }, true)) - } - quasis.push(t.templateElement({ cooked: ' ', raw: ' ' }, true)) - }) - return t.templateLiteral(quasis, expressions) -} - export function replaceCssWithCallExpression ( path, identifier, From e961314284f50e96fe3428ee6fbc0078ffd742ee Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Thu, 20 Jul 2017 21:17:29 -0600 Subject: [PATCH 30/57] Make the code more clear by moving parseCSS into babel.js --- src/babel.js | 14 ++++++++------ src/inline.js | 18 +++--------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/babel.js b/src/babel.js index c2e8140e5..4c5f2373c 100644 --- a/src/babel.js +++ b/src/babel.js @@ -6,6 +6,7 @@ import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' import { forEach, map } from './utils' import { inline } from './inline' +import { parseCSS } from './parser' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' @@ -18,7 +19,7 @@ export function replaceCssWithCallExpression ( removePath = false ) { try { - const { name, hash, src, parser } = inline( + const { name, hash, src } = inline( path.node.quasi, getIdentifierName(path, t), 'css' @@ -26,6 +27,7 @@ export function replaceCssWithCallExpression ( if (state.extractStatic && !path.node.quasi.expressions.length) { const cssText = staticCSSTextCreator(name, hash, src) const { staticCSSRules } = parser(cssText, true) + const { styles, staticCSSRules } = parseCSS(cssText, true) state.insertStaticRules(staticCSSRules) return removePath @@ -33,7 +35,7 @@ export function replaceCssWithCallExpression ( : path.replaceWith(t.stringLiteral(`${name}-${hash}`)) } - const { styles, composesCount } = parser(src, false) + const { styles, composesCount } = parseCSS(src, false) const inputClasses = [] const composeValues = [] @@ -68,14 +70,14 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) if (state.extractStatic && !path.node.quasi.expressions.length) { - const { name, hash, src, parser } = inline( + const { name, hash, src } = inline( path.node.quasi, identifierName, 'styled' // we don't want these styles to be merged in css`` ) const cssText = `.${name}-${hash} { ${src} }` - const { staticCSSRules } = parser(cssText, true) + const { staticCSSRules } = parseCSS(cssText, true) state.insertStaticRules(staticCSSRules) return t.callExpression(identifier, [ @@ -84,13 +86,13 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { ]) } - const { src, parser } = inline( + const { src } = inline( path.node.quasi, getIdentifierName(path, t), 'css' ) - const { styles, composesCount } = parser(src, false) + const { styles, composesCount } = parseCSS(src, false) const inputClasses = [] const composeValues = [] for (let i = 0; i < composesCount; i++) { diff --git a/src/inline.js b/src/inline.js index 910d2ea21..735213183 100644 --- a/src/inline.js +++ b/src/inline.js @@ -1,5 +1,4 @@ // @flow -import { parseCSS } from './parser' import { hashArray } from './hash' function extractNameFromProperty (str: string) { @@ -50,18 +49,10 @@ export function inline ( ): { name: string, hash: string, - src: string, - parser: ( - css: string, - extractStatic: boolean - ) => { - staticCSSRules: Array<string>, - styles: { [string]: any }, - composesCount: number - } + src: string } { let strs = quasi.quasis.map(x => x.value.cooked) - let hash = hashArray([...strs]) // todo - add current filename? + let hash = hashArray([...strs]) let name = getName( extractNameFromProperty(strs.join('xxx')), identifierName, @@ -72,9 +63,6 @@ export function inline ( return { name, hash, - src, - parser (css, extractStatic = false) { - return parseCSS(css, extractStatic) - } + src } } From 75ad839bd516517c701f3ad60495b93f2976e07a Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Thu, 20 Jul 2017 22:08:19 -0600 Subject: [PATCH 31/57] - Remove code that added `.` to component selectors - prefixer is not a time machine - prefixer expanded properties are compiled ahead of time --- package.json | 1 + src/babel.js | 9 +- src/index.js | 6 +- src/parser.js | 32 ++++--- test/__snapshots__/parser.test.js.snap | 18 +--- test/__snapshots__/react.test.js.snap | 23 +++-- test/babel/__snapshots__/css.test.js.snap | 94 ++++++++----------- test/babel/__snapshots__/fs.test.js.snap | 22 ++--- .../__snapshots__/inject-global.test.js.snap | 2 +- .../__snapshots__/keyframes.test.js.snap | 20 ++-- test/babel/__snapshots__/macro.test.js.snap | 26 +++-- test/babel/__snapshots__/styled.test.js.snap | 25 ++--- test/babel/css.test.js | 16 +++- test/babel/inject-global.test.js | 2 +- test/babel/styled.test.js | 2 +- test/macro/__snapshots__/react.test.js.snap | 13 ++- test/macro/react.test.js | 2 +- test/react.test.js | 2 +- 18 files changed, 144 insertions(+), 171 deletions(-) diff --git a/package.json b/package.json index 78642b03a..d7b8487ba 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "autoprefixer": "^7.1.2", "babel-errors": "^1.1.1", "babel-plugin-syntax-jsx": "^6.18.0", + "babel-types": "^6.25.0", "fbjs": "^0.8.12", "inline-style-prefixer": "^3.0.6", "postcss-js": "^1.0.0", diff --git a/src/babel.js b/src/babel.js index 4c5f2373c..3e219ea9d 100644 --- a/src/babel.js +++ b/src/babel.js @@ -6,7 +6,7 @@ import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' import { forEach, map } from './utils' import { inline } from './inline' -import { parseCSS } from './parser' +import { parseCSS, expandCSSFallbacks } from './parser' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' @@ -26,8 +26,7 @@ export function replaceCssWithCallExpression ( ) if (state.extractStatic && !path.node.quasi.expressions.length) { const cssText = staticCSSTextCreator(name, hash, src) - const { staticCSSRules } = parser(cssText, true) - const { styles, staticCSSRules } = parseCSS(cssText, true) + const { staticCSSRules } = parseCSS(cssText, true) state.insertStaticRules(staticCSSRules) return removePath @@ -166,9 +165,9 @@ function prefixAst (args, t) { : property.value.value const style = { [property.key.name]: propertyValue } - const prefixedStyle = prefixer(style) + const prefixedStyle = expandCSSFallbacks(prefixer(style)) - for (var k in prefixedStyle) { + for (let k in prefixedStyle) { const key = t.isStringLiteral(property.key) ? t.stringLiteral(k) : t.identifier(k) diff --git a/src/index.js b/src/index.js index 48b1ed2f8..ca9404ef1 100644 --- a/src/index.js +++ b/src/index.js @@ -177,7 +177,6 @@ export function hydrate (ids: string[]) { forEach(ids, id => (inserted[id] = true)) } -// 🍩 type EmotionRule = { [string]: any } type CSSRuleList = Array<EmotionRule> @@ -191,6 +190,7 @@ let cachedCss: (rules: CSSRuleList) => EmotionClassName = typeof WeakMap !== ? multiIndexCache(_css) : _css +// 🍩 // https://github.com/threepointone/glamor export function objStyle (...rules: CSSRuleList): EmotionClassName { rules = clean(rules) @@ -224,14 +224,14 @@ export function isLikeRule (rule: EmotionRule) { if (keys.length !== 1) { return false } - return !!/css\-obj\-([a-zA-Z0-9]+)/.exec(keys[0]) + return !!/css\-([a-zA-Z0-9]+)/.exec(keys[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\-obj\-([a-zA-Z0-9]+)/ + let regex = /css\-([a-zA-Z0-9]+)/ let match = regex.exec(keys[0]) if (!match) throw new Error('not a rule') return match[1] diff --git a/src/parser.js b/src/parser.js index f4da122d0..75395e16a 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,14 +1,13 @@ // @flow -import { t } from 'babel-types' import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' -import autoprefix from 'styled-components/lib/utils/autoprefix' -// import camelizeStyleName from 'fbjs/lib/camelizeStyleName' import postcssJs from 'postcss-js' +import autoprefixer from 'autoprefixer' +import { processStyleName } from './glamor/CSSPropertyOperations' import { objStyle } from './index' -let prefixer = postcssJs.sync([autoprefix]) +const prefixer = postcssJs.sync([autoprefixer]) type Rule = { parent: { selector: string, nodes: Array<mixed> }, @@ -38,13 +37,6 @@ export function parseCSS ( postcssNested(root) - root.walkRules((rule: Rule) => { - // TODO: do this everywhere except `,xxx9xxx` - if (/\bxxx\d+xxx/.exec(rule.selector)) { - rule.selector = `.${rule.selector}` - } - }) - root.walkDecls((decl: Decl): void => { if (decl.prop === 'composes') { if (!/xxx(\d+)xxx/gm.exec(decl.value)) { @@ -61,7 +53,7 @@ export function parseCSS ( } }) - const styles = prefixer(postcssJs.objectify(root)) + const styles = expandCSSFallbacks(prefixer(postcssJs.objectify(root))) return { styles, @@ -81,3 +73,19 @@ function stringifyCSSRoot (root) { return str }) } + +export function expandCSSFallbacks (style: { [string]: any }) { + let flattened = Object.keys(style).reduce((accum, key) => { + if (Array.isArray(style[key])) { + accum[key] = style[key].join(`; ${processStyleName(key)}: `) + } else if (Object.prototype.toString.call(style[key]) === '[object Object]') { + accum[key] = expandCSSFallbacks(style[key]) + } else { + accum[key] = style[key]; + } + return accum + }, {}) + // todo - + // flatten arrays which haven't been flattened yet + return flattened +} diff --git a/test/__snapshots__/parser.test.js.snap b/test/__snapshots__/parser.test.js.snap index 3af89aa6c..05d786b39 100644 --- a/test/__snapshots__/parser.test.js.snap +++ b/test/__snapshots__/parser.test.js.snap @@ -7,14 +7,7 @@ Object { "styles": Object { ".thing": Object { "WebkitBoxPack": "center", - "WebkitJustifyContent": "center", - "display": Array [ - "-webkit-box", - "-moz-box", - "-ms-flexbox", - "-webkit-flex", - "flex", - ], + "display": "-webkit-box; display: -ms-flexbox; display: flex", "justifyContent": "center", "msFlexPack": "center", "width": "var(--css-hash-0)", @@ -30,14 +23,7 @@ Object { "styles": Object { ".some-selector": Object { "WebkitBoxPack": "center", - "WebkitJustifyContent": "center", - "display": Array [ - "-webkit-box", - "-moz-box", - "-ms-flexbox", - "-webkit-flex", - "flex", - ], + "display": "-webkit-box; display: -ms-flexbox; display: flex", "justifyContent": "center", "msFlexPack": "center", "width": "var(--css-hash-0)", diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 9036f2227..ea71727c5 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -42,12 +42,12 @@ exports[`styled component as selector 1`] = ` font-size: 20px; } -.css-1eki6f4 { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +.css-1d77tm { + display: -webkit-box,-ms-flexbox,flex; } <div - className="css-1eki6f4" + className="css-1d77tm" > hello <h1 @@ -149,19 +149,18 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-15zlqsx { +.css-11fn3ay { font-size: 20px; name: onyx; background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; + -ms-flex-direction: column; flex-direction: column; } <div - className="css-15zlqsx" + className="css-11fn3ay" /> `; @@ -191,15 +190,15 @@ exports[`styled name 1`] = ` `; exports[`styled nested 1`] = ` -.css-1msxvyz { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +.css-bex773 { + display: -webkit-box,-ms-flexbox,flex; } -.css-1msxvyz div { +.css-bex773 div { color: green; } -.css-1msxvyz div span { +.css-bex773 div span { color: red; } @@ -208,7 +207,7 @@ exports[`styled nested 1`] = ` } <div - className="css-1msxvyz" + className="css-bex773" > hello <h1 diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index ca3d0380e..abf92698c 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -2,7 +2,7 @@ exports[`babel css extract basic object support 1`] = ` "css({ - display: ['-webkit-box', '-ms-flexbox', 'flex'] + display: '-webkit-box; display: -ms-flexbox; display: flex' });" `; @@ -12,13 +12,11 @@ exports[`babel css extract composes 1`] = ` const cls1 = 'css-cls1-1q8jsgx'; const cls2 = css([['one-class', 'another-class', cls1]], ['center'], function createEmotionStyledRules(x0) { return [{ - 'WebkitJustifyContent': 'center', - 'msFlexPack': 'center', 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', 'justifyContent': 'center', - 'WebkitAlignItems': x0, - 'msFlexAlign': x0, 'WebkitBoxAlign': x0, + 'msFlexAlign': x0, 'alignItems': x0 }]; });" @@ -26,11 +24,7 @@ const cls2 = css([['one-class', 'another-class', cls1]], ['center'], function cr exports[`babel css extract composes 2`] = ` ".css-cls1-1q8jsgx { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex + display: -webkit-box; display: -ms-flexbox; display: flex }" `; @@ -40,13 +34,11 @@ exports[`babel css extract composes no dynamic 1`] = ` const cls1 = 'css-cls1-1q8jsgx'; const cls2 = css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { return [{ - 'WebkitJustifyContent': 'center', - 'msFlexPack': 'center', 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', 'justifyContent': 'center', - 'WebkitAlignItems': 'center', - 'msFlexAlign': 'center', 'WebkitBoxAlign': 'center', + 'msFlexAlign': 'center', 'alignItems': 'center' }]; });" @@ -54,25 +46,20 @@ const cls2 = css(['one-class', 'another-class', cls1], [], function createEmotio exports[`babel css extract composes no dynamic 2`] = ` ".css-cls1-1q8jsgx { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex + display: -webkit-box; display: -ms-flexbox; display: flex }" `; exports[`babel css extract composes with objects 1`] = ` " const cls1 = css({ - display: ['-webkit-box', '-ms-flexbox', 'flex'] + display: '-webkit-box; display: -ms-flexbox; display: flex' }); const cls2 = css([cls1], [], function createEmotionStyledRules() { return [{ 'height': '20', - 'WebkitJustifyContent': 'center', - 'msFlexPack': 'center', 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', 'justifyContent': 'center' }]; });" @@ -80,11 +67,7 @@ const cls2 = css([cls1], [], function createEmotionStyledRules() { exports[`babel css extract composes with objects 2`] = ` ".css-cls1-1q8jsgx { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex + display: -webkit-box; display: -ms-flexbox; display: flex }" `; @@ -97,14 +80,9 @@ exports[`babel css extract css basic 1`] = ` exports[`babel css extract css basic 2`] = ` ".css-153l48f { margin: 12px 48px; - color: #ffffff; - color: blue; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; + color: #ffffff; color: blue; + display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-flex: 1; -ms-flex: 1 0 auto; flex: 1 0 auto }" @@ -116,14 +94,14 @@ css([{ borderRadius: '50%', WebkitBoxSizing: 'border-box', boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], + display: '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { WebkitTransform: 'scale(1.2)', transform: 'scale(1.2)' } }, { WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'] + transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out' }]);" `; @@ -132,10 +110,10 @@ exports[`babel css extract prefixed objects 1`] = ` css({ borderRadius: '50%', WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'], + transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', WebkitBoxSizing: 'border-box', boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], + display: '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { WebkitTransform: 'scale(1.2)', transform: 'scale(1.2)' @@ -147,18 +125,16 @@ exports[`babel css inline composes 1`] = ` " const cls1 = css([], [], function createEmotionStyledRules() { return [{ - 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; }); const cls2 = css(['one-class', 'another-class', cls1], ['center'], function createEmotionStyledRules(x0) { return [{ - 'WebkitJustifyContent': 'center', - 'msFlexPack': 'center', 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', 'justifyContent': 'center', - 'WebkitAlignItems': x0, - 'msFlexAlign': x0, 'WebkitBoxAlign': x0, + 'msFlexAlign': x0, 'alignItems': x0 }]; });" @@ -169,9 +145,9 @@ exports[`babel css inline css basic 1`] = ` css([], [widthVar], function createEmotionStyledRules(x0) { return [{ \\"margin\\": \\"12px 48px\\", - \\"color\\": [\\"#ffffff\\", \\"blue\\"], - \\"display\\": [\\"-webkit-box\\", \\"-moz-box\\", \\"-ms-flexbox\\", \\"-webkit-flex\\", \\"flex\\"], - \\"WebkitFlex\\": \\"1 0 auto\\", + \\"color\\": \\"#ffffff; color: blue\\", + \\"display\\": \\"-webkit-box; display: -ms-flexbox; display: flex\\", + \\"WebkitBoxFlex\\": \\"1\\", \\"msFlex\\": \\"1 0 auto\\", \\"flex\\": \\"1 0 auto\\", \\"width\\": x0 @@ -185,7 +161,7 @@ const cls2 = css([], [className], function createEmotionStyledRules(x0) { return [{ \\"margin\\": \\"12px 48px\\", \\"color\\": \\"#ffffff\\", - [\`.\${x0}\`]: { + [x0]: { \\"display\\": \\"none\\" } }]; @@ -196,23 +172,33 @@ exports[`babel css inline lots of composes 1`] = ` " const cls2 = css(['one-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class'], ['center'], function createEmotionStyledRules(x0) { return [{ - 'WebkitJustifyContent': 'center', - 'msFlexPack': 'center', 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', 'justifyContent': 'center', - 'WebkitAlignItems': x0, - 'msFlexAlign': x0, 'WebkitBoxAlign': x0, + 'msFlexAlign': x0, 'alignItems': x0 }]; });" `; +exports[`babel css inline nested expanded properties 1`] = ` +" +css([], [], function createEmotionStyledRules() { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"& .div\\": { + \\"display\\": \\"'flex'\\" + } + }]; +});" +`; + exports[`babel css inline only composes 1`] = ` " const cls1 = css([], [], function createEmotionStyledRules() { return [{ - 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; }); const cls2 = css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { @@ -224,7 +210,7 @@ exports[`babel css inline only styles on nested selector 1`] = ` " const cls1 = css([], [], function createEmotionStyledRules() { return [{ - \\"display\\": [\\"-webkit-box\\", \\"-moz-box\\", \\"-ms-flexbox\\", \\"-webkit-flex\\", \\"flex\\"] + \\"display\\": \\"-webkit-box; display: -ms-flexbox; display: flex\\" }]; }); const cls2 = css([], [], function createEmotionStyledRules() { diff --git a/test/babel/__snapshots__/fs.test.js.snap b/test/babel/__snapshots__/fs.test.js.snap index 175e55161..e0839964b 100644 --- a/test/babel/__snapshots__/fs.test.js.snap +++ b/test/babel/__snapshots__/fs.test.js.snap @@ -3,14 +3,9 @@ exports[`babel plugin fs creates and writes to the css file when it does not exist 1`] = ` ".css-class-1yfv4zm { margin: 12px 48px; - color: #ffffff; - color: blue; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; + color: #ffffff; color: blue; + display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-flex: 1; -ms-flex: 1 0 auto; flex: 1 0 auto; name: class @@ -32,14 +27,9 @@ exports[`babel plugin fs does not write to the css file when it is the same as i exports[`babel plugin fs writes to the css file when it does exist 1`] = ` ".css-class-1yfv4zm { margin: 12px 48px; - color: #ffffff; - color: blue; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; + color: #ffffff; color: blue; + display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-flex: 1; -ms-flex: 1 0 auto; flex: 1 0 auto; name: class diff --git a/test/babel/__snapshots__/inject-global.test.js.snap b/test/babel/__snapshots__/inject-global.test.js.snap index 6833725ec..ecb65f763 100644 --- a/test/babel/__snapshots__/inject-global.test.js.snap +++ b/test/babel/__snapshots__/inject-global.test.js.snap @@ -62,7 +62,7 @@ injectGlobal([], [], function createEmotionStyledRules() { \\"padding\\": \\"0\\" }, \\"body > div\\": { - \\"display\\": \\"none\\" + \\"display\\": \\"-webkit-box; display: -ms-flexbox; display: flex\\" }, \\"html\\": { \\"background\\": \\"green\\" diff --git a/test/babel/__snapshots__/keyframes.test.js.snap b/test/babel/__snapshots__/keyframes.test.js.snap index 29da460ae..21da53a0e 100644 --- a/test/babel/__snapshots__/keyframes.test.js.snap +++ b/test/babel/__snapshots__/keyframes.test.js.snap @@ -7,15 +7,23 @@ const rotate360 = \\"css-rotate360-f35ahc\\";" `; exports[`babel keyframes extract keyframes basic 2`] = ` -"@keyframes css-rotate360-f35ahc { +"@-webkit-keyframes css-rotate360-f35ahc { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg) + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg) + } +} +@keyframes css-rotate360-f35ahc { from { -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); transform: rotate(0deg) } to { -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); transform: rotate(360deg) } }" @@ -27,12 +35,10 @@ const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRu return [{ \\"from\\": { \\"WebkitTransform\\": \\"rotate(0deg)\\", - \\"msTransform\\": \\"rotate(0deg)\\", \\"transform\\": \\"rotate(0deg)\\" }, \\"to\\": { \\"WebkitTransform\\": \`rotate(\${x0})\`, - \\"msTransform\\": \`rotate(\${x0})\`, \\"transform\\": \`rotate(\${x0})\` } }]; @@ -45,12 +51,10 @@ const rotate360 = keyframes([], [], function createEmotionStyledRules() { return [{ \\"from\\": { \\"WebkitTransform\\": \\"rotate(0deg)\\", - \\"msTransform\\": \\"rotate(0deg)\\", \\"transform\\": \\"rotate(0deg)\\" }, \\"to\\": { \\"WebkitTransform\\": \\"rotate(360deg)\\", - \\"msTransform\\": \\"rotate(360deg)\\", \\"transform\\": \\"rotate(360deg)\\" } }]; @@ -63,12 +67,10 @@ const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRu return [{ \\"from\\": { \\"WebkitTransform\\": \\"rotate(0deg)\\", - \\"msTransform\\": \\"rotate(0deg)\\", \\"transform\\": \\"rotate(0deg)\\" }, \\"to\\": { \\"WebkitTransform\\": \`rotate(\${x0})\`, - \\"msTransform\\": \`rotate(\${x0})\`, \\"transform\\": \`rotate(\${x0})\` } }]; diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index 70fc166c3..180694cb6 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -6,9 +6,9 @@ exports[`babel macro css 1`] = ` _css([], [widthVar], function createEmotionStyledRules(x0) { return [{ 'margin': '12px 48px', - 'color': ['#ffffff', 'blue'], - 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'], - 'WebkitFlex': '1 0 auto', + 'color': '#ffffff; color: blue', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', 'msFlex': '1 0 auto', 'flex': '1 0 auto', 'width': x0 @@ -81,12 +81,10 @@ const rotate360 = _keyframes([], [], function createEmotionStyledRules() { return [{ 'from': { 'WebkitTransform': 'rotate(0deg)', - 'msTransform': 'rotate(0deg)', 'transform': 'rotate(0deg)' }, 'to': { 'WebkitTransform': 'rotate(360deg)', - 'msTransform': 'rotate(360deg)', 'transform': 'rotate(360deg)' } }]; @@ -100,12 +98,10 @@ const rotate360 = _keyframes([], [], function createEmotionStyledRules() { return [{ 'from': { 'WebkitTransform': 'rotate(0deg)', - 'msTransform': 'rotate(0deg)', 'transform': 'rotate(0deg)' }, 'to': { 'WebkitTransform': 'rotate(360deg)', - 'msTransform': 'rotate(360deg)', 'transform': 'rotate(360deg)' } }]; @@ -113,9 +109,9 @@ const rotate360 = _keyframes([], [], function createEmotionStyledRules() { const thing = _css([], [widthVar], function createEmotionStyledRules(x0) { return [{ 'margin': '12px 48px', - 'color': ['#ffffff', 'blue'], - 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'], - 'WebkitFlex': '1 0 auto', + 'color': '#ffffff; color: blue', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', 'msFlex': '1 0 auto', 'flex': '1 0 auto', 'width': x0 @@ -134,7 +130,7 @@ exports[`babel macro styled css from react 1`] = ` const someCls = _css([], [], function createEmotionStyledRules() { return [{ - 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; });" `; @@ -143,7 +139,7 @@ exports[`babel macro styled object function 1`] = ` "import _styled from '../../src/react'; const SomeComponent = _styled('div', [{ - display: ['-webkit-box', '-ms-flexbox', 'flex'] + display: '-webkit-box; display: -ms-flexbox; display: flex' }]);" `; @@ -151,7 +147,7 @@ exports[`babel macro styled object member 1`] = ` "import _styled from '../../src/react'; const SomeComponent = _styled('div', [{ - display: ['-webkit-box', '-ms-flexbox', 'flex'] + display: '-webkit-box; display: -ms-flexbox; display: flex' }]);" `; @@ -166,7 +162,7 @@ exports[`babel macro styled tagged template literal function 1`] = ` const SomeComponent = _styled('div', [], [], function createEmotionStyledRules() { return [{ - 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; });" `; @@ -176,7 +172,7 @@ exports[`babel macro styled tagged template literal member 1`] = ` const SomeComponent = _styled('div', [], [], function createEmotionStyledRules() { return [{ - 'display': ['-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex', 'flex'] + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; });" `; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 3b768532c..0eef938d6 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -7,14 +7,9 @@ const H1 = styled(\\"h1\\", [\\"styled-H1-10x82eg\\"]);" exports[`babel styled component extract basic 2`] = ` ".styled-H1-10x82eg { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; + display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; width: var(--css-hash-0) } @@ -68,7 +63,6 @@ const H1 = styled('h1', [props => { 'fontSize': x0, 'height': '20px', 'WebkitTransform': \`translateX(\${x1})\`, - 'msTransform': \`translateX(\${x1})\`, 'transform': \`translateX(\${x1})\` }]; });" @@ -88,9 +82,8 @@ const H1 = styled('h1', [], [fontSize + 'px', props => props.translateX, somethi return [{ 'fontSize': x0, 'height': '20px', - 'WebkitTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], - 'msTransform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], - 'transform': [\`translateX(\${x1})\`, \`translateX(\${x4}) translateY(\${x5})\`], + 'WebkitTransform': \`translateX(\${x1}); -webkit-transform: translateX(\${x4}) translateY(\${x5})\`, + 'transform': \`translateX(\${x1}); transform: translateX(\${x4}) translateY(\${x5})\`, 'height1': \`\${x2}wow\`, 'width': \`w\${x3}ow\`, 'transform1': \`translateX(\${x6}) translateY(\${x7})\`, @@ -181,7 +174,7 @@ const H1 = styled('h1', [{ exports[`babel styled component inline objects fn call 1`] = ` " const H1 = styled('h1', [{ - display: ['-webkit-box', '-ms-flexbox', 'flex'] + display: '-webkit-box; display: -ms-flexbox; display: flex' }]);" `; @@ -190,10 +183,10 @@ exports[`babel styled component inline objects prefixed 1`] = ` const H1 = styled('h1', [{ borderRadius: '50%', WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'], + transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', WebkitBoxSizing: 'border-box', boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], + display: '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { WebkitTransform: 'scale(1.2)', transform: 'scale(1.2)' @@ -223,10 +216,10 @@ exports[`babel styled component inline styled objects prefixed 1`] = ` const H1 = styled('h1', [{ borderRadius: '50%', WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'], + transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', WebkitBoxSizing: 'border-box', boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], + display: '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { WebkitTransform: 'scale(1.2)', transform: 'scale(1.2)' diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 43a1095fc..704ca766a 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -24,6 +24,20 @@ describe('babel css', () => { expect(code).toMatchSnapshot() }) + test('nested expanded properties', () => { + const basic = ` + css\` + margin: 12px 48px; + & .div { + display: 'flex'; + } + \`` + const {code} = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) + test('interpolation in selector', () => { const basic = ` const cls2 = css\` @@ -148,7 +162,7 @@ describe('babel css', () => { justify-content: center; align-items: center; .some-class { - composes: \${cls1} + composes: \${cls1}; } \` ` diff --git a/test/babel/inject-global.test.js b/test/babel/inject-global.test.js index d25cd33ee..1cec8aaab 100644 --- a/test/babel/inject-global.test.js +++ b/test/babel/inject-global.test.js @@ -15,7 +15,7 @@ describe('babel injectGlobal', () => { margin: 0; padding: 0; & > div { - display: none; + display: flex; } } html { diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index fdd07c555..d454fc8f3 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -30,7 +30,7 @@ describe('babel styled component', () => { const SomeComponent = styled.div\` \` styled.h1\` color:blue; - \${SomeComponent} { + .\${SomeComponent} { color: green; } \`` diff --git a/test/macro/__snapshots__/react.test.js.snap b/test/macro/__snapshots__/react.test.js.snap index a1b074ba0..f26447a58 100644 --- a/test/macro/__snapshots__/react.test.js.snap +++ b/test/macro/__snapshots__/react.test.js.snap @@ -37,8 +37,8 @@ exports[`styled call expression 1`] = ` `; exports[`styled component as selector 1`] = ` -.css-1eki6f4 { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +.css-1d77tm { + display: -webkit-box,-ms-flexbox,flex; } .css-1ikz3g9 { @@ -46,7 +46,7 @@ exports[`styled component as selector 1`] = ` } <div - className="css-1eki6f4" + className="css-1d77tm" > hello <h1 @@ -150,18 +150,17 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-lid1wd { +.css-1nkevqp { font-size: 20px; background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; + -ms-flex-direction: column; flex-direction: column; } <div - className="css-lid1wd" + className="css-1nkevqp" /> `; diff --git a/test/macro/react.test.js b/test/macro/react.test.js index 97f61c21e..406e589d8 100644 --- a/test/macro/react.test.js +++ b/test/macro/react.test.js @@ -81,7 +81,7 @@ describe('styled', () => { const Thing = styled.div` display: flex; - ${H1} { + .${H1} { color: green; } ` diff --git a/test/react.test.js b/test/react.test.js index 7393a517a..76ec18c51 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -158,7 +158,7 @@ describe('styled', () => { const Thing = styled.div` display: flex; - ${H1} { + .${H1} { color: green; } ` From e860520764e3ae690525aae08afdf0ca37357585 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Thu, 20 Jul 2017 22:19:55 -0600 Subject: [PATCH 32/57] Fix example site demos --- example/src/blocks/composes.example | 2 +- example/src/blocks/css-prop.example | 2 +- example/src/blocks/intro.example | 2 +- example/src/blocks/keyframes.example | 2 +- example/src/blocks/media.example | 2 +- example/src/blocks/named.example | 2 +- example/src/blocks/nested.example | 2 +- example/src/blocks/objects.example | 2 +- example/src/blocks/props.example | 2 +- example/src/blocks/pseudo.example | 2 +- example/src/blocks/styled-with-object.example | 2 +- example/src/blocks/styling-any-component.example | 2 +- example/src/blocks/theming.example | 2 +- src/react/index.js | 15 +++++++++------ 14 files changed, 22 insertions(+), 19 deletions(-) diff --git a/example/src/blocks/composes.example b/example/src/blocks/composes.example index 421a99753..5c0cd2335 100644 --- a/example/src/blocks/composes.example +++ b/example/src/blocks/composes.example @@ -13,4 +13,4 @@ const Avatar = styled('img')` } ` -render(<Avatar src={avatarUrl} />, mountNode) +render(<Avatar src={logoUrl} />, mountNode) diff --git a/example/src/blocks/css-prop.example b/example/src/blocks/css-prop.example index c26787a56..023b0d858 100644 --- a/example/src/blocks/css-prop.example +++ b/example/src/blocks/css-prop.example @@ -24,7 +24,7 @@ render( } `} > - <img src={avatarUrl} /> + <img src={logoUrl} /> </div>, mountNode ) diff --git a/example/src/blocks/intro.example b/example/src/blocks/intro.example index 917ed86f9..326946f1c 100644 --- a/example/src/blocks/intro.example +++ b/example/src/blocks/intro.example @@ -4,4 +4,4 @@ const Avatar = styled('img')` border-radius: 50%; ` -render(<Avatar src={avatarUrl} />, mountNode) +render(<Avatar src={logoUrl} />, mountNode) diff --git a/example/src/blocks/keyframes.example b/example/src/blocks/keyframes.example index e49379e2e..4ce1be48a 100644 --- a/example/src/blocks/keyframes.example +++ b/example/src/blocks/keyframes.example @@ -25,6 +25,6 @@ const Avatar = styled('img')` ` render( - <Avatar src={avatarUrl} rounded />, + <Avatar src={logoUrl} rounded />, mountNode ) diff --git a/example/src/blocks/media.example b/example/src/blocks/media.example index a63653867..04b8d5a27 100644 --- a/example/src/blocks/media.example +++ b/example/src/blocks/media.example @@ -10,6 +10,6 @@ const Avatar = styled('img')` ` render( - <Avatar src={avatarUrl} rounded />, + <Avatar src={logoUrl} rounded />, mountNode ) diff --git a/example/src/blocks/named.example b/example/src/blocks/named.example index ecd6b2fe0..2b4704f67 100644 --- a/example/src/blocks/named.example +++ b/example/src/blocks/named.example @@ -6,6 +6,6 @@ const Avatar = styled('img')` ` render( - <Avatar src={avatarUrl} />, + <Avatar src={logoUrl} />, mountNode ) diff --git a/example/src/blocks/nested.example b/example/src/blocks/nested.example index b3c1e9c8a..b660f7b44 100644 --- a/example/src/blocks/nested.example +++ b/example/src/blocks/nested.example @@ -7,6 +7,6 @@ const Avatar = styled('div')` ` render( - <Avatar><img src={avatarUrl} /></Avatar>, + <Avatar><img src={logoUrl} /></Avatar>, mountNode ) diff --git a/example/src/blocks/objects.example b/example/src/blocks/objects.example index 0601b7322..0ab68d334 100644 --- a/example/src/blocks/objects.example +++ b/example/src/blocks/objects.example @@ -38,6 +38,6 @@ const Avatar = styled('img')` ` render( - <Avatar src={avatarUrl}/>, + <Avatar src={logoUrl}/>, mountNode ) diff --git a/example/src/blocks/props.example b/example/src/blocks/props.example index 51b11b91a..4b1457329 100644 --- a/example/src/blocks/props.example +++ b/example/src/blocks/props.example @@ -6,6 +6,6 @@ const Avatar = styled('img')` ` render( - <Avatar src={avatarUrl} rounded />, + <Avatar src={logoUrl} rounded />, mountNode ) diff --git a/example/src/blocks/pseudo.example b/example/src/blocks/pseudo.example index 448ec0334..8a9629881 100644 --- a/example/src/blocks/pseudo.example +++ b/example/src/blocks/pseudo.example @@ -10,6 +10,6 @@ const Avatar = styled('img')` ` render( - <Avatar src={avatarUrl} rounded />, + <Avatar src={logoUrl} rounded />, mountNode ) diff --git a/example/src/blocks/styled-with-object.example b/example/src/blocks/styled-with-object.example index e6401c9df..4ad40987c 100644 --- a/example/src/blocks/styled-with-object.example +++ b/example/src/blocks/styled-with-object.example @@ -4,4 +4,4 @@ const Avatar = styled('img')({ borderRadius: '50%' }) -render(<Avatar src={avatarUrl} />, mountNode) +render(<Avatar src={logoUrl} />, mountNode) diff --git a/example/src/blocks/styling-any-component.example b/example/src/blocks/styling-any-component.example index 16c56317a..e5d0f4d02 100644 --- a/example/src/blocks/styling-any-component.example +++ b/example/src/blocks/styling-any-component.example @@ -7,4 +7,4 @@ const Avatar = styled(Image)` border-radius: 50%; ` -render(<Avatar src={avatarUrl} />, mountNode) +render(<Avatar src={logoUrl} />, mountNode) diff --git a/example/src/blocks/theming.example b/example/src/blocks/theming.example index 38b8e1a15..d5d1e15c7 100644 --- a/example/src/blocks/theming.example +++ b/example/src/blocks/theming.example @@ -14,7 +14,7 @@ const Avatar = styled('img')` render( <ThemeProvider theme={theme}> - <Avatar src={avatarUrl} /> + <Avatar src={logoUrl} /> </ThemeProvider>, mountNode ) diff --git a/src/react/index.js b/src/react/index.js index b39d4912f..9661ba65b 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -14,6 +14,8 @@ export { objStyle } from '../index' +const push = (obj, items) => Array.prototype.push.apply(obj, items) + export default function (tag, objs, vars = [], content) { if (!tag) { throw new Error( @@ -70,14 +72,15 @@ export default function (tag, objs, vars = [], content) { let finalObjs = [] + if (tag.__emotion_spec) { - Array.prototype.push.apply( + push( finalObjs, reduce( tag.__emotion_spec, (accum, spec) => { - Array.prototype.push.apply(accum, spec.objs) - Array.prototype.push.apply( + push(accum, spec.objs) + push( accum, spec.content(map(spec.vars, getValue)) ) @@ -88,14 +91,14 @@ export default function (tag, objs, vars = [], content) { ) } - Array.prototype.push.apply(finalObjs, objs) + push(finalObjs, objs) if (content) { - Array.prototype.push.apply(finalObjs, content(map(vars, getValue))) + push(finalObjs, content(map(vars, getValue))) } if (mergedProps.className) { - Array.prototype.push.apply(finalObjs, mergedProps.className.split(' ')) + push(finalObjs, mergedProps.className.split(' ')) } const className = css(map(finalObjs, getValue)) From 7886bbe8e009f912f1ebe75eaceed7ed2cf9c4e8 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Thu, 20 Jul 2017 22:28:02 -0600 Subject: [PATCH 33/57] Add test for dynamic properties of styled --- test/__snapshots__/react.test.js.snap | 42 +++++++++++++++++++-------- test/react.test.js | 22 ++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index ea71727c5..a4daab801 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -42,12 +42,14 @@ exports[`styled component as selector 1`] = ` font-size: 20px; } -.css-1d77tm { - display: -webkit-box,-ms-flexbox,flex; +.css-qrvryj { + display: -webkit-box; + display: -ms-flexbox; + display: flex; } <div - className="css-1d77tm" + className="css-qrvryj" > hello <h1 @@ -148,6 +150,18 @@ exports[`styled function in expression 1`] = ` </h1> `; +exports[`styled handles more than 10 dynamic properties 1`] = ` +.css-1i41sa5 { + background: white,black,underline,block,3px,25px,500px,100,18px,center,solid 1px red; +} + +<h1 + className="legacy__class css-1i41sa5" +> + hello world +</h1> +`; + exports[`styled higher order component 1`] = ` .css-11fn3ay { font-size: 20px; @@ -190,15 +204,17 @@ exports[`styled name 1`] = ` `; exports[`styled nested 1`] = ` -.css-bex773 { - display: -webkit-box,-ms-flexbox,flex; +.css-1fir8qd { + display: -webkit-box; + display: -ms-flexbox; + display: flex; } -.css-bex773 div { +.css-1fir8qd div { color: green; } -.css-bex773 div span { +.css-1fir8qd div span { color: red; } @@ -207,7 +223,7 @@ exports[`styled nested 1`] = ` } <div - className="css-bex773" + className="css-1fir8qd" > hello <h1 @@ -232,23 +248,25 @@ exports[`styled no dynamic 1`] = ` `; exports[`styled object composition 1`] = ` -.css-ifc29m { +.css-xxf8x1 { border-radius: 50%; -webkit-transition: -webkit-transform 400ms ease-in-out; - transition: -webkit-transform 400ms ease-in-out,transform 400ms ease-in-out,transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out; + transition: -webkit-transform 400ms ease-in-out; + transition: transform 400ms ease-in-out; + transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out; border: 3px solid currentColor; width: 96px; height: 96px; color: blue; } -.css-ifc29m:hover { +.css-xxf8x1:hover { -webkit-transform: scale(1.2); transform: scale(1.2); } <img - className="css-ifc29m" + className="css-xxf8x1" /> `; diff --git a/test/react.test.js b/test/react.test.js index 76ec18c51..2dc3ae700 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -152,6 +152,28 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) + test('handles more than 10 dynamic properties', () => { + const H1 = styled('h1')` + background: ${'white'}; + color: ${'black'}; + text-decoration: ${'underline'}; + display: ${'block'}; + border-radius: ${'3px'}; + padding: ${'25px'}; + width: ${'500px'}; + z-index: ${100}; + font-size: ${'18px'}; + text-align: ${'center'}; + border: ${'solid 1px red'}; + ` + + const tree = renderer + .create(<H1 className={'legacy__class'}>hello world</H1>) + .toJSON() + + expect(tree).toMatchSnapshotWithEmotion() + }) + test('component as selector', () => { const fontSize = '20px' const H1 = styled.h1`font-size: ${fontSize};` From cd4a25a800411e0451be4b0b76bc5bddf1264bea Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Thu, 20 Jul 2017 23:45:16 -0600 Subject: [PATCH 34/57] add failing snapshots --- test/__snapshots__/css.test.js.snap | 53 ++++++++++++++++------------- test/css.test.js | 4 +-- test/react.test.js | 11 ++++-- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index f3d6d84d8..922108a07 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -1,68 +1,72 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css composes 1`] = ` -.css-1cjz2s1 { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; - -webkit-justify-content: center; - -ms-flex-pack: center; +.css-w9g11r { + display: -webkit-box; + display: -ms-flexbox; + display: flex; -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; } <div - className="css-1cjz2s1" + className="css-w9g11r" /> `; exports[`css composes with objects 1`] = ` -.css-xcpja8 { - display: -webkit-box,-ms-flexbox,flex,block; +.css-17yqurf { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + display: block; width: 30px; height: calc(40vw - 50px); - -webkit-justify-content: center; - -ms-flex-pack: center; -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; } -.css-xcpja8:hover { +.css-17yqurf:hover { color: blue; } -.css-xcpja8:after { +.css-17yqurf:after { content: " "; color: red; } @media (min-width: 420px) { - .css-xcpja8 { + .css-17yqurf { color: green; } } <div - className="css-xcpja8" + className="css-17yqurf" /> `; exports[`css composes with undefined values 1`] = ` -.css-1rsyrbq { - -webkit-justify-content: center; - -ms-flex-pack: center; +.css-awzhlo { -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; } <div - className="css-1rsyrbq" + className="css-awzhlo" /> `; exports[`css handles more than 10 dynamic properties 1`] = ` -.css-550542 { +.css-n3p8vq { + -webkit-text-decoration: underline; + text-decoration: underline; + border-right: solid blue 54px; background: white; color: black; - text-decoration: underline; display: block; border-radius: 3px; padding: 25px; @@ -70,20 +74,21 @@ exports[`css handles more than 10 dynamic properties 1`] = ` z-index: 100; font-size: 18px; text-align: center; - border: solid 1px red; } <div - className="css-550542" + className="css-n3p8vq" /> `; exports[`css handles objects 1`] = ` -.css-xmm9em { - display: -webkit-box,-ms-flexbox,flex; +.css-71uu79 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; } <div - className="css-xmm9em" + className="css-71uu79" /> `; diff --git a/test/css.test.js b/test/css.test.js index 75e937dce..2f5a70047 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -10,9 +10,10 @@ expect.extend(matcher) describe('css', () => { test('handles more than 10 dynamic properties', () => { const cls1 = css` + text-decoration: ${'underline'}; + border-right: solid blue 54px; background: ${'white'}; color: ${'black'}; - text-decoration: ${'underline'}; display: ${'block'}; border-radius: ${'3px'}; padding: ${'25px'}; @@ -20,7 +21,6 @@ describe('css', () => { z-index: ${100}; font-size: ${'18px'}; text-align: ${'center'}; - border: ${'solid 1px red'}; ` const tree = renderer.create(<div className={cls1} />).toJSON() diff --git a/test/react.test.js b/test/react.test.js index 2dc3ae700..6903188b4 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -154,9 +154,10 @@ describe('styled', () => { test('handles more than 10 dynamic properties', () => { const H1 = styled('h1')` + text-decoration: ${'underline'}; + border-right: solid blue 54px; background: ${'white'}; color: ${'black'}; - text-decoration: ${'underline'}; display: ${'block'}; border-radius: ${'3px'}; padding: ${'25px'}; @@ -164,11 +165,15 @@ describe('styled', () => { z-index: ${100}; font-size: ${'18px'}; text-align: ${'center'}; - border: ${'solid 1px red'}; + border-left: ${p => p.theme.blue}; ` const tree = renderer - .create(<H1 className={'legacy__class'}>hello world</H1>) + .create( + <H1 className={'legacy__class'} theme={{ blue: 'blue' }}> + hello world + </H1> + ) .toJSON() expect(tree).toMatchSnapshotWithEmotion() From d44c6ba05a2f2dbf774a43ba012c4d4c23bb54e2 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Fri, 21 Jul 2017 20:15:27 +1000 Subject: [PATCH 35/57] Remove shorthandPropertyExpansions --- .../CSSPropertyOperations/CSSProperty.js | 69 +------------------ .../dangerousStyleValue.js | 3 +- 2 files changed, 2 insertions(+), 70 deletions(-) diff --git a/src/glamor/CSSPropertyOperations/CSSProperty.js b/src/glamor/CSSPropertyOperations/CSSProperty.js index 642a839d4..f01a44925 100644 --- a/src/glamor/CSSPropertyOperations/CSSProperty.js +++ b/src/glamor/CSSPropertyOperations/CSSProperty.js @@ -76,71 +76,4 @@ Object.keys(isUnitlessNumber).forEach(function (prop) { }) }) -/** - * Most style properties can be unset by doing .style[prop] = '' but IE8 - * doesn't like doing that with shorthand properties so for the properties that - * IE8 breaks on, which are listed here, we instead unset each of the - * individual properties. See http://bugs.jquery.com/ticket/12385. - * The 4-value 'clock' properties like margin, padding, border-width seem to - * behave without any problems. Curiously, list-style works too without any - * special prodding. - */ -let shorthandPropertyExpansions = { - background: { - backgroundAttachment: true, - backgroundColor: true, - backgroundImage: true, - backgroundPositionX: true, - backgroundPositionY: true, - backgroundRepeat: true - }, - backgroundPosition: { - backgroundPositionX: true, - backgroundPositionY: true - }, - border: { - borderWidth: true, - borderStyle: true, - borderColor: true - }, - borderBottom: { - borderBottomWidth: true, - borderBottomStyle: true, - borderBottomColor: true - }, - borderLeft: { - borderLeftWidth: true, - borderLeftStyle: true, - borderLeftColor: true - }, - borderRight: { - borderRightWidth: true, - borderRightStyle: true, - borderRightColor: true - }, - borderTop: { - borderTopWidth: true, - borderTopStyle: true, - borderTopColor: true - }, - font: { - fontStyle: true, - fontVariant: true, - fontWeight: true, - fontSize: true, - lineHeight: true, - fontFamily: true - }, - outline: { - outlineWidth: true, - outlineStyle: true, - outlineColor: true - } -} - -const CSSProperty = { - isUnitlessNumber: isUnitlessNumber, - shorthandPropertyExpansions: shorthandPropertyExpansions -} - -export default CSSProperty +export default isUnitlessNumber diff --git a/src/glamor/CSSPropertyOperations/dangerousStyleValue.js b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js index 69dd058be..6e724bbcc 100644 --- a/src/glamor/CSSPropertyOperations/dangerousStyleValue.js +++ b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js @@ -9,10 +9,9 @@ * @providesModule dangerousStyleValue */ -import CSSProperty from './CSSProperty' +import isUnitlessNumber from './CSSProperty' import warning from 'fbjs/lib/warning' -let isUnitlessNumber = CSSProperty.isUnitlessNumber let styleWarnings = {} /** From 1bb13e3988c167dde28eeb5b28df323420122be0 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Fri, 21 Jul 2017 22:06:44 -0600 Subject: [PATCH 36/57] wtf is this bug --- src/babel.js | 39 +- src/parser.js | 3 + test/babel/__snapshots__/styled.test.js.snap | 22 + test/babel/styled.test.js | 484 ++++++++++--------- 4 files changed, 298 insertions(+), 250 deletions(-) diff --git a/src/babel.js b/src/babel.js index 3e219ea9d..e6f43d56f 100644 --- a/src/babel.js +++ b/src/babel.js @@ -92,24 +92,23 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { ) const { styles, composesCount } = parseCSS(src, false) - const inputClasses = [] - const composeValues = [] - for (let i = 0; i < composesCount; i++) { - composeValues.push(path.node.quasi.expressions[i]) - } - - inputClasses.push(createAstObj(styles, false, composesCount, t)) - + // const inputClasses = [] + // const composeValues = [] + // for (let i = 0; i < composesCount; i++) { + // composeValues.push(path.node.quasi.expressions[i]) + // } + + const objs = path.node.quasi.expressions.slice(0, composesCount) + const vars = path.node.quasi.expressions + .slice(composesCount) const args = [ tag, - t.arrayExpression(composeValues), - t.arrayExpression(path.node.quasi.expressions.slice(composesCount)), + t.arrayExpression(objs), + t.arrayExpression(vars), t.functionExpression( t.identifier('createEmotionStyledRules'), - path.node.quasi.expressions - .slice(composesCount) - .map((x, i) => t.identifier(`x${i}`)), - t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) + vars.map((x, i) => t.identifier(`x${i}`)), + t.blockStatement([t.returnStatement(t.arrayExpression([createAstObj(styles, false, composesCount, t)]))]) ) ] @@ -208,10 +207,11 @@ function getDynamicMatches (str) { while ((match = re.exec(str)) !== null) { matches.push({ value: match[0], - p1: match[1], + p1: parseInt(match[1], 10), index: match.index }) } + return matches } @@ -222,12 +222,14 @@ function replacePlaceholdersWithExpressions ( composesCount, t ) { + console.log('matches', JSON.stringify(matches, null, 2)) const templateElements = [] const templateExpressions = [] let cursor = 0 let hasSingleInterpolation = false forEach(matches, ({ value, p1, index }, i) => { const preMatch = str.substring(cursor, index) + console.log('value', value, 'preMatch', preMatch) cursor = cursor + preMatch.length + value.length if (preMatch) { templateElements.push( @@ -257,9 +259,9 @@ function replacePlaceholdersWithExpressions ( ) } }) - if (hasSingleInterpolation) { - return templateExpressions[0] - } + // if (hasSingleInterpolation) { + // return templateExpressions[0] + // } return t.templateLiteral(templateElements, templateExpressions) } @@ -306,6 +308,7 @@ function objValueToAst (value, expressions, composesCount, t) { } return t.stringLiteral(value) } else if (Array.isArray(value)) { + console.log('value is an array', value) return t.arrayExpression( value.map(v => objValueToAst(v, expressions, composesCount, t)) ) diff --git a/src/parser.js b/src/parser.js index 75395e16a..b84a164fd 100644 --- a/src/parser.js +++ b/src/parser.js @@ -55,6 +55,8 @@ export function parseCSS ( const styles = expandCSSFallbacks(prefixer(postcssJs.objectify(root))) + console.log(styles) + return { styles, staticCSSRules: vars === 0 && extractStatic @@ -79,6 +81,7 @@ export function expandCSSFallbacks (style: { [string]: any }) { if (Array.isArray(style[key])) { accum[key] = style[key].join(`; ${processStyleName(key)}: `) } else if (Object.prototype.toString.call(style[key]) === '[object Object]') { + console.log('thought it was an object', key, style[key]) accum[key] = expandCSSFallbacks(style[key]) } else { accum[key] = style[key]; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 0eef938d6..66f259c8e 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -1,5 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`babel styled component dynamic fns 1`] = ` +"const Avatar = styled('img', [], [props => props.theme.borderRadius, props => props.theme.borderColor], function createEmotionStyledRules(x0, x1) { + return [{ + 'width': '96px', + 'height': '96px', + 'borderRadius': x0, + 'border': \`1px solid \${x1}\` + }]; +});" +`; + exports[`babel styled component extract basic 1`] = ` "import \\"./styled.test.emotion.css\\"; const H1 = styled(\\"h1\\", [\\"styled-H1-10x82eg\\"]);" @@ -68,6 +79,17 @@ const H1 = styled('h1', [props => { });" `; +exports[`babel styled component inline dynamic fns 1`] = ` +"const Avatar = styled('img', [], [props => props.theme.borderRadius, props => props.theme.borderColor], function createEmotionStyledRules(x0, x1) { + return [{ + 'width': '96px', + 'height': '96px', + 'borderRadius': x0, + 'border': \`1px solid \${x1}\` + }]; +});" +`; + exports[`babel styled component inline function call 1`] = ` "styled(MyComponent, [], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [{ diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index d454fc8f3..6d362448f 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -8,245 +8,265 @@ jest.mock('fs') fs.existsSync.mockReturnValue(true) describe('babel styled component', () => { - describe('inline', () => { - test('no use', () => { - const basic = 'styled.h1``' - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('no dynamic', () => { - const basic = 'styled.h1`color:blue;`' - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('styled component as selector', () => { - const basic = ` - const SomeComponent = styled.div\` \` - styled.h1\` - color:blue; - .\${SomeComponent} { - color: green; - } - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('basic', () => { - const basic = "const H1 = styled.h1`font-size: ${fontSize + 'px'};`" - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('nested', () => { - const basic = - 'const H1 = styled.h1`' + - "font-size: ${fontSize + 'px'};" + - '& div { color: blue;' + - '& span { color: red } }' + - '`' - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('interpolation in different places', () => { - const basic = ` - const H1 = styled.h1\` - font-size: \${fontSize + 'px'}; - height: 20px; - transform: translateX(\${(props) => props.translateX}); - height1: \${something}wow; - width: w\${something}ow; - transform: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); - transform1: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); - transform2: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}; - \`` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('media query', () => { - const basic = - 'const H1 = styled.h1`@media print {' + - ' font-size: 10pt' + - '}' + - '@media screen {' + - ' .child-selector { font-size: 13px }' + - '}' + - '@media screen, print {' + - ' &:hover + & { line-height: 1.2 }' + - '}' + - '@media only screen ' + - ' and (min-device-width: 320px) ' + - ' and (max-device-width: 480px)' + - ' and (-webkit-min-device-pixel-ratio: 2) {' + - ' .child-selector { line-height: 1.4 }' + - '}`' - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('function call', () => { - const basic = "styled(MyComponent)`font-size: ${fontSize + 'px'};`" - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('name is correct with no identifier', () => { - const basic = ` - css\` - margin: 12px 48px; - color: #ffffff; - \` - ` - const { code } = babel.transform(basic, { - plugins: [[plugin]] - }) - expect(code).toMatchSnapshot() - }) + // describe('inline', () => { + // test('no use', () => { + // const basic = 'styled.h1``' + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('no dynamic', () => { + // const basic = 'styled.h1`color:blue;`' + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + test('dynamic fns', () => { + const basic = `const Avatar = styled('img')\` + width: 96px; + height: 96px; - test('objects fn call', () => { - const basic = ` - const H1 = styled('h1')({ - display: 'flex' - })` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) + border-radius: $\{props => + props.theme.borderRadius}; - test('objects based on props', () => { - const basic = ` - const H1 = styled('h1')({ padding: 10 },props => ({ - display: props.display - }))` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('object composes with classes', () => { - const basic = ` - const H1 = styled('h1')('some-class',props => ({ - display: props.display - }))` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('objects prefixed', () => { - const basic = ` - const H1 = styled('h1')({ - borderRadius: '50%', - transition: 'transform 400ms ease-in-out', - boxSizing: 'border-box', - display: 'flex', - ':hover': { - transform: 'scale(1.2)' - } - }, props => { - padding: props.padding - })` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('styled. objects', () => { - const basic = ` - const H1 = styled.h1({ padding: 10 },props => ({ - display: props.display - }))` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('styled objects prefixed', () => { - const basic = ` - const H1 = styled.h1({ - borderRadius: '50%', - transition: 'transform 400ms ease-in-out', - boxSizing: 'border-box', - display: 'flex', - ':hover': { - transform: 'scale(1.2)' - } - },props => ({ - display: props.display - }))` - const { code } = babel.transform(basic, { - plugins: [plugin] - }) - expect(code).toMatchSnapshot() - }) - - test('composes based on props', () => { - const basic = `const cls1 = css\` width: 20px; \` - const H1 = styled.h1\` - composes: $\{props => { - return props.a ? cssA : cssB - }}; - font-size: \${fontSize + 'px'}; - height: 20px; - transform: translateX(\${(props) => props.translateX}); + border: 1px solid $\{props => + props.theme.borderColor}; \`` const { code } = babel.transform(basic, { plugins: [plugin] }) + console.log(code) expect(code).toMatchSnapshot() }) - }) - - describe('extract', () => { - test('no use', () => { - const basic = 'styled.h1``' - const { code } = babel.transform(basic, { - plugins: [[plugin, { extractStatic: true }]], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(1) - expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() - }) - - test('basic', () => { - const basic = - "const H1 = styled.h1`display: flex; justify-content: center; width: var(--css-hash-0); &:hover { background-color: green; } @media (max-width: 500px) { height: var(--css-hash-1); position: fixed; } @media print { display: none; } &::before { color: blue; width: 20px; height: 20px; content: 'pseudo' }`" - const { code } = babel.transform(basic, { - plugins: [[plugin, { extractStatic: true }]], - filename: __filename, - babelrc: false - }) + // + // + // + // test('styled component as selector', () => { + // const basic = ` + // const SomeComponent = styled.div\` \` + // styled.h1\` + // color:blue; + // .\${SomeComponent} { + // color: green; + // } + // \`` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('basic', () => { + // const basic = "const H1 = styled.h1`font-size: ${fontSize + 'px'};`" + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('nested', () => { + // const basic = + // 'const H1 = styled.h1`' + + // "font-size: ${fontSize + 'px'};" + + // '& div { color: blue;' + + // '& span { color: red } }' + + // '`' + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('interpolation in different places', () => { + // const basic = ` + // const H1 = styled.h1\` + // font-size: \${fontSize + 'px'}; + // height: 20px; + // transform: translateX(\${(props) => props.translateX}); + // height1: \${something}wow; + // width: w\${something}ow; + // transform: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + // transform1: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + // transform2: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}; + // \`` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('media query', () => { + // const basic = + // 'const H1 = styled.h1`@media print {' + + // ' font-size: 10pt' + + // '}' + + // '@media screen {' + + // ' .child-selector { font-size: 13px }' + + // '}' + + // '@media screen, print {' + + // ' &:hover + & { line-height: 1.2 }' + + // '}' + + // '@media only screen ' + + // ' and (min-device-width: 320px) ' + + // ' and (max-device-width: 480px)' + + // ' and (-webkit-min-device-pixel-ratio: 2) {' + + // ' .child-selector { line-height: 1.4 }' + + // '}`' + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('function call', () => { + // const basic = "styled(MyComponent)`font-size: ${fontSize + 'px'};`" + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('name is correct with no identifier', () => { + // const basic = ` + // css\` + // margin: 12px 48px; + // color: #ffffff; + // \` + // ` + // const { code } = babel.transform(basic, { + // plugins: [[plugin]] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('objects fn call', () => { + // const basic = ` + // const H1 = styled('h1')({ + // display: 'flex' + // })` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('objects based on props', () => { + // const basic = ` + // const H1 = styled('h1')({ padding: 10 },props => ({ + // display: props.display + // }))` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('object composes with classes', () => { + // const basic = ` + // const H1 = styled('h1')('some-class',props => ({ + // display: props.display + // }))` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('objects prefixed', () => { + // const basic = ` + // const H1 = styled('h1')({ + // borderRadius: '50%', + // transition: 'transform 400ms ease-in-out', + // boxSizing: 'border-box', + // display: 'flex', + // ':hover': { + // transform: 'scale(1.2)' + // } + // }, props => { + // padding: props.padding + // })` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('styled. objects', () => { + // const basic = ` + // const H1 = styled.h1({ padding: 10 },props => ({ + // display: props.display + // }))` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('styled objects prefixed', () => { + // const basic = ` + // const H1 = styled.h1({ + // borderRadius: '50%', + // transition: 'transform 400ms ease-in-out', + // boxSizing: 'border-box', + // display: 'flex', + // ':hover': { + // transform: 'scale(1.2)' + // } + // },props => ({ + // display: props.display + // }))` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // + // test('composes based on props', () => { + // const basic = `const cls1 = css\` width: 20px; \` + // const H1 = styled.h1\` + // composes: $\{props => { + // return props.a ? cssA : cssB + // }}; + // font-size: \${fontSize + 'px'}; + // height: 20px; + // transform: translateX(\${(props) => props.translateX}); + // \`` + // const { code } = babel.transform(basic, { + // plugins: [plugin] + // }) + // expect(code).toMatchSnapshot() + // }) + // }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(2) - expect(fs.writeFileSync.mock.calls[1][1]).toMatchSnapshot() - }) - }) + // describe('extract', () => { + // test('no use', () => { + // const basic = 'styled.h1``' + // const { code } = babel.transform(basic, { + // plugins: [[plugin, { extractStatic: true }]], + // filename: __filename, + // babelrc: false + // }) + // expect(code).toMatchSnapshot() + // expect(fs.writeFileSync).toHaveBeenCalledTimes(1) + // expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() + // }) + // + // test('basic', () => { + // const basic = + // "const H1 = styled.h1`display: flex; justify-content: center; width: var(--css-hash-0); &:hover { background-color: green; } @media (max-width: 500px) { height: var(--css-hash-1); position: fixed; } @media print { display: none; } &::before { color: blue; width: 20px; height: 20px; content: 'pseudo' }`" + // const { code } = babel.transform(basic, { + // plugins: [[plugin, { extractStatic: true }]], + // filename: __filename, + // babelrc: false + // }) + // + // expect(code).toMatchSnapshot() + // expect(fs.writeFileSync).toHaveBeenCalledTimes(2) + // expect(fs.writeFileSync.mock.calls[1][1]).toMatchSnapshot() + // }) + // }) }) From f3db5eb7027b1746e99e0a9e04e038facd34e440 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Fri, 21 Jul 2017 23:35:51 -0600 Subject: [PATCH 37/57] fixed - bad parsing for values like `50%` fixed - content was not getting correct arguments --- package.json | 1 - src/ast-object.js | 138 +++++ src/babel.js | 159 +----- .../dangerousStyleValue.js | 47 +- src/parser.js | 9 +- src/react/index.js | 8 +- test/__snapshots__/css-prop.test.js.snap | 32 +- test/__snapshots__/css.test.js.snap | 4 +- test/__snapshots__/keyframes.test.js.snap | 18 +- test/__snapshots__/react.test.js.snap | 50 +- test/__snapshots__/server.test.js.snap | 14 +- .../babel/__snapshots__/css-prop.test.js.snap | 2 +- test/babel/__snapshots__/css.test.js.snap | 22 +- .../__snapshots__/font-face.test.js.snap | 4 +- .../__snapshots__/inject-global.test.js.snap | 4 +- test/babel/__snapshots__/macro.test.js.snap | 4 +- test/babel/__snapshots__/styled.test.js.snap | 43 +- test/babel/styled.test.js | 508 +++++++++--------- test/macro/__snapshots__/css.test.js.snap | 36 +- .../__snapshots__/keyframes.test.js.snap | 18 +- test/macro/__snapshots__/react.test.js.snap | 28 +- vue/index.js | 1 - vue/macro.js | 1 - 23 files changed, 574 insertions(+), 577 deletions(-) create mode 100644 src/ast-object.js delete mode 100644 vue/index.js delete mode 100644 vue/macro.js diff --git a/package.json b/package.json index d7b8487ba..0f1599607 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "lib", "react", "server.js", - "vue", "dist/DO-NOT-USE.min.js", "macro.js" ], diff --git a/src/ast-object.js b/src/ast-object.js new file mode 100644 index 000000000..f03f27321 --- /dev/null +++ b/src/ast-object.js @@ -0,0 +1,138 @@ +import { forEach } from './utils' +export default class ASTObject { + obj: { [string]: any } + expressions: Array<any> + composesCount: number + t: any + + constructor (obj, expressions, composesCount, t) { + this.obj = obj + this.expressions = expressions + this.composesCount = composesCount + this.t = t + } + + toAST () { + const {obj, t} = this + + const props = [] + for (let key in obj) { + const rawValue = obj[key] + const {computed, composes, ast: keyAST} = this.objKeyToAst(key) + + let valueAST + if (composes) { + // valueAST = t.arrayExpression(expressions.slice(0, composesCount)) + continue + } else { + valueAST = this.objValueToAst(rawValue) + } + + props.push(t.objectProperty(keyAST, valueAST, computed)) + } + return t.objectExpression(props) + } + + objKeyToAst (key): { computed: boolean, ast: any, composes: boolean } { + const {t} = this + const matches = this.getDynamicMatches(key) + + if (matches.length) { + return { + computed: true, + composes: key === 'composes', + ast: this.replacePlaceholdersWithExpressions( + matches, + key + ) + } + } + + return { + computed: false, + composes: key === 'composes', + ast: t.stringLiteral(key) + } + } + + objValueToAst (value) { + const {expressions, composesCount, t} = this + + if (typeof value === 'string') { + const matches = this.getDynamicMatches(value) + if (matches.length) { + return this.replacePlaceholdersWithExpressions( + matches, + value + ) + } + return t.stringLiteral(value) + } else if (Array.isArray(value)) { + // should never hit here + return t.arrayExpression(value.map(v => this.objValueToAst(v))) + } + + const obj = new this.constructor(value, expressions, composesCount, t) + return obj.toAST() + } + + getDynamicMatches (str) { + const re = /xxx(\d+)xxx/gm + let match + const matches = [] + while ((match = re.exec(str)) !== null) { + matches.push({ + value: match[0], + p1: parseInt(match[1], 10), + index: match.index + }) + } + + return matches + } + + replacePlaceholdersWithExpressions (matches: any[], str: string) { + const {expressions, composesCount, t} = this + + const templateElements = [] + const templateExpressions = [] + let cursor = 0 + // not sure how to detect when to add 'px' + // let hasSingleInterpolation = false + forEach(matches, ({value, p1, index}, i) => { + const preMatch = str.substring(cursor, index) + cursor = cursor + preMatch.length + value.length + if (preMatch) { + templateElements.push( + t.templateElement({raw: preMatch, cooked: preMatch}) + ) + } else if (i === 0) { + templateElements.push(t.templateElement({raw: '', cooked: ''})) + } + // if (value === str) { + // hasSingleInterpolation = true + // } + + templateExpressions.push( + expressions + ? expressions[p1 - composesCount] + : t.identifier(`x${p1 - composesCount}`) + ) + if (i === matches.length - 1) { + templateElements.push( + t.templateElement( + { + raw: str.substring(index + value.length), + cooked: str.substring(index + value.length) + }, + true + ) + ) + } + }) + // if (hasSingleInterpolation) { + // return templateExpressions[0] + // } + return t.templateLiteral(templateElements, templateExpressions) + } +} diff --git a/src/babel.js b/src/babel.js index e6f43d56f..0e21b16e1 100644 --- a/src/babel.js +++ b/src/babel.js @@ -9,6 +9,7 @@ import { inline } from './inline' import { parseCSS, expandCSSFallbacks } from './parser' import { getIdentifierName } from './babel-utils' import cssProps from './css-prop' +import ASTObject from './ast-object' export function replaceCssWithCallExpression ( path, @@ -42,7 +43,7 @@ export function replaceCssWithCallExpression ( composeValues.push(path.node.quasi.expressions[i]) } - inputClasses.push(createAstObj(styles, false, composesCount, t)) + inputClasses.push(new ASTObject(styles, false, composesCount, t).toAST()) const vars = path.node.quasi.expressions.slice(composesCount) path.replaceWith( @@ -85,11 +86,7 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { ]) } - const { src } = inline( - path.node.quasi, - getIdentifierName(path, t), - 'css' - ) + const { src } = inline(path.node.quasi, getIdentifierName(path, t), 'css') const { styles, composesCount } = parseCSS(src, false) // const inputClasses = [] @@ -99,8 +96,7 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { // } const objs = path.node.quasi.expressions.slice(0, composesCount) - const vars = path.node.quasi.expressions - .slice(composesCount) + const vars = path.node.quasi.expressions.slice(composesCount) const args = [ tag, t.arrayExpression(objs), @@ -108,7 +104,11 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { t.functionExpression( t.identifier('createEmotionStyledRules'), vars.map((x, i) => t.identifier(`x${i}`)), - t.blockStatement([t.returnStatement(t.arrayExpression([createAstObj(styles, false, composesCount, t)]))]) + t.blockStatement([ + t.returnStatement( + t.arrayExpression([new ASTObject(styles, false, composesCount, t).toAST()]) + ) + ]) ) ] @@ -200,147 +200,6 @@ function prefixAst (args, t) { return args } -function getDynamicMatches (str) { - const re = /xxx(\d+)xxx/gm - let match - const matches = [] - while ((match = re.exec(str)) !== null) { - matches.push({ - value: match[0], - p1: parseInt(match[1], 10), - index: match.index - }) - } - - return matches -} - -function replacePlaceholdersWithExpressions ( - matches: any[], - str: string, - expressions?: any[], - composesCount, - t -) { - console.log('matches', JSON.stringify(matches, null, 2)) - const templateElements = [] - const templateExpressions = [] - let cursor = 0 - let hasSingleInterpolation = false - forEach(matches, ({ value, p1, index }, i) => { - const preMatch = str.substring(cursor, index) - console.log('value', value, 'preMatch', preMatch) - cursor = cursor + preMatch.length + value.length - if (preMatch) { - templateElements.push( - t.templateElement({ raw: preMatch, cooked: preMatch }) - ) - } else if (i === 0) { - templateElements.push(t.templateElement({ raw: '', cooked: '' })) - } - if (value === str) { - hasSingleInterpolation = true - } - - templateExpressions.push( - expressions - ? expressions[p1 - composesCount] - : t.identifier(`x${p1 - composesCount}`) - ) - if (i === matches.length - 1) { - templateElements.push( - t.templateElement( - { - raw: str.substring(index + value.length), - cooked: str.substring(index + value.length) - }, - true - ) - ) - } - }) - // if (hasSingleInterpolation) { - // return templateExpressions[0] - // } - return t.templateLiteral(templateElements, templateExpressions) -} - -function objKeyToAst ( - key, - expressions, - composesCount: number, - t -): { computed: boolean, ast: any, composes: boolean } { - const matches = getDynamicMatches(key) - - if (matches.length) { - return { - computed: true, - composes: key === 'composes', - ast: replacePlaceholdersWithExpressions( - matches, - key, - expressions, - composesCount, - t - ) - } - } - - return { - computed: false, - composes: key === 'composes', - ast: t.stringLiteral(key) - } -} - -function objValueToAst (value, expressions, composesCount, t) { - if (typeof value === 'string') { - const matches = getDynamicMatches(value) - if (matches.length) { - return replacePlaceholdersWithExpressions( - matches, - value, - expressions, - composesCount, - t - ) - } - return t.stringLiteral(value) - } else if (Array.isArray(value)) { - console.log('value is an array', value) - return t.arrayExpression( - value.map(v => objValueToAst(v, expressions, composesCount, t)) - ) - } - - return createAstObj(value, expressions, composesCount, t) -} - -function createAstObj (obj, expressions, composesCount, t) { - const props = [] - - for (let key in obj) { - const rawValue = obj[key] - const { computed, composes, ast: keyAST } = objKeyToAst( - key, - expressions, - composesCount, - t - ) - - let valueAST - if (composes) { - valueAST = t.arrayExpression(expressions.slice(0, composesCount)) - } else { - valueAST = objValueToAst(rawValue, expressions, composesCount, t) - } - - props.push(t.objectProperty(keyAST, valueAST, computed)) - } - return t.objectExpression(props) -} - const visited = Symbol('visited') export default function (babel) { diff --git a/src/glamor/CSSPropertyOperations/dangerousStyleValue.js b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js index 6e724bbcc..6db41a341 100644 --- a/src/glamor/CSSPropertyOperations/dangerousStyleValue.js +++ b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js @@ -10,7 +10,6 @@ */ import isUnitlessNumber from './CSSProperty' -import warning from 'fbjs/lib/warning' let styleWarnings = {} @@ -25,16 +24,6 @@ let styleWarnings = {} * @return {string} Normalized style value with dimensions applied. */ function dangerousStyleValue (name, value, component) { - // Note that we've removed escapeTextForBrowser() calls here since the - // whole string will be escaped when the attribute is injected into - // the markup. If you provide unsafe user data here they can inject - // arbitrary CSS which may be problematic (I couldn't repro this): - // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet - // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/ - // This is not an XSS hole but instead a potential CSS injection issue - // which has lead to a greater discussion about how we're going to - // trust URLs moving forward. See #2115901 - let isEmpty = value == null || typeof value === 'boolean' || value === '' if (isEmpty) { return '' @@ -50,37 +39,11 @@ function dangerousStyleValue (name, value, component) { } if (typeof value === 'string') { - if (process.env.NODE_ENV !== 'production') { - // Allow '0' to pass through without warning. 0 is already special and - // doesn't require units, so we don't need to warn about it. - if (component && value !== '0') { - let owner = component._currentElement._owner - let ownerName = owner ? owner.getName() : null - if (ownerName && !styleWarnings[ownerName]) { - styleWarnings[ownerName] = {} - } - let warned = false - if (ownerName) { - let warnings = styleWarnings[ownerName] - warned = warnings[name] - if (!warned) { - warnings[name] = true - } - } - if (!warned) { - process.env.NODE_ENV !== 'production' - ? warning( - false, - 'a `%s` tag (owner: `%s`) was passed a numeric string value ' + - 'for CSS property `%s` (value: `%s`) which will be treated ' + - 'as a unitless number in a future version of React.', - component._currentElement.type, - ownerName || 'unknown', - name, - value - ) - : void 0 - } + if (component && value !== '0') { + let owner = component._currentElement._owner + let ownerName = owner ? owner.getName() : null + if (ownerName && !styleWarnings[ownerName]) { + styleWarnings[ownerName] = {} } } value = value.trim() diff --git a/src/parser.js b/src/parser.js index b84a164fd..027ddc6eb 100644 --- a/src/parser.js +++ b/src/parser.js @@ -55,8 +55,6 @@ export function parseCSS ( const styles = expandCSSFallbacks(prefixer(postcssJs.objectify(root))) - console.log(styles) - return { styles, staticCSSRules: vars === 0 && extractStatic @@ -80,11 +78,12 @@ export function expandCSSFallbacks (style: { [string]: any }) { let flattened = Object.keys(style).reduce((accum, key) => { if (Array.isArray(style[key])) { accum[key] = style[key].join(`; ${processStyleName(key)}: `) - } else if (Object.prototype.toString.call(style[key]) === '[object Object]') { - console.log('thought it was an object', key, style[key]) + } else if ( + Object.prototype.toString.call(style[key]) === '[object Object]' + ) { accum[key] = expandCSSFallbacks(style[key]) } else { - accum[key] = style[key]; + accum[key] = style[key] } return accum }, {}) diff --git a/src/react/index.js b/src/react/index.js index 9661ba65b..e52246cbf 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -72,7 +72,6 @@ export default function (tag, objs, vars = [], content) { let finalObjs = [] - if (tag.__emotion_spec) { push( finalObjs, @@ -80,10 +79,7 @@ export default function (tag, objs, vars = [], content) { tag.__emotion_spec, (accum, spec) => { push(accum, spec.objs) - push( - accum, - spec.content(map(spec.vars, getValue)) - ) + push(accum, spec.content.apply(null, map(spec.vars, getValue))) return accum }, [] @@ -94,7 +90,7 @@ export default function (tag, objs, vars = [], content) { push(finalObjs, objs) if (content) { - push(finalObjs, content(map(vars, getValue))) + push(finalObjs, content.apply(null, map(vars, getValue))) } if (mergedProps.className) { diff --git a/test/__snapshots__/css-prop.test.js.snap b/test/__snapshots__/css-prop.test.js.snap index 24b4dc62b..cdfe3037e 100644 --- a/test/__snapshots__/css-prop.test.js.snap +++ b/test/__snapshots__/css-prop.test.js.snap @@ -14,20 +14,20 @@ exports[`css prop react basic 1`] = ` `; exports[`css prop react kitchen sink 1`] = ` -.css-ggycvc { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; +.css-o2rzdt { + display: -webkit-box; + display: -ms-flexbox; + display: flex; font-weight: bold; - -webkit-justify-content: center; - -ms-flex-pack: center; -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; -webkit-box-align: center; + -ms-flex-align: center; align-items: center; } -.css-2sukuz { +.css-12lxyi1 { font-size: 6px; color: red; } @@ -36,25 +36,27 @@ exports[`css prop react kitchen sink 1`] = ` color: blue; } -.css-1gq9vfz { - display: -webkit-box,-moz-box,-ms-inline-flexbox,-webkit-inline-flex,inline-flex; +.css-1426hi0 { + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; } -.css-c5xqxn { +.css-btxk1p { color: red; border-radius: 5px; } -.css-c5xqxn:hover { +.css-btxk1p:hover { font-weight: bold; color: gray; } <div - className="css__legacy-stuff css-ggycvc" + className="css__legacy-stuff css-o2rzdt" > <h1 - className="css-2sukuz" + className="css-12lxyi1" > BOOM </h1> @@ -64,12 +66,12 @@ exports[`css prop react kitchen sink 1`] = ` Hello </p> <p - className="test_class1 test___class45 css-1gq9vfz" + className="test_class1 test___class45 css-1426hi0" > World </p> <p - className="css-c5xqxn" + className="css-btxk1p" > hello world </p> diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 922108a07..1f2dd20b0 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -61,7 +61,7 @@ exports[`css composes with undefined values 1`] = ` `; exports[`css handles more than 10 dynamic properties 1`] = ` -.css-n3p8vq { +.css-1tjas36 { -webkit-text-decoration: underline; text-decoration: underline; border-right: solid blue 54px; @@ -77,7 +77,7 @@ exports[`css handles more than 10 dynamic properties 1`] = ` } <div - className="css-n3p8vq" + className="css-1tjas36" /> `; diff --git a/test/__snapshots__/keyframes.test.js.snap b/test/__snapshots__/keyframes.test.js.snap index c05f3f1c5..1c8614065 100644 --- a/test/__snapshots__/keyframes.test.js.snap +++ b/test/__snapshots__/keyframes.test.js.snap @@ -1,28 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes keyframes with interpolation 1`] = ` -.css-1ufgv90 { - -webkit-animation: animation_k1mnnl 2s linear infinite; - animation: animation_k1mnnl 2s linear infinite; +.css-1nja0ph { + -webkit-animation: animation_1cnjdtk 2s linear infinite; + animation: animation_1cnjdtk 2s linear infinite; } <h1 - className="css-1ufgv90" + className="css-1nja0ph" > hello world </h1> `; -exports[`keyframes keyframes with interpolation 2`] = `"@-webkit-keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-xgcxry{-webkit-animation:animation_1w7bh5k 2s linear infinite;animation:animation_1w7bh5k 2s linear infinite;}@-webkit-keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}.css-1ufgv90{-webkit-animation:animation_k1mnnl 2s linear infinite;animation:animation_k1mnnl 2s linear infinite;}"`; +exports[`keyframes keyframes with interpolation 2`] = `"@-webkit-keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-1m1x9re{-webkit-animation:animation_7jdctn 2s linear infinite;animation:animation_7jdctn 2s linear infinite;}@-webkit-keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}.css-1nja0ph{-webkit-animation:animation_1cnjdtk 2s linear infinite;animation:animation_1cnjdtk 2s linear infinite;}"`; exports[`keyframes renders 1`] = ` -.css-xgcxry { - -webkit-animation: animation_1w7bh5k 2s linear infinite; - animation: animation_1w7bh5k 2s linear infinite; +.css-1m1x9re { + -webkit-animation: animation_7jdctn 2s linear infinite; + animation: animation_7jdctn 2s linear infinite; } <h1 - className="css-xgcxry" + className="css-1m1x9re" > hello world </h1> diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index a4daab801..42ca85bf2 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`styled basic render 1`] = ` -.css-vrq9pr { +.css-wwpoem { color: blue; font-size: 20px; } <h1 - className="css-vrq9pr" + className="css-wwpoem" > hello world </h1> @@ -38,7 +38,7 @@ exports[`styled call expression 1`] = ` `; exports[`styled component as selector 1`] = ` -.css-1ikz3g9 { +.css-1vj1sv9 { font-size: 20px; } @@ -53,7 +53,7 @@ exports[`styled component as selector 1`] = ` > hello <h1 - className="css-1ikz3g9" + className="css-1vj1sv9" > This will be green </h1> @@ -126,24 +126,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-1lyjzdi { +.css-1x2u9fm { font-size: 13.333333333333334px; } <h1 - className="legacy__class css-1lyjzdi" + className="legacy__class css-1x2u9fm" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-1lmko4z { +.css-6hsipw { font-size: 40px; } <h1 - className="legacy__class css-1lmko4z" + className="legacy__class css-6hsipw" scale={2} > hello world @@ -151,12 +151,24 @@ exports[`styled function in expression 1`] = ` `; exports[`styled handles more than 10 dynamic properties 1`] = ` -.css-1i41sa5 { - background: white,black,underline,block,3px,25px,500px,100,18px,center,solid 1px red; +.css-lkeub0 { + -webkit-text-decoration: underline; + text-decoration: underline; + border-right: solid blue 54px; + background: white; + color: black; + display: block; + border-radius: 3px; + padding: 25px; + width: 500px; + z-index: 100; + font-size: 18px; + text-align: center; + border-left: undefined; } <h1 - className="legacy__class css-1i41sa5" + className="legacy__class css-lkeub0" > hello world </h1> @@ -204,6 +216,10 @@ exports[`styled name 1`] = ` `; exports[`styled nested 1`] = ` +.css-1vj1sv9 { + font-size: 20px; +} + .css-1fir8qd { display: -webkit-box; display: -ms-flexbox; @@ -218,16 +234,12 @@ exports[`styled nested 1`] = ` color: red; } -.css-1ikz3g9 { - font-size: 20px; -} - <div className="css-1fir8qd" > hello <h1 - className="css-1ikz3g9" + className="css-1vj1sv9" > This will be green </h1> @@ -285,15 +297,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-1vectwv { +.css-93wdk { background-color: #ffd43b; - color: blue; + color: #8c81d8; height: 64px; font-size: 32px; } <span - className="css-1vectwv" + className="css-93wdk" scale={2} > hello world diff --git a/test/__snapshots__/server.test.js.snap b/test/__snapshots__/server.test.js.snap index 29121a411..4f5c996b9 100644 --- a/test/__snapshots__/server.test.js.snap +++ b/test/__snapshots__/server.test.js.snap @@ -2,21 +2,21 @@ exports[`extractCritical returns static css 1`] = ` Object { - "css": ".no-prefix {display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;-webkit-justify-content:center;-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;}.css-1666ym{display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;}.css-hp1yas{border-radius:50%;height:50px;width:50px;background-color:red;}", - "html": "<main class=\\"css-1666ym\\" data-reactroot=\\"\\" data-reactid=\\"1\\" data-react-checksum=\\"1172063568\\"><img size=\\"30\\" class=\\"css-hp1yas\\" data-reactid=\\"2\\"/><img size=\\"100\\" class=\\"css-hp1yas\\" data-reactid=\\"3\\"/><img class=\\"css-hp1yas\\" data-reactid=\\"4\\"/></main>", + "css": ".no-prefix {display:-webkit-box; display: -ms-flexbox; display: flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;}.css-71uu79{display:-webkit-box; display: -ms-flexbox; display: flex;}.css-l2qi8{border-radius:50%;height:50px;width:50px;background-color:red;}", + "html": "<main class=\\"css-71uu79\\" data-reactroot=\\"\\" data-reactid=\\"1\\" data-react-checksum=\\"912212839\\"><img size=\\"30\\" class=\\"css-l2qi8\\" data-reactid=\\"2\\"/><img size=\\"100\\" class=\\"css-l2qi8\\" data-reactid=\\"3\\"/><img class=\\"css-l2qi8\\" data-reactid=\\"4\\"/></main>", "ids": Array [ - "1666ym", - "hp1yas", + "71uu79", + "l2qi8", ], "rules": Array [ Object { - "cssText": ".no-prefix {display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;-webkit-justify-content:center;-ms-flex-pack:center;-webkit-box-pack:center;justify-content:center;}", + "cssText": ".no-prefix {display:-webkit-box; display: -ms-flexbox; display: flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;}", }, Object { - "cssText": ".css-1666ym{display:-webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex;}", + "cssText": ".css-71uu79{display:-webkit-box; display: -ms-flexbox; display: flex;}", }, Object { - "cssText": ".css-hp1yas{border-radius:50%;height:50px;width:50px;background-color:red;}", + "cssText": ".css-l2qi8{border-radius:50%;height:50px;width:50px;background-color:red;}", }, ], } diff --git a/test/babel/__snapshots__/css-prop.test.js.snap b/test/babel/__snapshots__/css-prop.test.js.snap index dce9524fd..1c58fc800 100644 --- a/test/babel/__snapshots__/css-prop.test.js.snap +++ b/test/babel/__snapshots__/css-prop.test.js.snap @@ -52,7 +52,7 @@ exports[`babel css prop css empty 1`] = ` exports[`babel css prop dynamic inline 1`] = ` "<div className={\\"a\\" + \\" \\" + css([], [color], function createEmotionStyledRules(x0) { return [{ - \\"color\\": x0 + \\"color\\": \`\${x0}\` }]; })}></div>;" `; diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index abf92698c..6709f38a5 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -15,9 +15,9 @@ const cls2 = css([['one-class', 'another-class', cls1]], ['center'], function cr 'WebkitBoxPack': 'center', 'msFlexPack': 'center', 'justifyContent': 'center', - 'WebkitBoxAlign': x0, - 'msFlexAlign': x0, - 'alignItems': x0 + 'WebkitBoxAlign': \`\${x0}\`, + 'msFlexAlign': \`\${x0}\`, + 'alignItems': \`\${x0}\` }]; });" `; @@ -133,9 +133,9 @@ const cls2 = css(['one-class', 'another-class', cls1], ['center'], function crea 'WebkitBoxPack': 'center', 'msFlexPack': 'center', 'justifyContent': 'center', - 'WebkitBoxAlign': x0, - 'msFlexAlign': x0, - 'alignItems': x0 + 'WebkitBoxAlign': \`\${x0}\`, + 'msFlexAlign': \`\${x0}\`, + 'alignItems': \`\${x0}\` }]; });" `; @@ -150,7 +150,7 @@ css([], [widthVar], function createEmotionStyledRules(x0) { \\"WebkitBoxFlex\\": \\"1\\", \\"msFlex\\": \\"1 0 auto\\", \\"flex\\": \\"1 0 auto\\", - \\"width\\": x0 + \\"width\\": \`\${x0}\` }]; });" `; @@ -161,7 +161,7 @@ const cls2 = css([], [className], function createEmotionStyledRules(x0) { return [{ \\"margin\\": \\"12px 48px\\", \\"color\\": \\"#ffffff\\", - [x0]: { + [\`\${x0}\`]: { \\"display\\": \\"none\\" } }]; @@ -175,9 +175,9 @@ const cls2 = css(['one-class', 'another-class', 'another-class', 'another-class' 'WebkitBoxPack': 'center', 'msFlexPack': 'center', 'justifyContent': 'center', - 'WebkitBoxAlign': x0, - 'msFlexAlign': x0, - 'alignItems': x0 + 'WebkitBoxAlign': \`\${x0}\`, + 'msFlexAlign': \`\${x0}\`, + 'alignItems': \`\${x0}\` }]; });" `; diff --git a/test/babel/__snapshots__/font-face.test.js.snap b/test/babel/__snapshots__/font-face.test.js.snap index a41226771..fe6ed37a1 100644 --- a/test/babel/__snapshots__/font-face.test.js.snap +++ b/test/babel/__snapshots__/font-face.test.js.snap @@ -32,7 +32,7 @@ exports[`fontFace babel extract interpolation 1`] = ` " fontFace([], [fontFamilyName], function createEmotionStyledRules(x0) { return [{ - \\"fontFamily\\": x0, + \\"fontFamily\\": \`\${x0}\`, \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", \\"fontWeight\\": \\"bold\\" }]; @@ -54,7 +54,7 @@ exports[`fontFace babel inline interpolation 1`] = ` " fontFace([], [fontFamilyName], function createEmotionStyledRules(x0) { return [{ - \\"fontFamily\\": x0, + \\"fontFamily\\": \`\${x0}\`, \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", \\"fontWeight\\": \\"bold\\" }]; diff --git a/test/babel/__snapshots__/inject-global.test.js.snap b/test/babel/__snapshots__/inject-global.test.js.snap index ecb65f763..fd7909a91 100644 --- a/test/babel/__snapshots__/inject-global.test.js.snap +++ b/test/babel/__snapshots__/inject-global.test.js.snap @@ -41,7 +41,7 @@ injectGlobal([], [display], function createEmotionStyledRules(x0) { \\"body\\": { \\"margin\\": \\"0\\", \\"padding\\": \\"0\\", - \\"display\\": x0 + \\"display\\": \`\${x0}\` }, \\"body > div\\": { \\"display\\": \\"none\\" @@ -78,7 +78,7 @@ injectGlobal([], [display], function createEmotionStyledRules(x0) { \\"body\\": { \\"margin\\": \\"0\\", \\"padding\\": \\"0\\", - \\"display\\": x0 + \\"display\\": \`\${x0}\` }, \\"body > div\\": { \\"display\\": \\"none\\" diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index 180694cb6..6d5d8257c 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -11,7 +11,7 @@ _css([], [widthVar], function createEmotionStyledRules(x0) { 'WebkitBoxFlex': '1', 'msFlex': '1 0 auto', 'flex': '1 0 auto', - 'width': x0 + 'width': \`\${x0}\` }]; });" `; @@ -114,7 +114,7 @@ const thing = _css([], [widthVar], function createEmotionStyledRules(x0) { 'WebkitBoxFlex': '1', 'msFlex': '1 0 auto', 'flex': '1 0 auto', - 'width': x0 + 'width': \`\${x0}\` }]; });" `; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 66f259c8e..ecbde4a1b 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -1,16 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`babel styled component dynamic fns 1`] = ` -"const Avatar = styled('img', [], [props => props.theme.borderRadius, props => props.theme.borderColor], function createEmotionStyledRules(x0, x1) { - return [{ - 'width': '96px', - 'height': '96px', - 'borderRadius': x0, - 'border': \`1px solid \${x1}\` - }]; -});" -`; - exports[`babel styled component extract basic 1`] = ` "import \\"./styled.test.emotion.css\\"; const H1 = styled(\\"h1\\", [\\"styled-H1-10x82eg\\"]);" @@ -56,7 +45,7 @@ exports[`babel styled component extract no use 2`] = `".styled-0 {}"`; exports[`babel styled component inline basic 1`] = ` "const H1 = styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [{ - 'fontSize': x0 + 'fontSize': \`\${x0}\` }]; });" `; @@ -71,7 +60,7 @@ const H1 = styled('h1', [props => { return props.a ? cssA : cssB; }], [fontSize + 'px', props => props.translateX], function createEmotionStyledRules(x0, x1) { return [{ - 'fontSize': x0, + 'fontSize': \`\${x0}\`, 'height': '20px', 'WebkitTransform': \`translateX(\${x1})\`, 'transform': \`translateX(\${x1})\` @@ -84,7 +73,7 @@ exports[`babel styled component inline dynamic fns 1`] = ` return [{ 'width': '96px', 'height': '96px', - 'borderRadius': x0, + 'borderRadius': \`\${x0}\`, 'border': \`1px solid \${x1}\` }]; });" @@ -93,7 +82,7 @@ exports[`babel styled component inline dynamic fns 1`] = ` exports[`babel styled component inline function call 1`] = ` "styled(MyComponent, [], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [{ - 'fontSize': x0 + 'fontSize': \`\${x0}\` }]; });" `; @@ -102,7 +91,7 @@ exports[`babel styled component inline interpolation in different places 1`] = ` " const H1 = styled('h1', [], [fontSize + 'px', props => props.translateX, something, something, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) { return [{ - 'fontSize': x0, + 'fontSize': \`\${x0}\`, 'height': '20px', 'WebkitTransform': \`translateX(\${x1}); -webkit-transform: translateX(\${x4}) translateY(\${x5})\`, 'transform': \`translateX(\${x1}); transform: translateX(\${x4}) translateY(\${x5})\`, @@ -139,6 +128,26 @@ exports[`babel styled component inline media query 1`] = ` });" `; +exports[`babel styled component inline more than 10 dynamic values 1`] = ` +"const H1 = styled('h1', [], ['underline', 54, 'white', 'black', 'block', '3px', '25px', '500px', 100, '18px', 'center', p => p.theme.blue], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) { + return [{ + 'WebkitTextDecoration': \`\${x0}\`, + 'textDecoration': \`\${x0}\`, + 'borderRight': \`solid blue \${x1}px\`, + 'background': \`\${x2}\`, + 'color': \`\${x3}\`, + 'display': \`\${x4}\`, + 'borderRadius': \`\${x5}\`, + 'padding': \`\${x6}\`, + 'width': \`\${x7}\`, + 'zIndex': \`\${x8}\`, + 'fontSize': \`\${x9}\`, + 'textAlign': \`\${x10}\`, + 'borderLeft': \`\${x11}\` + }]; +});" +`; + exports[`babel styled component inline name is correct with no identifier 1`] = ` " css([], [], function createEmotionStyledRules() { @@ -152,7 +161,7 @@ css([], [], function createEmotionStyledRules() { exports[`babel styled component inline nested 1`] = ` "const H1 = styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [{ - 'fontSize': x0, + 'fontSize': \`\${x0}\`, '& div': { 'color': 'blue' }, diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index 6d362448f..148865e72 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -8,23 +8,23 @@ jest.mock('fs') fs.existsSync.mockReturnValue(true) describe('babel styled component', () => { - // describe('inline', () => { - // test('no use', () => { - // const basic = 'styled.h1``' - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('no dynamic', () => { - // const basic = 'styled.h1`color:blue;`' - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // + describe('inline', () => { + test('no use', () => { + const basic = 'styled.h1``' + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('no dynamic', () => { + const basic = 'styled.h1`color:blue;`' + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + test('dynamic fns', () => { const basic = `const Avatar = styled('img')\` width: 96px; @@ -39,234 +39,254 @@ describe('babel styled component', () => { const { code } = babel.transform(basic, { plugins: [plugin] }) - console.log(code) + + expect(code).toMatchSnapshot() + }) + + test('more than 10 dynamic values', () => { + const basic = `const H1 = styled('h1')\` + text-decoration: $\{'underline'}; + border-right: solid blue $\{54}px; + background: $\{'white'}; + color: $\{'black'}; + display: $\{'block'}; + border-radius: $\{'3px'}; + padding: $\{'25px'}; + width: $\{'500px'}; + z-index: $\{100}; + font-size: $\{'18px'}; + text-align: $\{'center'}; + border-left: $\{p => p.theme.blue}; + \`` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + + expect(code).toMatchSnapshot() + }) + + test('styled component as selector', () => { + const basic = ` + const SomeComponent = styled.div\` \` + styled.h1\` + color:blue; + .\${SomeComponent} { + color: green; + } + \`` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('basic', () => { + const basic = "const H1 = styled.h1`font-size: ${fontSize + 'px'};`" + const { code } = babel.transform(basic, { + plugins: [plugin] + }) expect(code).toMatchSnapshot() }) - // - // - // - // test('styled component as selector', () => { - // const basic = ` - // const SomeComponent = styled.div\` \` - // styled.h1\` - // color:blue; - // .\${SomeComponent} { - // color: green; - // } - // \`` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('basic', () => { - // const basic = "const H1 = styled.h1`font-size: ${fontSize + 'px'};`" - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('nested', () => { - // const basic = - // 'const H1 = styled.h1`' + - // "font-size: ${fontSize + 'px'};" + - // '& div { color: blue;' + - // '& span { color: red } }' + - // '`' - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('interpolation in different places', () => { - // const basic = ` - // const H1 = styled.h1\` - // font-size: \${fontSize + 'px'}; - // height: 20px; - // transform: translateX(\${(props) => props.translateX}); - // height1: \${something}wow; - // width: w\${something}ow; - // transform: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); - // transform1: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); - // transform2: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}; - // \`` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('media query', () => { - // const basic = - // 'const H1 = styled.h1`@media print {' + - // ' font-size: 10pt' + - // '}' + - // '@media screen {' + - // ' .child-selector { font-size: 13px }' + - // '}' + - // '@media screen, print {' + - // ' &:hover + & { line-height: 1.2 }' + - // '}' + - // '@media only screen ' + - // ' and (min-device-width: 320px) ' + - // ' and (max-device-width: 480px)' + - // ' and (-webkit-min-device-pixel-ratio: 2) {' + - // ' .child-selector { line-height: 1.4 }' + - // '}`' - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('function call', () => { - // const basic = "styled(MyComponent)`font-size: ${fontSize + 'px'};`" - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('name is correct with no identifier', () => { - // const basic = ` - // css\` - // margin: 12px 48px; - // color: #ffffff; - // \` - // ` - // const { code } = babel.transform(basic, { - // plugins: [[plugin]] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('objects fn call', () => { - // const basic = ` - // const H1 = styled('h1')({ - // display: 'flex' - // })` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('objects based on props', () => { - // const basic = ` - // const H1 = styled('h1')({ padding: 10 },props => ({ - // display: props.display - // }))` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('object composes with classes', () => { - // const basic = ` - // const H1 = styled('h1')('some-class',props => ({ - // display: props.display - // }))` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('objects prefixed', () => { - // const basic = ` - // const H1 = styled('h1')({ - // borderRadius: '50%', - // transition: 'transform 400ms ease-in-out', - // boxSizing: 'border-box', - // display: 'flex', - // ':hover': { - // transform: 'scale(1.2)' - // } - // }, props => { - // padding: props.padding - // })` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('styled. objects', () => { - // const basic = ` - // const H1 = styled.h1({ padding: 10 },props => ({ - // display: props.display - // }))` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('styled objects prefixed', () => { - // const basic = ` - // const H1 = styled.h1({ - // borderRadius: '50%', - // transition: 'transform 400ms ease-in-out', - // boxSizing: 'border-box', - // display: 'flex', - // ':hover': { - // transform: 'scale(1.2)' - // } - // },props => ({ - // display: props.display - // }))` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // - // test('composes based on props', () => { - // const basic = `const cls1 = css\` width: 20px; \` - // const H1 = styled.h1\` - // composes: $\{props => { - // return props.a ? cssA : cssB - // }}; - // font-size: \${fontSize + 'px'}; - // height: 20px; - // transform: translateX(\${(props) => props.translateX}); - // \`` - // const { code } = babel.transform(basic, { - // plugins: [plugin] - // }) - // expect(code).toMatchSnapshot() - // }) - // }) - // describe('extract', () => { - // test('no use', () => { - // const basic = 'styled.h1``' - // const { code } = babel.transform(basic, { - // plugins: [[plugin, { extractStatic: true }]], - // filename: __filename, - // babelrc: false - // }) - // expect(code).toMatchSnapshot() - // expect(fs.writeFileSync).toHaveBeenCalledTimes(1) - // expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() - // }) - // - // test('basic', () => { - // const basic = - // "const H1 = styled.h1`display: flex; justify-content: center; width: var(--css-hash-0); &:hover { background-color: green; } @media (max-width: 500px) { height: var(--css-hash-1); position: fixed; } @media print { display: none; } &::before { color: blue; width: 20px; height: 20px; content: 'pseudo' }`" - // const { code } = babel.transform(basic, { - // plugins: [[plugin, { extractStatic: true }]], - // filename: __filename, - // babelrc: false - // }) - // - // expect(code).toMatchSnapshot() - // expect(fs.writeFileSync).toHaveBeenCalledTimes(2) - // expect(fs.writeFileSync.mock.calls[1][1]).toMatchSnapshot() - // }) - // }) + test('nested', () => { + const basic = + 'const H1 = styled.h1`' + + "font-size: ${fontSize + 'px'};" + + '& div { color: blue;' + + '& span { color: red } }' + + '`' + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('interpolation in different places', () => { + const basic = ` + const H1 = styled.h1\` + font-size: \${fontSize + 'px'}; + height: 20px; + transform: translateX(\${(props) => props.translateX}); + height1: \${something}wow; + width: w\${something}ow; + transform: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + transform1: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + transform2: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}; + \`` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('media query', () => { + const basic = + 'const H1 = styled.h1`@media print {' + + ' font-size: 10pt' + + '}' + + '@media screen {' + + ' .child-selector { font-size: 13px }' + + '}' + + '@media screen, print {' + + ' &:hover + & { line-height: 1.2 }' + + '}' + + '@media only screen ' + + ' and (min-device-width: 320px) ' + + ' and (max-device-width: 480px)' + + ' and (-webkit-min-device-pixel-ratio: 2) {' + + ' .child-selector { line-height: 1.4 }' + + '}`' + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('function call', () => { + const basic = "styled(MyComponent)`font-size: ${fontSize + 'px'};`" + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('name is correct with no identifier', () => { + const basic = ` + css\` + margin: 12px 48px; + color: #ffffff; + \` + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) + + test('objects fn call', () => { + const basic = ` + const H1 = styled('h1')({ + display: 'flex' + })` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('objects based on props', () => { + const basic = ` + const H1 = styled('h1')({ padding: 10 },props => ({ + display: props.display + }))` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('object composes with classes', () => { + const basic = ` + const H1 = styled('h1')('some-class',props => ({ + display: props.display + }))` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('objects prefixed', () => { + const basic = ` + const H1 = styled('h1')({ + borderRadius: '50%', + transition: 'transform 400ms ease-in-out', + boxSizing: 'border-box', + display: 'flex', + ':hover': { + transform: 'scale(1.2)' + } + }, props => { + padding: props.padding + })` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('styled. objects', () => { + const basic = ` + const H1 = styled.h1({ padding: 10 },props => ({ + display: props.display + }))` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('styled objects prefixed', () => { + const basic = ` + const H1 = styled.h1({ + borderRadius: '50%', + transition: 'transform 400ms ease-in-out', + boxSizing: 'border-box', + display: 'flex', + ':hover': { + transform: 'scale(1.2)' + } + },props => ({ + display: props.display + }))` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + + test('composes based on props', () => { + const basic = `const cls1 = css\` width: 20px; \` + const H1 = styled.h1\` + composes: $\{props => { + return props.a ? cssA : cssB + }}; + font-size: \${fontSize + 'px'}; + height: 20px; + transform: translateX(\${(props) => props.translateX}); + \`` + const { code } = babel.transform(basic, { + plugins: [plugin] + }) + expect(code).toMatchSnapshot() + }) + }) + + describe('extract', () => { + test('no use', () => { + const basic = 'styled.h1``' + const { code } = babel.transform(basic, { + plugins: [[plugin, { extractStatic: true }]], + filename: __filename, + babelrc: false + }) + expect(code).toMatchSnapshot() + expect(fs.writeFileSync).toHaveBeenCalledTimes(1) + expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() + }) + + test('basic', () => { + const basic = + "const H1 = styled.h1`display: flex; justify-content: center; width: var(--css-hash-0); &:hover { background-color: green; } @media (max-width: 500px) { height: var(--css-hash-1); position: fixed; } @media print { display: none; } &::before { color: blue; width: 20px; height: 20px; content: 'pseudo' }`" + const { code } = babel.transform(basic, { + plugins: [[plugin, { extractStatic: true }]], + filename: __filename, + babelrc: false + }) + + expect(code).toMatchSnapshot() + expect(fs.writeFileSync).toHaveBeenCalledTimes(2) + expect(fs.writeFileSync.mock.calls[1][1]).toMatchSnapshot() + }) + }) }) diff --git a/test/macro/__snapshots__/css.test.js.snap b/test/macro/__snapshots__/css.test.js.snap index 77bebdf75..9a3c7cf66 100644 --- a/test/macro/__snapshots__/css.test.js.snap +++ b/test/macro/__snapshots__/css.test.js.snap @@ -1,67 +1,67 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css macro composes 1`] = ` -.css-1cjz2s1 { - display: -webkit-box,-moz-box,-ms-flexbox,-webkit-flex,flex; - -webkit-justify-content: center; - -ms-flex-pack: center; +.css-w9g11r { + display: -webkit-box; + display: -ms-flexbox; + display: flex; -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; } <div - className="css-1cjz2s1" + className="css-w9g11r" /> `; exports[`css macro composes with objects 1`] = ` -.css-1vx1y9j { +.css-d6v2z3 { display: flex,block; width: 30px; height: calc(40vw - 50px); - -webkit-justify-content: center; - -ms-flex-pack: center; -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; } -.css-1vx1y9j:hover { +.css-d6v2z3:hover { color: blue; } -.css-1vx1y9j:after { +.css-d6v2z3:after { content: " "; color: red; } @media (min-width: 420px) { - .css-1vx1y9j { + .css-d6v2z3 { color: green; } } <div - className="css-1vx1y9j" + className="css-d6v2z3" /> `; exports[`css macro composes with undefined values 1`] = ` -.css-1rsyrbq { - -webkit-justify-content: center; - -ms-flex-pack: center; +.css-awzhlo { -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; } <div - className="css-1rsyrbq" + className="css-awzhlo" /> `; exports[`css macro handles more than 10 dynamic properties 1`] = ` -.css-550542 { +.css-1fz3d5g { background: white; color: black; + -webkit-text-decoration: underline; text-decoration: underline; display: block; border-radius: 3px; @@ -74,7 +74,7 @@ exports[`css macro handles more than 10 dynamic properties 1`] = ` } <div - className="css-550542" + className="css-1fz3d5g" /> `; diff --git a/test/macro/__snapshots__/keyframes.test.js.snap b/test/macro/__snapshots__/keyframes.test.js.snap index c61672940..58cd914bf 100644 --- a/test/macro/__snapshots__/keyframes.test.js.snap +++ b/test/macro/__snapshots__/keyframes.test.js.snap @@ -1,28 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes - macro keyframes with interpolation 1`] = ` -.css-1ufgv90 { - -webkit-animation: animation_k1mnnl 2s linear infinite; - animation: animation_k1mnnl 2s linear infinite; +.css-1nja0ph { + -webkit-animation: animation_1cnjdtk 2s linear infinite; + animation: animation_1cnjdtk 2s linear infinite; } <h1 - className="css-1ufgv90" + className="css-1nja0ph" > hello world </h1> `; -exports[`keyframes - macro keyframes with interpolation 2`] = `"@-webkit-keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_1w7bh5k{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);-ms-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);-ms-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);-ms-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-xgcxry{-webkit-animation:animation_1w7bh5k 2s linear infinite;animation:animation_1w7bh5k 2s linear infinite;}@-webkit-keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_k1mnnl{from {-webkit-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg);}}.css-1ufgv90{-webkit-animation:animation_k1mnnl 2s linear infinite;animation:animation_k1mnnl 2s linear infinite;}"`; +exports[`keyframes - macro keyframes with interpolation 2`] = `"@-webkit-keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-1m1x9re{-webkit-animation:animation_7jdctn 2s linear infinite;animation:animation_7jdctn 2s linear infinite;}@-webkit-keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}.css-1nja0ph{-webkit-animation:animation_1cnjdtk 2s linear infinite;animation:animation_1cnjdtk 2s linear infinite;}"`; exports[`keyframes - macro renders 1`] = ` -.css-xgcxry { - -webkit-animation: animation_1w7bh5k 2s linear infinite; - animation: animation_1w7bh5k 2s linear infinite; +.css-1m1x9re { + -webkit-animation: animation_7jdctn 2s linear infinite; + animation: animation_7jdctn 2s linear infinite; } <h1 - className="css-xgcxry" + className="css-1m1x9re" > hello world </h1> diff --git a/test/macro/__snapshots__/react.test.js.snap b/test/macro/__snapshots__/react.test.js.snap index f26447a58..fcd789e25 100644 --- a/test/macro/__snapshots__/react.test.js.snap +++ b/test/macro/__snapshots__/react.test.js.snap @@ -37,20 +37,22 @@ exports[`styled call expression 1`] = ` `; exports[`styled component as selector 1`] = ` -.css-1d77tm { - display: -webkit-box,-ms-flexbox,flex; +.css-1vj1sv9 { + font-size: 20px; } -.css-1ikz3g9 { - font-size: 20px; +.css-qrvryj { + display: -webkit-box; + display: -ms-flexbox; + display: flex; } <div - className="css-1d77tm" + className="css-qrvryj" > hello <h1 - className="css-1ikz3g9" + className="css-1vj1sv9" > This will be green </h1> @@ -125,24 +127,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-1lyjzdi { +.css-1x2u9fm { font-size: 13.333333333333334px; } <h1 - className="css-1lyjzdi" + className="css-1x2u9fm" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-1j40edd { +.css-1iqzzhq { font-size: 40px; } <h1 - className="css-1j40edd" + className="css-1iqzzhq" scale={2} > hello world @@ -216,15 +218,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-1vectwv { +.css-93wdk { background-color: #ffd43b; - color: blue; + color: #8c81d8; height: 64px; font-size: 32px; } <span - className="css-1vectwv" + className="css-93wdk" scale={2} > hello world diff --git a/vue/index.js b/vue/index.js deleted file mode 100644 index 9b0c265eb..000000000 --- a/vue/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../lib/vue') diff --git a/vue/macro.js b/vue/macro.js deleted file mode 100644 index b6de0934d..000000000 --- a/vue/macro.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../lib/vue/macro') From 08ce19ba3c924e29d9cc1babedff666b56eb09c7 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Sun, 23 Jul 2017 19:29:30 +1000 Subject: [PATCH 38/57] Formatting, remove unused stuff, remove useless regex escapes and fix typo --- src/index.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/index.js b/src/index.js index ca9404ef1..e7065fa1c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ // @flow import { StyleSheet } from './sheet' import { forEach, map, reduce } from './utils' -import { hashString as hash, hashArray, hashObject } from './hash' +import { hashString as hash, hashObject } from './hash' import { createMarkupForStyles } from './glamor/CSSPropertyOperations' import clean from './glamor/clean.js' @@ -213,25 +213,20 @@ function _css (rules) { return toRule(spec) } -// takes a string, converts to lowercase, strips out nonalphanumeric. -function simple (str) { - return str.toLowerCase().replace(/[^a-z0-9]/g, '') -} - // of shape { 'data-css-<id>': '' } export function isLikeRule (rule: EmotionRule) { let keys = Object.keys(rule).filter(x => x !== 'toString') if (keys.length !== 1) { return false } - return !!/css\-([a-zA-Z0-9]+)/.exec(keys[0]) + return !!/css-([a-zA-Z0-9]+)/.exec(keys[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 regex = /css-([a-zA-Z0-9]+)/ let match = regex.exec(keys[0]) if (!match) throw new Error('not a rule') return match[1] @@ -239,7 +234,7 @@ export function idFor (rule: EmotionRule) { function selector (id: string, path: string = '') { if (!id) { - return path.replace(/\&/g, '') + return path.replace(/&/g, '') } if (!path) return `.css-${id}` @@ -247,7 +242,7 @@ function selector (id: string, path: string = '') { .split(',') .map( x => - x.indexOf('&') >= 0 ? x.replace(/\&/gm, `.css-${id}`) : `.css-${id}${x}` + x.indexOf('&') >= 0 ? x.replace(/&/gm, `.css-${id}`) : `.css-${id}${x}` ) .join(',') @@ -333,9 +328,9 @@ function toRule (spec) { } function isSelector (key) { - let possibles = [':', '.', '[', '>', ' '], - found = false, - ch = key.charAt(0) + const possibles = [':', '.', '[', '>', ' '] + let found = false + const ch = key.charAt(0) for (let i = 0; i < possibles.length; i++) { if (ch === possibles[i]) { found = true @@ -350,7 +345,7 @@ function joinSelectors (a, b) { let bs = b.split(',').map(b => (!(b.indexOf('&') >= 0) ? '&' + b : b)) return bs - .reduce((arr, b) => arr.concat(as.map(a => b.replace(/\&/g, a))), []) + .reduce((arr, b) => arr.concat(as.map(a => b.replace(/&/g, a))), []) .join(',') } @@ -443,7 +438,7 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { src: _src[key] }) } else if (key === 'composes') { - // ignore, we already dealth with it + // ignore, we already dealt with it } else { let _dest = dest if (supp) { From f391862580abb040bc70fc246ac4b9334425c4d2 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Sun, 23 Jul 2017 20:08:02 +1000 Subject: [PATCH 39/57] Add tests for ::placeholder --- test/__snapshots__/react.test.js.snap | 52 +++++++++++++++++++++++++++ test/react.test.js | 28 ++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 42ca85bf2..eb4619783 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -202,6 +202,58 @@ exports[`styled innerRef 1`] = ` </h1> `; +exports[`styled input placeholder 1`] = ` +.css-1uwopro::-webkit-input-placeholder { + background-color: green; +} + +.css-1uwopro:-ms-input-placeholder { + background-color: green; +} + +.css-1uwopro::-moz-placeholder { + background-color: green; +} + +.css-1uwopro::-ms-input-placeholder { + background-color: green; +} + +.css-1uwopro::placeholder { + background-color: green; +} + +<input + className="css-1uwopro" +> + hello world +</input> +`; + +exports[`styled input placeholder object 1`] = ` +.css-tkwref::-webkit-input-placeholder { + background-color: green; +} + +.css-tkwref::-moz-placeholder { + background-color: green; +} + +.css-tkwref::-ms-input-placeholder { + background-color: green; +} + +.css-tkwref::placeholder { + background-color: green; +} + +<input + className="css-tkwref" +> + hello world +</input> +`; + exports[`styled name 1`] = ` .css-1i0qtj6 { name: FancyH1; diff --git a/test/react.test.js b/test/react.test.js index 6903188b4..1d82bf782 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -107,6 +107,33 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithEmotion() }) + test('input placeholder', () => { + const Input = styled.input` + ::placeholder { + background-color: green; + } + ` + const tree = renderer + .create(<Input>hello world</Input>) + .toJSON() + + expect(tree).toMatchSnapshotWithEmotion() + }) + + test('input placeholder object', () => { + const Input = styled('input')({ + '::placeholder': { + backgroundColor: 'green' + } + }) + + const tree = renderer + .create(<Input>hello world</Input>) + .toJSON() + + expect(tree).toMatchSnapshotWithEmotion() + }) + test('object composition', () => { const imageStyles = css({ width: 96, @@ -221,7 +248,6 @@ describe('styled', () => { }) test('composes', () => { - debugger const fontSize = '20px' const cssA = css` From 975a38334b7bf7deb100725b11a199f92d7ba1f4 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Sun, 23 Jul 2017 21:25:25 +1000 Subject: [PATCH 40/57] Add rollup-plugin-replace --- package.json | 1 + rollup.config.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/package.json b/package.json index 0f1599607..f6ac0aefa 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "rollup-plugin-babel": "^2.7.1", "rollup-plugin-commonjs": "^8.0.2", "rollup-plugin-node-resolve": "^3.0.0", + "rollup-plugin-replace": "^1.1.1", "rollup-plugin-uglify": "^2.0.1", "standard": "^10.0.2", "vue": "^2.3.4" diff --git a/rollup.config.js b/rollup.config.js index ab2bf60dc..013d59d81 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,6 +2,7 @@ import resolve from 'rollup-plugin-node-resolve' import commonjs from 'rollup-plugin-commonjs' import babel from 'rollup-plugin-babel' import uglify from 'rollup-plugin-uglify' +import replace from 'rollup-plugin-replace' import pkg from './package.json' export default { @@ -30,6 +31,9 @@ export default { ignoreGlobal: true, include: 'node_modules/**' }), + replace({ + 'process.env.NODE_ENV': JSON.stringify('production') + }), uglify() ], targets: [ From b063ff612293517dd4b454695cfbc1a8065401de Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 09:51:06 +1000 Subject: [PATCH 41/57] Remove stuff for ::placeholder --- src/index.js | 21 ------------------ test/__snapshots__/react.test.js.snap | 32 +++++---------------------- 2 files changed, 6 insertions(+), 47 deletions(-) diff --git a/src/index.js b/src/index.js index e7065fa1c..95edfc4db 100644 --- a/src/index.js +++ b/src/index.js @@ -396,27 +396,6 @@ function build (dest, { selector = '', mq = '', supp = '', src = {} }) { } Object.keys(_src || {}).forEach(key => { if (isSelector(key)) { - if (key === '::placeholder') { - build(dest, { - selector: joinSelectors(selector, '::-webkit-input-placeholder'), - mq, - supp, - src: _src[key] - }) - build(dest, { - selector: joinSelectors(selector, '::-moz-placeholder'), - mq, - supp, - src: _src[key] - }) - build(dest, { - selector: joinSelectors(selector, '::-ms-input-placeholder'), - mq, - supp, - src: _src[key] - }) - } - build(dest, { selector: joinSelectors(selector, key), mq, diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index eb4619783..b171de674 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -203,52 +203,32 @@ exports[`styled innerRef 1`] = ` `; exports[`styled input placeholder 1`] = ` -.css-1uwopro::-webkit-input-placeholder { +.css-wmofgo::-webkit-input-placeholder { background-color: green; } -.css-1uwopro:-ms-input-placeholder { +.css-wmofgo:-ms-input-placeholder { background-color: green; } -.css-1uwopro::-moz-placeholder { - background-color: green; -} - -.css-1uwopro::-ms-input-placeholder { - background-color: green; -} - -.css-1uwopro::placeholder { +.css-wmofgo::placeholder { background-color: green; } <input - className="css-1uwopro" + className="css-wmofgo" > hello world </input> `; exports[`styled input placeholder object 1`] = ` -.css-tkwref::-webkit-input-placeholder { - background-color: green; -} - -.css-tkwref::-moz-placeholder { - background-color: green; -} - -.css-tkwref::-ms-input-placeholder { - background-color: green; -} - -.css-tkwref::placeholder { +.css-1u5gkp5::placeholder { background-color: green; } <input - className="css-tkwref" + className="css-1u5gkp5" > hello world </input> From 3f6ef03ded971c0273238db81e62af14a7e4a0c2 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 11:16:33 +1000 Subject: [PATCH 42/57] Use jest-glamor-react instead of custom jest-utils --- jest-utils/index.js | 7 -- jest-utils/matcher.js | 81 -------------- jest-utils/serializer.js | 95 ---------------- package.json | 3 +- test/__snapshots__/css-prop.test.js.snap | 30 ++--- test/__snapshots__/css.test.js.snap | 26 ++--- test/__snapshots__/keyframes.test.js.snap | 8 +- test/__snapshots__/react.test.js.snap | 104 +++++++++--------- test/babel/__snapshots__/css.test.js.snap | 10 ++ test/babel/css.test.js | 12 ++ test/css-prop.test.js | 12 +- test/css.test.js | 14 +-- test/extract/extract.test.js | 12 +- test/keyframes.test.js | 11 +- test/macro/__snapshots__/css.test.js.snap | 26 ++--- .../__snapshots__/keyframes.test.js.snap | 8 +- test/macro/__snapshots__/react.test.js.snap | 70 ++++++------ test/macro/css.test.js | 14 +-- test/macro/keyframes.test.js | 8 +- test/macro/react.test.js | 41 +++---- test/react.test.js | 52 ++++----- 21 files changed, 240 insertions(+), 404 deletions(-) delete mode 100644 jest-utils/index.js delete mode 100644 jest-utils/matcher.js delete mode 100644 jest-utils/serializer.js diff --git a/jest-utils/index.js b/jest-utils/index.js deleted file mode 100644 index 82590fb4a..000000000 --- a/jest-utils/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const matcher = require('./matcher') -const serializer = require('./serializer') - -module.exports = { - matcher, - serializer -} diff --git a/jest-utils/matcher.js b/jest-utils/matcher.js deleted file mode 100644 index 80f769045..000000000 --- a/jest-utils/matcher.js +++ /dev/null @@ -1,81 +0,0 @@ -const chalk = require('chalk') -const diff = require('jest-diff') -const stripAnsi = require('strip-ansi') -const { toMatchSnapshot } = require('jest-snapshot') - -const isAddition = line => /^\+/.test(line) - -const isDeletion = line => /^-/.test(line) - -const removeGlamorClassNames = line => - line.replace(/css-.+?( |")/, '').slice(1).trim() -const isClassNameAttribute = ( - line, - previous = '', - /* istanbul ignore next */ next = '' -) => { - const containsGlamorClassName = /class(Name)?=".*css-.*"/.test(line) - if (containsGlamorClassName) { - // just because the line contains a glamor class name doesn't mean that - // it doesn't also have some other change, so we'll remove everything - // except the glamor class name and see if it's the same as the previous - // or the next line. It's not really perfect, but it's less likely that - // we'll show something in white that shouldn't be :) - const lineRemoved = removeGlamorClassNames(line) - const previousRemoved = removeGlamorClassNames(previous) - const nextRemoved = removeGlamorClassNames(next) - return lineRemoved === previousRemoved || lineRemoved === nextRemoved - } - return containsGlamorClassName -} -const isDataAttribute = line => /data-css-.*/.test(line) -const isClassNameSelector = line => /\.css-.*,/.test(line) -const isDataSelector = line => /\[data-css-.*\] {/.test(line) - -const isClassName = (line, previous, next) => - (isAddition(line) || isDeletion(line)) && - (isClassNameAttribute(line, previous, next) || - isDataAttribute(line) || - isClassNameSelector(line) || - isDataSelector(line)) - -const colorize = message => { - const messageLines = message.split('\n') - - return messageLines - .map((line, index) => { - const previous = messageLines[index - 1] - const next = messageLines[index + 1] - if (isClassName(line, previous, next)) { - return chalk.white(line) - } - if (isAddition(line)) { - return chalk.red(line) - } - if (isDeletion(line)) { - return chalk.green(line) - } - return chalk.dim(line) - }) - .join('\n') -} - -const matcher = { - toMatchSnapshotWithEmotion (...args) { - const result = toMatchSnapshot.apply(this, args) - let message - - if (!result.pass) { - message = diff(result.expected, result.actual, { - aAnnotation: 'Snapshot', - bAnnotation: 'Received' - }) - message = stripAnsi(message) - message = colorize(message) - } - - return { pass: result.pass, message } - } -} - -module.exports = matcher diff --git a/jest-utils/serializer.js b/jest-utils/serializer.js deleted file mode 100644 index 6675fa9ac..000000000 --- a/jest-utils/serializer.js +++ /dev/null @@ -1,95 +0,0 @@ -const css = require('css') -const { sheet } = require('../src/index') - -const serializer = { test, print } - -module.exports = serializer - -function test (val) { - return ( - val && !val.withStyles && val.$$typeof === Symbol.for('react.test.json') - ) -} - -function print (val, printer) { - const selectors = getSelectors(val) - const styles = getStyles(selectors) - val.withStyles = true - const printedVal = printer(val) - if (styles) { - return `${styles}\n\n${printedVal}` - } else { - return printedVal - } -} - -function getSelectors (node) { - let selectors = [] - if (node.children && node.children.reduce) { - selectors = node.children.reduce( - (acc, child) => acc.concat(getSelectors(child)), - [] - ) - } - if (node.props) { - return getSelectorsFromProps(selectors, node.props) - } - return selectors -} - -function getSelectorsFromProps (selectors, props) { - const className = props.className || props.class - if (className) { - selectors = selectors.concat( - className.toString().split(' ').map(cn => `.${cn}`) - ) - } - const dataProps = Object.keys(props).reduce((dProps, key) => { - if (key.startsWith('data-')) { - dProps.push(`[${key}]`) - } - return dProps - }, []) - if (dataProps.length) { - selectors = selectors.concat(dataProps) - } - return selectors -} - -function getStyles (nodeSelectors) { - const styles = sheet.tags - .map(tag => /* istanbul ignore next */ tag.textContent || '') - .join('\n') - const ast = css.parse(styles) - const rules = ast.stylesheet.rules.filter(filter) - const mediaQueries = getMediaQueries(ast, filter) - - ast.stylesheet.rules = [...rules, ...mediaQueries] - - const ret = css.stringify(ast) - return ret - - function filter (rule) { - if (rule.type === 'rule') { - return rule.selectors.some(selector => { - const baseSelector = selector.split(/:| /)[0] - return nodeSelectors.includes(baseSelector) - }) - } - return false - } -} - -function getMediaQueries (ast, filter) { - return ast.stylesheet.rules - .filter(rule => rule.type === 'media') - .reduce((acc, mediaQuery) => { - mediaQuery.rules = mediaQuery.rules.filter(filter) - - if (mediaQuery.rules.length) { - return acc.concat(mediaQuery) - } - - return acc - }, []) -} diff --git a/package.json b/package.json index f6ac0aefa..36408feb8 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,7 @@ "caniuse-api": "^2.0.0", "jest": "^20.0.4", "jest-cli": "^20.0.4", - "jest-emotion-react": "^0.0.2", - "jest-glamor-react": "^1.4.0", + "jest-glamor-react": "^2.0.0", "npm-run-all": "^4.0.2", "polished": "^1.2.1", "prettier-eslint-cli": "^4.0.3", diff --git a/test/__snapshots__/css-prop.test.js.snap b/test/__snapshots__/css-prop.test.js.snap index cdfe3037e..5b5d16412 100644 --- a/test/__snapshots__/css-prop.test.js.snap +++ b/test/__snapshots__/css-prop.test.js.snap @@ -1,20 +1,20 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css prop react basic 1`] = ` -.css-xs9rh7 { +.glamor-0 { color: red; font-size: 1px; } <p - className="css-xs9rh7" + className="glamor-0" > hello world </p> `; exports[`css prop react kitchen sink 1`] = ` -.css-o2rzdt { +.glamor-4 { display: -webkit-box; display: -ms-flexbox; display: flex; @@ -27,51 +27,51 @@ exports[`css prop react kitchen sink 1`] = ` align-items: center; } -.css-12lxyi1 { +.glamor-0 { font-size: 6px; color: red; } -.css-bithoy { +.glamor-1 { color: blue; } -.css-1426hi0 { +.glamor-2 { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; } -.css-btxk1p { +.glamor-3 { color: red; border-radius: 5px; } -.css-btxk1p:hover { +.glamor-3:hover { font-weight: bold; color: gray; } <div - className="css__legacy-stuff css-o2rzdt" + className="css__legacy-stuff glamor-4" > <h1 - className="css-12lxyi1" + className="glamor-0" > BOOM </h1> <p - className="test_class1 css-bithoy" + className="test_class1 glamor-1" > Hello </p> <p - className="test_class1 test___class45 css-1426hi0" + className="test_class1 test___class45 glamor-2" > World </p> <p - className="css-btxk1p" + className="glamor-3" > hello world </p> @@ -79,14 +79,14 @@ exports[`css prop react kitchen sink 1`] = ` `; exports[`css prop react string expression 1`] = ` -.css-1ylcmay { +.glamor-0 { color: red; background: blue; font-size: 48px; } <p - className="css-1ylcmay" + className="glamor-0" > hello world </p> diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 1f2dd20b0..57742b36b 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css composes 1`] = ` -.css-w9g11r { +.glamor-0 { display: -webkit-box; display: -ms-flexbox; display: flex; @@ -11,12 +11,12 @@ exports[`css composes 1`] = ` } <div - className="css-w9g11r" + className="glamor-0" /> `; exports[`css composes with objects 1`] = ` -.css-17yqurf { +.glamor-0 { display: -webkit-box; display: -ms-flexbox; display: flex; @@ -28,40 +28,40 @@ exports[`css composes with objects 1`] = ` justify-content: center; } -.css-17yqurf:hover { +.glamor-0:hover { color: blue; } -.css-17yqurf:after { +.glamor-0:after { content: " "; color: red; } @media (min-width: 420px) { - .css-17yqurf { + .glamor-0 { color: green; } } <div - className="css-17yqurf" + className="glamor-0" /> `; exports[`css composes with undefined values 1`] = ` -.css-awzhlo { +.glamor-0 { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } <div - className="css-awzhlo" + className="glamor-0" /> `; exports[`css handles more than 10 dynamic properties 1`] = ` -.css-1tjas36 { +.glamor-0 { -webkit-text-decoration: underline; text-decoration: underline; border-right: solid blue 54px; @@ -77,18 +77,18 @@ exports[`css handles more than 10 dynamic properties 1`] = ` } <div - className="css-1tjas36" + className="glamor-0" /> `; exports[`css handles objects 1`] = ` -.css-71uu79 { +.glamor-0 { display: -webkit-box; display: -ms-flexbox; display: flex; } <div - className="css-71uu79" + className="glamor-0" /> `; diff --git a/test/__snapshots__/keyframes.test.js.snap b/test/__snapshots__/keyframes.test.js.snap index 1c8614065..db944dbab 100644 --- a/test/__snapshots__/keyframes.test.js.snap +++ b/test/__snapshots__/keyframes.test.js.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes keyframes with interpolation 1`] = ` -.css-1nja0ph { +.glamor-0 { -webkit-animation: animation_1cnjdtk 2s linear infinite; animation: animation_1cnjdtk 2s linear infinite; } <h1 - className="css-1nja0ph" + className="glamor-0" > hello world </h1> @@ -16,13 +16,13 @@ exports[`keyframes keyframes with interpolation 1`] = ` exports[`keyframes keyframes with interpolation 2`] = `"@-webkit-keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-1m1x9re{-webkit-animation:animation_7jdctn 2s linear infinite;animation:animation_7jdctn 2s linear infinite;}@-webkit-keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}.css-1nja0ph{-webkit-animation:animation_1cnjdtk 2s linear infinite;animation:animation_1cnjdtk 2s linear infinite;}"`; exports[`keyframes renders 1`] = ` -.css-1m1x9re { +.glamor-0 { -webkit-animation: animation_7jdctn 2s linear infinite; animation: animation_7jdctn 2s linear infinite; } <h1 - className="css-1m1x9re" + className="glamor-0" > hello world </h1> diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index b171de674..6880587bf 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -1,59 +1,59 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`styled basic render 1`] = ` -.css-wwpoem { +.glamor-0 { color: blue; font-size: 20px; } <h1 - className="css-wwpoem" + className="glamor-0" > hello world </h1> `; exports[`styled basic render with object as style 1`] = ` -.css-kp2uw9 { +.glamor-0 { font-size: 20px; } <h1 - className="css-kp2uw9" + className="glamor-0" > hello world </h1> `; exports[`styled call expression 1`] = ` -.css-1vj1sv9 { +.glamor-0 { font-size: 20px; } <h1 - className="legacy__class css-1vj1sv9" + className="legacy__class glamor-0" > hello world </h1> `; exports[`styled component as selector 1`] = ` -.css-1vj1sv9 { +.glamor-0 { font-size: 20px; } -.css-qrvryj { +.glamor-1 { display: -webkit-box; display: -ms-flexbox; display: flex; } <div - className="css-qrvryj" + className="glamor-1" > hello <h1 - className="css-1vj1sv9" + className="glamor-0" > This will be green </h1> @@ -62,13 +62,13 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-135lost { +.glamor-0 { color: blue; font-size: 32px; } <h1 - className="legacy__class css-135lost" + className="legacy__class glamor-0" scale={2} > hello world @@ -76,32 +76,32 @@ exports[`styled composes 1`] = ` `; exports[`styled composes based on props 1`] = ` -.css-bithoy { +.glamor-0 { color: blue; } <h1 a={true} - className="css-bithoy" + className="glamor-0" > hello world </h1> `; exports[`styled composes based on props 2`] = ` -.css-45352 { +.glamor-0 { color: green; } <h1 - className="css-45352" + className="glamor-0" > hello world </h1> `; exports[`styled composes with objects 1`] = ` -.css-1at6569 { +.glamor-0 { color: #333; font-size: 32px; height: 64px; @@ -112,13 +112,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-1at6569 { + .glamor-0 { font-size: 1.4323121856191332em; } } <h1 - className="legacy__class css-1at6569" + className="legacy__class glamor-0" scale={2} > hello world @@ -126,24 +126,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-1x2u9fm { +.glamor-0 { font-size: 13.333333333333334px; } <h1 - className="legacy__class css-1x2u9fm" + className="legacy__class glamor-0" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-6hsipw { +.glamor-0 { font-size: 40px; } <h1 - className="legacy__class css-6hsipw" + className="legacy__class glamor-0" scale={2} > hello world @@ -151,7 +151,7 @@ exports[`styled function in expression 1`] = ` `; exports[`styled handles more than 10 dynamic properties 1`] = ` -.css-lkeub0 { +.glamor-0 { -webkit-text-decoration: underline; text-decoration: underline; border-right: solid blue 54px; @@ -168,14 +168,14 @@ exports[`styled handles more than 10 dynamic properties 1`] = ` } <h1 - className="legacy__class css-lkeub0" + className="legacy__class glamor-0" > hello world </h1> `; exports[`styled higher order component 1`] = ` -.css-11fn3ay { +.glamor-0 { font-size: 20px; name: onyx; background-color: '#343a40'; @@ -186,92 +186,92 @@ exports[`styled higher order component 1`] = ` } <div - className="css-11fn3ay" + className="glamor-0" /> `; exports[`styled innerRef 1`] = ` -.css-f8g05s { +.glamor-0 { font-size: 12px; } <h1 - className="css-f8g05s" + className="glamor-0" > hello world </h1> `; exports[`styled input placeholder 1`] = ` -.css-wmofgo::-webkit-input-placeholder { +.glamor-0::-webkit-input-placeholder { background-color: green; } -.css-wmofgo:-ms-input-placeholder { +.glamor-0:-ms-input-placeholder { background-color: green; } -.css-wmofgo::placeholder { +.glamor-0::placeholder { background-color: green; } <input - className="css-wmofgo" + className="glamor-0" > hello world </input> `; exports[`styled input placeholder object 1`] = ` -.css-1u5gkp5::placeholder { +.glamor-0::placeholder { background-color: green; } <input - className="css-1u5gkp5" + className="glamor-0" > hello world </input> `; exports[`styled name 1`] = ` -.css-1i0qtj6 { +.glamor-0 { name: FancyH1; font-size: 20px; } <h1 - className="css-1i0qtj6" + className="glamor-0" > hello world </h1> `; exports[`styled nested 1`] = ` -.css-1vj1sv9 { +.glamor-0 { font-size: 20px; } -.css-1fir8qd { +.glamor-1 { display: -webkit-box; display: -ms-flexbox; display: flex; } -.css-1fir8qd div { +.glamor-1 div { color: green; } -.css-1fir8qd div span { +.glamor-1 div span { color: red; } <div - className="css-1fir8qd" + className="glamor-1" > hello <h1 - className="css-1vj1sv9" + className="glamor-0" > This will be green </h1> @@ -280,19 +280,19 @@ exports[`styled nested 1`] = ` `; exports[`styled no dynamic 1`] = ` -.css-f8g05s { +.glamor-0 { font-size: 12px; } <h1 - className="css-f8g05s" + className="glamor-0" > hello world </h1> `; exports[`styled object composition 1`] = ` -.css-xxf8x1 { +.glamor-0 { border-radius: 50%; -webkit-transition: -webkit-transform 400ms ease-in-out; transition: -webkit-transform 400ms ease-in-out; @@ -304,24 +304,24 @@ exports[`styled object composition 1`] = ` color: blue; } -.css-xxf8x1:hover { +.glamor-0:hover { -webkit-transform: scale(1.2); transform: scale(1.2); } <img - className="css-xxf8x1" + className="glamor-0" /> `; exports[`styled objects 1`] = ` -.css-1j0hret { +.glamor-0 { padding: 10px; display: flex; } <h1 - className="some-class css-1j0hret" + className="some-class glamor-0" display="flex" > hello world @@ -329,7 +329,7 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-93wdk { +.glamor-0 { background-color: #ffd43b; color: #8c81d8; height: 64px; @@ -337,7 +337,7 @@ exports[`styled themes 1`] = ` } <span - className="css-93wdk" + className="glamor-0" scale={2} > hello world diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index 6709f38a5..f54aae56b 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -194,6 +194,16 @@ css([], [], function createEmotionStyledRules() { });" `; +exports[`babel css inline object with a bunch of stuff 1`] = ` +" +const cls2 = css({ + display: '-webkit-box; display: -ms-flexbox; display: flex', + WebkitBoxFlex: '1', + msFlex: '1', + flex: '1' +});" +`; + exports[`babel css inline only composes 1`] = ` " const cls1 = css([], [], function createEmotionStyledRules() { diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 704ca766a..0c846d799 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -173,6 +173,18 @@ describe('babel css', () => { ).toThrowErrorMatchingSnapshot() } ) + test('object with a bunch of stuff', () => { + const basic = ` + const cls2 = css({ + display: 'flex', + flex: 1 + }) + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) }) describe('extract', () => { test('css basic', () => { diff --git a/test/css-prop.test.js b/test/css-prop.test.js index 34b54399f..fb75e1d24 100644 --- a/test/css-prop.test.js +++ b/test/css-prop.test.js @@ -1,10 +1,10 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../jest-utils' -import { css } from '../src/index' +import { matcher, serializer } from 'jest-glamor-react' +import { css, sheet } from '../src/index' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('css prop react', () => { @@ -14,7 +14,7 @@ describe('css prop react', () => { .create(<p css={`color: red;font-size:${fontSize}`}>hello world</p>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('string expression', () => { @@ -24,7 +24,7 @@ describe('css prop react', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('kitchen sink', () => { @@ -89,6 +89,6 @@ describe('css prop react', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) }) diff --git a/test/css.test.js b/test/css.test.js index 2f5a70047..4ec35e110 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -1,10 +1,10 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../jest-utils' +import { matcher, serializer } from 'jest-glamor-react' import { sheet, css } from '../src/index' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('css', () => { @@ -24,7 +24,7 @@ describe('css', () => { ` const tree = renderer.create(<div className={cls1} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with undefined values', () => { @@ -33,7 +33,7 @@ describe('css', () => { justifyContent: center; ` const tree = renderer.create(<div className={cls2} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -45,13 +45,13 @@ describe('css', () => { justifyContent: center; ` const tree = renderer.create(<div className={cls2} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('handles objects', () => { const cls1 = css({ display: 'flex' }) const tree = renderer.create(<div className={cls1} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { @@ -74,6 +74,6 @@ describe('css', () => { ` const tree = renderer.create(<div className={cls2} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) }) diff --git a/test/extract/extract.test.js b/test/extract/extract.test.js index 5671385aa..e4f112cfa 100644 --- a/test/extract/extract.test.js +++ b/test/extract/extract.test.js @@ -3,11 +3,11 @@ import React from 'react' import renderer from 'react-test-renderer' import { basename } from 'path' -import { matcher, serializer } from '../../jest-utils' -import { injectGlobal, css } from '../../src/index' +import { matcher, serializer } from 'jest-glamor-react' +import { injectGlobal, css, sheet } from '../../src' import styled from '../../src/react' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('styled', () => { @@ -16,7 +16,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render nested', () => { @@ -36,7 +36,7 @@ describe('styled', () => { ` const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('name', () => { @@ -48,7 +48,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('injectGlobal', () => { diff --git a/test/keyframes.test.js b/test/keyframes.test.js index 21d12c046..ccaaf5f5d 100644 --- a/test/keyframes.test.js +++ b/test/keyframes.test.js @@ -1,14 +1,11 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../jest-utils' +import { matcher, serializer } from 'jest-glamor-react' import { keyframes, sheet } from '../src/index' import styled from '../src/react' -expect.addSnapshotSerializer(serializer) -expect.extend(matcher) - -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('keyframes', () => { @@ -41,7 +38,7 @@ describe('keyframes', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('keyframes with interpolation', () => { const endingRotation = '360deg' @@ -60,7 +57,7 @@ describe('keyframes', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() expect( sheet.tags.map(tag => tag.textContent || '').join('') diff --git a/test/macro/__snapshots__/css.test.js.snap b/test/macro/__snapshots__/css.test.js.snap index 9a3c7cf66..6ca7d7cad 100644 --- a/test/macro/__snapshots__/css.test.js.snap +++ b/test/macro/__snapshots__/css.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css macro composes 1`] = ` -.css-w9g11r { +.glamor-0 { display: -webkit-box; display: -ms-flexbox; display: flex; @@ -11,12 +11,12 @@ exports[`css macro composes 1`] = ` } <div - className="css-w9g11r" + className="glamor-0" /> `; exports[`css macro composes with objects 1`] = ` -.css-d6v2z3 { +.glamor-0 { display: flex,block; width: 30px; height: calc(40vw - 50px); @@ -25,40 +25,40 @@ exports[`css macro composes with objects 1`] = ` justify-content: center; } -.css-d6v2z3:hover { +.glamor-0:hover { color: blue; } -.css-d6v2z3:after { +.glamor-0:after { content: " "; color: red; } @media (min-width: 420px) { - .css-d6v2z3 { + .glamor-0 { color: green; } } <div - className="css-d6v2z3" + className="glamor-0" /> `; exports[`css macro composes with undefined values 1`] = ` -.css-awzhlo { +.glamor-0 { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } <div - className="css-awzhlo" + className="glamor-0" /> `; exports[`css macro handles more than 10 dynamic properties 1`] = ` -.css-1fz3d5g { +.glamor-0 { background: white; color: black; -webkit-text-decoration: underline; @@ -74,16 +74,16 @@ exports[`css macro handles more than 10 dynamic properties 1`] = ` } <div - className="css-1fz3d5g" + className="glamor-0" /> `; exports[`css macro handles objects 1`] = ` -.css-1fe3owl { +.glamor-0 { display: flex; } <div - className="css-1fe3owl" + className="glamor-0" /> `; diff --git a/test/macro/__snapshots__/keyframes.test.js.snap b/test/macro/__snapshots__/keyframes.test.js.snap index 58cd914bf..9a61faa49 100644 --- a/test/macro/__snapshots__/keyframes.test.js.snap +++ b/test/macro/__snapshots__/keyframes.test.js.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes - macro keyframes with interpolation 1`] = ` -.css-1nja0ph { +.glamor-0 { -webkit-animation: animation_1cnjdtk 2s linear infinite; animation: animation_1cnjdtk 2s linear infinite; } <h1 - className="css-1nja0ph" + className="glamor-0" > hello world </h1> @@ -16,13 +16,13 @@ exports[`keyframes - macro keyframes with interpolation 1`] = ` exports[`keyframes - macro keyframes with interpolation 2`] = `"@-webkit-keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-1m1x9re{-webkit-animation:animation_7jdctn 2s linear infinite;animation:animation_7jdctn 2s linear infinite;}@-webkit-keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}.css-1nja0ph{-webkit-animation:animation_1cnjdtk 2s linear infinite;animation:animation_1cnjdtk 2s linear infinite;}"`; exports[`keyframes - macro renders 1`] = ` -.css-1m1x9re { +.glamor-0 { -webkit-animation: animation_7jdctn 2s linear infinite; animation: animation_7jdctn 2s linear infinite; } <h1 - className="css-1m1x9re" + className="glamor-0" > hello world </h1> diff --git a/test/macro/__snapshots__/react.test.js.snap b/test/macro/__snapshots__/react.test.js.snap index fcd789e25..00201709c 100644 --- a/test/macro/__snapshots__/react.test.js.snap +++ b/test/macro/__snapshots__/react.test.js.snap @@ -1,58 +1,58 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`styled basic render 1`] = ` -.css-1vj1sv9 { +.glamor-0 { font-size: 20px; } <h1 - className="css-1vj1sv9" + className="glamor-0" > hello world </h1> `; exports[`styled basic render with object as style 1`] = ` -.css-kp2uw9 { +.glamor-0 { font-size: 20px; } <h1 - className="css-kp2uw9" + className="glamor-0" > hello world </h1> `; exports[`styled call expression 1`] = ` -.css-1vj1sv9 { +.glamor-0 { font-size: 20px; } <h1 - className="css-1vj1sv9" + className="glamor-0" > hello world </h1> `; exports[`styled component as selector 1`] = ` -.css-1vj1sv9 { +.glamor-0 { font-size: 20px; } -.css-qrvryj { +.glamor-1 { display: -webkit-box; display: -ms-flexbox; display: flex; } <div - className="css-qrvryj" + className="glamor-1" > hello <h1 - className="css-1vj1sv9" + className="glamor-0" > This will be green </h1> @@ -61,14 +61,14 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-1baty9 { +.glamor-0 { color: blue; height: 64px; font-size: 32px; } <h1 - className="css-1baty9" + className="glamor-0" scale={2} > hello world @@ -76,32 +76,32 @@ exports[`styled composes 1`] = ` `; exports[`styled composes based on props 1`] = ` -.css-bithoy { +.glamor-0 { color: blue; } <h1 a={true} - className="css-bithoy" + className="glamor-0" > hello world </h1> `; exports[`styled composes based on props 2`] = ` -.css-45352 { +.glamor-0 { color: green; } <h1 - className="css-45352" + className="glamor-0" > hello world </h1> `; exports[`styled composes with objects 1`] = ` -.css-k8dg7v { +.glamor-0 { color: #333; font-size: 1.333em; height: 64px; @@ -113,13 +113,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-k8dg7v { + .glamor-0 { font-size: 1.4323121856191332em; } } <h1 - className="css-k8dg7v" + className="glamor-0" scale={2} > hello world @@ -127,24 +127,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-1x2u9fm { +.glamor-0 { font-size: 13.333333333333334px; } <h1 - className="css-1x2u9fm" + className="glamor-0" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-1iqzzhq { +.glamor-0 { font-size: 40px; } <h1 - className="css-1iqzzhq" + className="glamor-0" scale={2} > hello world @@ -152,7 +152,7 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-1nkevqp { +.glamor-0 { font-size: 20px; background-color: '#343a40'; -webkit-box-orient: vertical; @@ -162,55 +162,55 @@ exports[`styled higher order component 1`] = ` } <div - className="css-1nkevqp" + className="glamor-0" /> `; exports[`styled innerRef 1`] = ` -.css-f8g05s { +.glamor-0 { font-size: 12px; } <h1 - className="css-f8g05s" + className="glamor-0" > hello world </h1> `; exports[`styled name 1`] = ` -.css-1i0qtj6 { +.glamor-0 { name: FancyH1; font-size: 20px; } <h1 - className="css-1i0qtj6" + className="glamor-0" > hello world </h1> `; exports[`styled no dynamic 1`] = ` -.css-f8g05s { +.glamor-0 { font-size: 12px; } <h1 - className="css-f8g05s" + className="glamor-0" > hello world </h1> `; exports[`styled objects 1`] = ` -.css-1j0hret { +.glamor-0 { padding: 10px; display: flex; } <h1 - className="some-class css-1j0hret" + className="some-class glamor-0" display="flex" > hello world @@ -218,7 +218,7 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-93wdk { +.glamor-0 { background-color: #ffd43b; color: #8c81d8; height: 64px; @@ -226,7 +226,7 @@ exports[`styled themes 1`] = ` } <span - className="css-93wdk" + className="glamor-0" scale={2} > hello world diff --git a/test/macro/css.test.js b/test/macro/css.test.js index ec2b6e370..8a9038d9e 100644 --- a/test/macro/css.test.js +++ b/test/macro/css.test.js @@ -3,9 +3,9 @@ import { css } from '../../src/macro' import { sheet } from '../../src' import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../../jest-utils' +import { matcher, serializer } from 'jest-glamor-react' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('css macro', () => { @@ -25,7 +25,7 @@ describe('css macro', () => { ` const tree = renderer.create(<div className={cls1} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with undefined values', () => { @@ -34,7 +34,7 @@ describe('css macro', () => { justifyContent: center; ` const tree = renderer.create(<div className={cls2} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -46,13 +46,13 @@ describe('css macro', () => { justifyContent: center; ` const tree = renderer.create(<div className={cls2} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('handles objects', () => { const cls1 = css({ display: 'flex' }) const tree = renderer.create(<div className={cls1} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { @@ -75,6 +75,6 @@ describe('css macro', () => { ` const tree = renderer.create(<div className={cls2} />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) }) diff --git a/test/macro/keyframes.test.js b/test/macro/keyframes.test.js index 57393d15d..eed0b804e 100644 --- a/test/macro/keyframes.test.js +++ b/test/macro/keyframes.test.js @@ -1,11 +1,11 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../../jest-utils' +import { matcher, serializer } from 'jest-glamor-react' import { keyframes, sheet } from '../../src/macro' import styled from '../../src/react/macro' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('keyframes - macro', () => { @@ -38,7 +38,7 @@ describe('keyframes - macro', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('keyframes with interpolation', () => { const endingRotation = '360deg' @@ -57,7 +57,7 @@ describe('keyframes - macro', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() expect( sheet.tags.map(tag => tag.textContent || '').join('') diff --git a/test/macro/react.test.js b/test/macro/react.test.js index 406e589d8..5ce3cf0cc 100644 --- a/test/macro/react.test.js +++ b/test/macro/react.test.js @@ -1,13 +1,14 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../../jest-utils' -import styled, { css } from '../../src/react/macro' +import { matcher, serializer } from 'jest-glamor-react' +import { css, sheet } from '../../src/macro' +import styled from '../../src/react/macro' import { ThemeProvider } from '../../src/react/theming' import { lighten, hiDPI, modularScale } from 'polished' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('styled', () => { @@ -16,7 +17,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render', () => { @@ -25,7 +26,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render with object as style', () => { @@ -34,7 +35,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('name', () => { @@ -46,7 +47,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('call expression', () => { @@ -57,7 +58,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composition', () => { @@ -70,7 +71,7 @@ describe('styled', () => { const tree = renderer.create(<H2>hello world</H2>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('component as selector', () => { @@ -90,7 +91,7 @@ describe('styled', () => { .create(<Thing>hello <H1>This will be green</H1> world</Thing>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('function in expression', () => { @@ -109,7 +110,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -139,7 +140,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { @@ -171,7 +172,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('innerRef', () => { @@ -183,7 +184,7 @@ describe('styled', () => { .create(<H1 innerRef={refFunction}>hello world</H1>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() expect(refFunction).toBeCalled() }) @@ -227,7 +228,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('higher order component', () => { @@ -252,11 +253,11 @@ describe('styled', () => { const ColumnContent = flexColumn(Content) - // expect(ColumnContent.displayName).toMatchSnapshotWithEmotion() + // expect(ColumnContent.displayName).toMatchSnapshotWithGlamor() const tree = renderer.create(<ColumnContent />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes based on props', () => { @@ -282,7 +283,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() const tree2 = renderer .create( <H1> @@ -291,7 +292,7 @@ describe('styled', () => { ) .toJSON() - expect(tree2).toMatchSnapshotWithEmotion() + expect(tree2).toMatchSnapshotWithGlamor() }) test('objects', () => { @@ -306,7 +307,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('throws if undefined is passed as the component', () => { diff --git a/test/react.test.js b/test/react.test.js index 1d82bf782..282a6e92c 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -1,14 +1,14 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../jest-utils' -import { css } from '../src/index' +import { matcher, serializer } from 'jest-glamor-react' +import { css, sheet } from '../src/index' import styled from '../src/react' import { ThemeProvider } from '../src/react/theming' -import { lighten, hiDPI, modularScale, borderWidth } from 'polished' +import { lighten, hiDPI, modularScale } from 'polished' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('styled', () => { @@ -17,7 +17,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render', () => { @@ -29,7 +29,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render with object as style', () => { @@ -38,7 +38,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('name', () => { @@ -50,7 +50,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('call expression', () => { @@ -63,7 +63,7 @@ describe('styled', () => { .create(<H1 className={'legacy__class'}>hello world</H1>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('nested', () => { @@ -89,7 +89,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composition', () => { @@ -104,7 +104,7 @@ describe('styled', () => { .create(<H2 className={'legacy__class'}>hello world</H2>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('input placeholder', () => { @@ -117,7 +117,7 @@ describe('styled', () => { .create(<Input>hello world</Input>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('input placeholder object', () => { @@ -131,7 +131,7 @@ describe('styled', () => { .create(<Input>hello world</Input>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('object composition', () => { @@ -176,7 +176,7 @@ describe('styled', () => { const tree = renderer.create(<Avatar />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('handles more than 10 dynamic properties', () => { @@ -203,7 +203,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('component as selector', () => { @@ -225,7 +225,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('function in expression', () => { @@ -244,7 +244,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -275,7 +275,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { @@ -307,7 +307,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('innerRef', () => { @@ -319,7 +319,7 @@ describe('styled', () => { .create(<H1 innerRef={refFunction}>hello world</H1>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() expect(refFunction).toBeCalled() }) @@ -363,7 +363,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('higher order component', () => { @@ -390,11 +390,11 @@ describe('styled', () => { const ColumnContent = flexColumn(Content) - // expect(ColumnContent.displayName).toMatchSnapshotWithEmotion() + // expect(ColumnContent.displayName).toMatchSnapshotWithGlamor() const tree = renderer.create(<ColumnContent />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes based on props', () => { @@ -412,10 +412,10 @@ describe('styled', () => { const tree = renderer.create(<H1 a>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() const tree2 = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree2).toMatchSnapshotWithEmotion() + expect(tree2).toMatchSnapshotWithGlamor() }) test('objects', () => { @@ -424,7 +424,7 @@ describe('styled', () => { })) const tree = renderer.create(<H1 display='flex'>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('throws if undefined is passed as the component', () => { From 49b01f17553c5dd49e6484e3b0fb91e83ce84e25 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Sun, 23 Jul 2017 19:23:41 -0600 Subject: [PATCH 43/57] Working on example --- example/.babelrc | 5 +- example/package.json | 4 +- example/src/main.emotion.css | 27 ------- example/src/markdown/index.emotion.css | 22 ------ example/src/playground/index.emotion.css | 96 ------------------------ 5 files changed, 6 insertions(+), 148 deletions(-) delete mode 100644 example/src/main.emotion.css delete mode 100644 example/src/markdown/index.emotion.css delete mode 100644 example/src/playground/index.emotion.css diff --git a/example/.babelrc b/example/.babelrc index c287bec0f..610172959 100644 --- a/example/.babelrc +++ b/example/.babelrc @@ -4,7 +4,10 @@ "env", { "modules": false, - "loose": true + "loose": true, + "targets": { + "uglify": true + } } ], "stage-2", diff --git a/example/package.json b/example/package.json index 093c270a1..7221c15d3 100755 --- a/example/package.json +++ b/example/package.json @@ -15,8 +15,8 @@ "devDependencies": { "babel-core": "^6.23.1", "babel-eslint": "^7.2.3", - "babel-loader": "^7.0.0", - "babel-preset-env": "^1.5.1", + "babel-loader": "^7.1.1", + "babel-preset-env": "^1.6.0", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "babel-standalone": "^6.24.2", diff --git a/example/src/main.emotion.css b/example/src/main.emotion.css deleted file mode 100644 index f6092f436..000000000 --- a/example/src/main.emotion.css +++ /dev/null @@ -1,27 +0,0 @@ -html, body { - font-family: -apple-system, - BlinkMacSystemFont, - "Segoe UI", - "Roboto", - "Roboto Light", - "Oxygen", - "Ubuntu", - "Cantarell", - "Fira Sans", - "Droid Sans", - "Helvetica Neue", - sans-serif, - "Apple Color Emoji", - "Segoe UI Emoji", - "Segoe UI Symbol"; - color: #495057; - width: 100%; - height: 100%; - padding: 0; - margin: 0; - } -@font-face {font-family: 'Oxygen'; - font-style: normal; - font-weight: 400; - src: local('Oxygen Regular'), local('Oxygen-Regular'), url(https://fonts.gstatic.com/s/oxygen/v6/qBSyz106i5ud7wkBU-FrPevvDin1pK8aKteLpeZ5c0A.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;} \ No newline at end of file diff --git a/example/src/markdown/index.emotion.css b/example/src/markdown/index.emotion.css deleted file mode 100644 index c5ac6b432..000000000 --- a/example/src/markdown/index.emotion.css +++ /dev/null @@ -1,22 +0,0 @@ -.css-MarkdownContainer-j714fw h1, .css-MarkdownContainer-j714fw h2, .css-MarkdownContainer-j714fw h3, .css-MarkdownContainer-j714fw h4, .css-MarkdownContainer-j714fw h5 { - margin: 16px 0 8px 0; - letter-spacing: 1px; -} -.css-Link-1ocach0 { font-size: 1rem; - margin-left: auto; - margin-right: 8px; - text-decoration: none; - color: var(--css-Link-1ocach0-0); } -p .css-Link-1ocach0 { margin: 0; } -.css-Link-1ocach0:hover { color: var(--css-Link-1ocach0-1); } -.css-Paragraph-60xrpl { margin: 16px 0; - padding: 2px; - font-size: 0.85rem; - color: var(--css-Paragraph-60xrpl-0); } -.css-Paragraph-60xrpl a { font-size: 0.85rem; } -.css-Code-1u9h7r0 { font-family: monospace; - font-size: 0.75rem; - color: var(--css-Code-1u9h7r0-0); - background-color: var(--css-Code-1u9h7r0-1); - padding: 1px; } -p .css-Code-1u9h7r0 { font-size: 0.99rem; } \ No newline at end of file diff --git a/example/src/playground/index.emotion.css b/example/src/playground/index.emotion.css deleted file mode 100644 index b601f13d6..000000000 --- a/example/src/playground/index.emotion.css +++ /dev/null @@ -1,96 +0,0 @@ -.css-dracula-uybcup .cm-s-dracula.CodeMirror, .css-dracula-uybcup .cm-s-dracula .CodeMirror-gutters { - background-color: #282a36 !important; - color: #f8f8f2 !important; - border: none; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-gutters { - color: #282a36; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-cursor { - border-left: solid thin #f8f8f2 !important; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-linenumber { - color: #6D8A88; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-selected { - background: rgba(255, 255, 255, 0.10); -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-line::selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span::selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span > span::selection { - background: rgba(255, 255, 255, 0.10); -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-line::-moz-selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span::-moz-selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { - background: rgba(255, 255, 255, 0.10); -} -.css-dracula-uybcup .cm-s-dracula span.cm-comment { - color: #6272a4; -} -.css-dracula-uybcup .cm-s-dracula span.cm-string, .css-dracula-uybcup .cm-s-dracula span.cm-string-2 { - color: #f1fa8c; -} -.css-dracula-uybcup .cm-s-dracula span.cm-number { - color: #bd93f9; -} -.css-dracula-uybcup .cm-s-dracula span.cm-variable { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-variable-2 { - color: white; -} -.css-dracula-uybcup .cm-s-dracula span.cm-def { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-operator { - color: #ff79c6; -} -.css-dracula-uybcup .cm-s-dracula span.cm-keyword { - color: #ff79c6; -} -.css-dracula-uybcup .cm-s-dracula span.cm-atom { - color: #bd93f9; -} -.css-dracula-uybcup .cm-s-dracula span.cm-meta { - color: #f8f8f2; -} -.css-dracula-uybcup .cm-s-dracula span.cm-tag { - color: #ff79c6; -} -.css-dracula-uybcup .cm-s-dracula span.cm-attribute { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-qualifier { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-property { - color: #66d9ef; -} -.css-dracula-uybcup .cm-s-dracula span.cm-builtin { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-variable-3, .css-dracula-uybcup .cm-s-dracula span.cm-type { - color: #ffb86c; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-activeline-background { - background: rgba(255, 255, 255, 0.1); -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-matchingbracket { - text-decoration: underline; - color: white !important; -} -.css-PlaygroundContent-1ecmb3 { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - margin: 0 auto 16px auto; - padding: 16px 0; - max-width: 560px; } -.css-PlaygroundContent-1ecmb3 .ReactCodeMirror { - -webkit-flex: 1 1 70%; - -ms-flex: 1 1 70%; - flex: 1 1 70%; } -.css-PlaygroundContent-1ecmb3 .ReactCodeMirror textarea { - border-radius: 4px; } -.css-PlaygroundContent-1ecmb3 .ReactCodeMirror .CodeMirror { - height: auto; - border-radius: 4px; } \ No newline at end of file From f0094f86e33808ddf6ef105cbf5120afd024ceb0 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Sun, 23 Jul 2017 19:48:03 -0600 Subject: [PATCH 44/57] Add grid properties to unitless list --- src/glamor/CSSPropertyOperations/CSSProperty.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/glamor/CSSPropertyOperations/CSSProperty.js b/src/glamor/CSSPropertyOperations/CSSProperty.js index f01a44925..67958f241 100644 --- a/src/glamor/CSSPropertyOperations/CSSProperty.js +++ b/src/glamor/CSSPropertyOperations/CSSProperty.js @@ -29,7 +29,11 @@ let isUnitlessNumber = { flexNegative: true, flexOrder: true, gridRow: true, + gridRowStart: true, + gridRowEnd: true, gridColumn: true, + gridColumnStart: true, + gridColumnEnd: true, fontWeight: true, lineClamp: true, lineHeight: true, From ad08e03522a5406a582029eb584c77e9002526b0 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Sun, 23 Jul 2017 23:16:02 -0600 Subject: [PATCH 45/57] Add `fromAST` to ASTObject so that we can extract static objs. We can store the AST and expressions of an object in an instance of ASTObject and then use that in parseCSS. --- src/ast-object.js | 106 ++++++++++++++++++++++++++++++++++++++-------- src/babel.js | 9 +++- src/index.js | 4 +- test/css.test.js | 7 ++- 4 files changed, 104 insertions(+), 22 deletions(-) diff --git a/src/ast-object.js b/src/ast-object.js index f03f27321..9031967c6 100644 --- a/src/ast-object.js +++ b/src/ast-object.js @@ -1,4 +1,5 @@ -import { forEach } from './utils' +import { forEach, reduce } from './utils' + export default class ASTObject { obj: { [string]: any } expressions: Array<any> @@ -13,12 +14,12 @@ export default class ASTObject { } toAST () { - const {obj, t} = this + const { obj, t } = this const props = [] for (let key in obj) { const rawValue = obj[key] - const {computed, composes, ast: keyAST} = this.objKeyToAst(key) + const { computed, composes, ast: keyAST } = this.objKeyToAst(key) let valueAST if (composes) { @@ -34,17 +35,14 @@ export default class ASTObject { } objKeyToAst (key): { computed: boolean, ast: any, composes: boolean } { - const {t} = this + const { t } = this const matches = this.getDynamicMatches(key) if (matches.length) { return { computed: true, composes: key === 'composes', - ast: this.replacePlaceholdersWithExpressions( - matches, - key - ) + ast: this.replacePlaceholdersWithExpressions(matches, key) } } @@ -56,15 +54,12 @@ export default class ASTObject { } objValueToAst (value) { - const {expressions, composesCount, t} = this + const { expressions, composesCount, t } = this if (typeof value === 'string') { const matches = this.getDynamicMatches(value) if (matches.length) { - return this.replacePlaceholdersWithExpressions( - matches, - value - ) + return this.replacePlaceholdersWithExpressions(matches, value) } return t.stringLiteral(value) } else if (Array.isArray(value)) { @@ -92,22 +87,22 @@ export default class ASTObject { } replacePlaceholdersWithExpressions (matches: any[], str: string) { - const {expressions, composesCount, t} = this + const { expressions, composesCount, t } = this const templateElements = [] const templateExpressions = [] let cursor = 0 // not sure how to detect when to add 'px' // let hasSingleInterpolation = false - forEach(matches, ({value, p1, index}, i) => { + forEach(matches, ({ value, p1, index }, i) => { const preMatch = str.substring(cursor, index) cursor = cursor + preMatch.length + value.length if (preMatch) { templateElements.push( - t.templateElement({raw: preMatch, cooked: preMatch}) + t.templateElement({ raw: preMatch, cooked: preMatch }) ) } else if (i === 0) { - templateElements.push(t.templateElement({raw: '', cooked: ''})) + templateElements.push(t.templateElement({ raw: '', cooked: '' })) } // if (value === str) { // hasSingleInterpolation = true @@ -135,4 +130,81 @@ export default class ASTObject { // } return t.templateLiteral(templateElements, templateExpressions) } + + static fromAST (astObj, t) { + function isLiteral (value) { + return ( + t.isStringLiteral(value) || + t.isNumericLiteral(value) || + t.isBooleanLiteral(value) + ) + } + + let obj = {} + let expressions = [] + + function replaceExpressionsWithPlaceholders (thing) { + if (t.isArrayExpression(thing)) { + return thing.value.elements.map(replaceExpressionsWithPlaceholders) + } else if (isLiteral(thing)) { + return thing.value + } else if (t.isTemplateLiteral(thing)) { + const strs = thing.quasis.map(x => x.value.cooked) + const exprs = thing.expressions + const value = reduce( + strs, + (arr, str, i) => { + arr.push(str) + if (i !== strs.length - 1) { + expressions.push(exprs[i]) + arr.push(`xxx${expressions.length}xxx`) + } + return arr + }, + [] + ) + .join('') + .trim() + return value + } + + expressions.push(thing) + return `xxx${expressions.length}xxx` + } + + function toObj (astObj) { + astObj.properties.forEach(property => { + // nested objects + if (t.isObjectExpression(property.value)) { + let key + if (property.computed) { + key = replaceExpressionsWithPlaceholders(property.key) + } else { + key = t.isStringLiteral(property.key) + ? t.stringLiteral(property.key.value) + : t.identifier(property.key.name) + } + + obj[key] = toObj(property.value) + } else { + let key + if (property.computed) { + key = replaceExpressionsWithPlaceholders(property.key) + } else { + key = property.key.name + } + obj[key] = replaceExpressionsWithPlaceholders(property.value) + } + }) + + return obj + } + + return new ASTObject( + toObj(astObj), + expressions, + 0, // composesCount: we should support this, + t + ) + } } diff --git a/src/babel.js b/src/babel.js index 0e21b16e1..2506dce40 100644 --- a/src/babel.js +++ b/src/babel.js @@ -4,7 +4,6 @@ import { basename } from 'path' import { touchSync } from 'touch' import postcssJs from 'postcss-js' import autoprefixer from 'autoprefixer' -import { forEach, map } from './utils' import { inline } from './inline' import { parseCSS, expandCSSFallbacks } from './parser' import { getIdentifierName } from './babel-utils' @@ -125,8 +124,8 @@ export function buildStyledObjectCallExpression (path, identifier, t) { ]) } +const prefixer = postcssJs.sync([autoprefixer]) function prefixAst (args, t) { - const prefixer = postcssJs.sync([autoprefixer]) function isLiteral (value) { return ( @@ -267,6 +266,12 @@ export default function (babel) { } if (t.isCallExpression(path.node) && path.node.callee.name === 'css') { + path.node.arguments.forEach((arg) => { + if (t.isObjectExpression(arg)) { + console.log(JSON.stringify(ASTObject.fromAST(arg, t).expressions, null, 2)) + } + }) + const prefixedAst = prefixAst(path.node.arguments, t) path.replaceWith(t.callExpression(t.identifier('css'), prefixedAst)) } diff --git a/src/index.js b/src/index.js index 95edfc4db..05f247b9c 100644 --- a/src/index.js +++ b/src/index.js @@ -341,8 +341,8 @@ function isSelector (key) { } function joinSelectors (a, b) { - let as = a.split(',').map(a => (!(a.indexOf('&') >= 0) ? '&' + a : a)) - let bs = b.split(',').map(b => (!(b.indexOf('&') >= 0) ? '&' + b : 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))), []) diff --git a/test/css.test.js b/test/css.test.js index 4ec35e110..700e27098 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -49,7 +49,12 @@ describe('css', () => { }) test('handles objects', () => { - const cls1 = css({ display: 'flex' }) + const cls1 = css({ + display: 'flex', + color: `${'blue'}`, + fontSize: `${'20px'}`, + height: `${50}` + }) const tree = renderer.create(<div className={cls1} />).toJSON() expect(tree).toMatchSnapshotWithGlamor() }) From 762207b9b00cc31d7a78526d3b58a74b32758fb9 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 18:02:19 +1000 Subject: [PATCH 46/57] Add pure comments before calls to css, keyframes and styled --- src/babel.js | 5 +++- src/macro.js | 8 ++++-- .../babel/__snapshots__/css-prop.test.js.snap | 18 ++++++------- test/babel/__snapshots__/css.test.js.snap | 26 +++++++++---------- .../__snapshots__/keyframes.test.js.snap | 6 ++--- test/babel/__snapshots__/macro.test.js.snap | 10 +++---- test/babel/__snapshots__/styled.test.js.snap | 4 +-- 7 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/babel.js b/src/babel.js index 2506dce40..fc024199c 100644 --- a/src/babel.js +++ b/src/babel.js @@ -42,6 +42,10 @@ export function replaceCssWithCallExpression ( composeValues.push(path.node.quasi.expressions[i]) } + if (!removePath) { + path.addComment('leading', '#__PURE__') + } + inputClasses.push(new ASTObject(styles, false, composesCount, t).toAST()) const vars = path.node.quasi.expressions.slice(composesCount) @@ -126,7 +130,6 @@ export function buildStyledObjectCallExpression (path, identifier, t) { const prefixer = postcssJs.sync([autoprefixer]) function prefixAst (args, t) { - function isLiteral (value) { return ( t.isStringLiteral(value) || diff --git a/src/macro.js b/src/macro.js index a25ad9154..f0f2560eb 100644 --- a/src/macro.js +++ b/src/macro.js @@ -21,7 +21,9 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t ), state, - t + t, + () => {}, + true ) } }) @@ -36,7 +38,9 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { path, buildMacroRuntimeNode(fontFaceReference, state, 'fontFace', t), state, - t + t, + () => {}, + true ) } }) diff --git a/test/babel/__snapshots__/css-prop.test.js.snap b/test/babel/__snapshots__/css-prop.test.js.snap index 1c58fc800..796edddc9 100644 --- a/test/babel/__snapshots__/css-prop.test.js.snap +++ b/test/babel/__snapshots__/css-prop.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel css prop StringLiteral css prop value 1`] = ` -"<div className={css([], [], function createEmotionStyledRules() { +"<div className={/*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"brown\\" }]; @@ -20,7 +20,7 @@ exports[`babel css prop basic 2`] = ` `; exports[`babel css prop basic inline 1`] = ` -"<div className={\\"a\\" + \\" \\" + css([], [], function createEmotionStyledRules() { +"<div className={\\"a\\" + \\" \\" + /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"brown\\" }]; @@ -28,7 +28,7 @@ exports[`babel css prop basic inline 1`] = ` `; exports[`babel css prop className as expression 1`] = ` -"<div className={variable + \\" \\" + css([], [], function createEmotionStyledRules() { +"<div className={variable + \\" \\" + /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"brown\\" }]; @@ -36,7 +36,7 @@ exports[`babel css prop className as expression 1`] = ` `; exports[`babel css prop className as expression string 1`] = ` -"<div className={\`test__class\` + \\" \\" + css([], [], function createEmotionStyledRules() { +"<div className={\`test__class\` + \\" \\" + /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"brown\\" }]; @@ -44,13 +44,13 @@ exports[`babel css prop className as expression string 1`] = ` `; exports[`babel css prop css empty 1`] = ` -"<div className={css([], [], function createEmotionStyledRules() { +"<div className={/*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{}]; })}></div>;" `; exports[`babel css prop dynamic inline 1`] = ` -"<div className={\\"a\\" + \\" \\" + css([], [color], function createEmotionStyledRules(x0) { +"<div className={\\"a\\" + \\" \\" + /*#__PURE__*/css([], [color], function createEmotionStyledRules(x0) { return [{ \\"color\\": \`\${x0}\` }]; @@ -58,7 +58,7 @@ exports[`babel css prop dynamic inline 1`] = ` `; exports[`babel css prop emptyClassName 1`] = ` -"<div className={\\"\\" + \\" \\" + css([], [], function createEmotionStyledRules() { +"<div className={\\"\\" + \\" \\" + /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"brown\\" }]; @@ -68,7 +68,7 @@ exports[`babel css prop emptyClassName 1`] = ` exports[`babel css prop no css attr 1`] = `"<div></div>;"`; exports[`babel css prop noClassName 1`] = ` -"<div className={css([], [], function createEmotionStyledRules() { +"<div className={/*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"brown\\" }]; @@ -76,7 +76,7 @@ exports[`babel css prop noClassName 1`] = ` `; exports[`babel css prop with spread arg in jsx opening tag 1`] = ` -"<div className={\\"a\\" + \\" \\" + css([], [], function createEmotionStyledRules() { +"<div className={\\"a\\" + \\" \\" + /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"brown\\" }]; diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index f54aae56b..7222275bc 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -10,7 +10,7 @@ exports[`babel css extract composes 1`] = ` "import './css.test.emotion.css'; const cls1 = 'css-cls1-1q8jsgx'; -const cls2 = css([['one-class', 'another-class', cls1]], ['center'], function createEmotionStyledRules(x0) { +const cls2 = /*#__PURE__*/css([['one-class', 'another-class', cls1]], ['center'], function createEmotionStyledRules(x0) { return [{ 'WebkitBoxPack': 'center', 'msFlexPack': 'center', @@ -32,7 +32,7 @@ exports[`babel css extract composes no dynamic 1`] = ` "import './css.test.emotion.css'; const cls1 = 'css-cls1-1q8jsgx'; -const cls2 = css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { return [{ 'WebkitBoxPack': 'center', 'msFlexPack': 'center', @@ -55,7 +55,7 @@ exports[`babel css extract composes with objects 1`] = ` const cls1 = css({ display: '-webkit-box; display: -ms-flexbox; display: flex' }); -const cls2 = css([cls1], [], function createEmotionStyledRules() { +const cls2 = /*#__PURE__*/css([cls1], [], function createEmotionStyledRules() { return [{ 'height': '20', 'WebkitBoxPack': 'center', @@ -123,12 +123,12 @@ css({ exports[`babel css inline composes 1`] = ` " -const cls1 = css([], [], function createEmotionStyledRules() { +const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; }); -const cls2 = css(['one-class', 'another-class', cls1], ['center'], function createEmotionStyledRules(x0) { +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', cls1], ['center'], function createEmotionStyledRules(x0) { return [{ 'WebkitBoxPack': 'center', 'msFlexPack': 'center', @@ -142,7 +142,7 @@ const cls2 = css(['one-class', 'another-class', cls1], ['center'], function crea exports[`babel css inline css basic 1`] = ` " -css([], [widthVar], function createEmotionStyledRules(x0) { +/*#__PURE__*/css([], [widthVar], function createEmotionStyledRules(x0) { return [{ \\"margin\\": \\"12px 48px\\", \\"color\\": \\"#ffffff; color: blue\\", @@ -157,7 +157,7 @@ css([], [widthVar], function createEmotionStyledRules(x0) { exports[`babel css inline interpolation in selector 1`] = ` " -const cls2 = css([], [className], function createEmotionStyledRules(x0) { +const cls2 = /*#__PURE__*/css([], [className], function createEmotionStyledRules(x0) { return [{ \\"margin\\": \\"12px 48px\\", \\"color\\": \\"#ffffff\\", @@ -170,7 +170,7 @@ const cls2 = css([], [className], function createEmotionStyledRules(x0) { exports[`babel css inline lots of composes 1`] = ` " -const cls2 = css(['one-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class'], ['center'], function createEmotionStyledRules(x0) { +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class'], ['center'], function createEmotionStyledRules(x0) { return [{ 'WebkitBoxPack': 'center', 'msFlexPack': 'center', @@ -184,7 +184,7 @@ const cls2 = css(['one-class', 'another-class', 'another-class', 'another-class' exports[`babel css inline nested expanded properties 1`] = ` " -css([], [], function createEmotionStyledRules() { +/*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"margin\\": \\"12px 48px\\", \\"& .div\\": { @@ -206,24 +206,24 @@ const cls2 = css({ exports[`babel css inline only composes 1`] = ` " -const cls1 = css([], [], function createEmotionStyledRules() { +const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; }); -const cls2 = css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { return [{}]; });" `; exports[`babel css inline only styles on nested selector 1`] = ` " -const cls1 = css([], [], function createEmotionStyledRules() { +const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"display\\": \\"-webkit-box; display: -ms-flexbox; display: flex\\" }]; }); -const cls2 = css([], [], function createEmotionStyledRules() { +const cls2 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"&:hover\\": { \\"background\\": \\"pink\\" diff --git a/test/babel/__snapshots__/keyframes.test.js.snap b/test/babel/__snapshots__/keyframes.test.js.snap index 21da53a0e..adb87d248 100644 --- a/test/babel/__snapshots__/keyframes.test.js.snap +++ b/test/babel/__snapshots__/keyframes.test.js.snap @@ -31,7 +31,7 @@ exports[`babel keyframes extract keyframes basic 2`] = ` exports[`babel keyframes extract keyframes with interpolation 1`] = ` " -const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRules(x0) { +const rotate360 = /*#__PURE__*/keyframes([], [endingRotation], function createEmotionStyledRules(x0) { return [{ \\"from\\": { \\"WebkitTransform\\": \\"rotate(0deg)\\", @@ -47,7 +47,7 @@ const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRu exports[`babel keyframes inline keyframes basic 1`] = ` " -const rotate360 = keyframes([], [], function createEmotionStyledRules() { +const rotate360 = /*#__PURE__*/keyframes([], [], function createEmotionStyledRules() { return [{ \\"from\\": { \\"WebkitTransform\\": \\"rotate(0deg)\\", @@ -63,7 +63,7 @@ const rotate360 = keyframes([], [], function createEmotionStyledRules() { exports[`babel keyframes inline keyframes with interpolation 1`] = ` " -const rotate360 = keyframes([], [endingRotation], function createEmotionStyledRules(x0) { +const rotate360 = /*#__PURE__*/keyframes([], [endingRotation], function createEmotionStyledRules(x0) { return [{ \\"from\\": { \\"WebkitTransform\\": \\"rotate(0deg)\\", diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index 6d5d8257c..3b3726c05 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -3,7 +3,7 @@ exports[`babel macro css 1`] = ` "import { css as _css } from '../../src'; -_css([], [widthVar], function createEmotionStyledRules(x0) { +/*#__PURE__*/_css([], [widthVar], function createEmotionStyledRules(x0) { return [{ 'margin': '12px 48px', 'color': '#ffffff; color: blue', @@ -77,7 +77,7 @@ _injectGlobal([], [], function createEmotionStyledRules() { exports[`babel macro keyframes 1`] = ` "import { keyframes as _keyframes } from '../../src'; -const rotate360 = _keyframes([], [], function createEmotionStyledRules() { +const rotate360 = /*#__PURE__*/_keyframes([], [], function createEmotionStyledRules() { return [{ 'from': { 'WebkitTransform': 'rotate(0deg)', @@ -94,7 +94,7 @@ const rotate360 = _keyframes([], [], function createEmotionStyledRules() { exports[`babel macro multiple imports 1`] = ` "import { keyframes as _keyframes, css as _css } from '../../src'; -const rotate360 = _keyframes([], [], function createEmotionStyledRules() { +const rotate360 = /*#__PURE__*/_keyframes([], [], function createEmotionStyledRules() { return [{ 'from': { 'WebkitTransform': 'rotate(0deg)', @@ -106,7 +106,7 @@ const rotate360 = _keyframes([], [], function createEmotionStyledRules() { } }]; }); -const thing = _css([], [widthVar], function createEmotionStyledRules(x0) { +const thing = /*#__PURE__*/_css([], [widthVar], function createEmotionStyledRules(x0) { return [{ 'margin': '12px 48px', 'color': '#ffffff; color: blue', @@ -128,7 +128,7 @@ const someOtherVar = _thisDoesNotExist;" exports[`babel macro styled css from react 1`] = ` "import { css as _css } from '../../src/react'; -const someCls = _css([], [], function createEmotionStyledRules() { +const someCls = /*#__PURE__*/_css([], [], function createEmotionStyledRules() { return [{ 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index ecbde4a1b..468a677b4 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -51,7 +51,7 @@ exports[`babel styled component inline basic 1`] = ` `; exports[`babel styled component inline composes based on props 1`] = ` -"const cls1 = css([], [], function createEmotionStyledRules() { +"const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ 'width': '20px' }]; @@ -150,7 +150,7 @@ exports[`babel styled component inline more than 10 dynamic values 1`] = ` exports[`babel styled component inline name is correct with no identifier 1`] = ` " -css([], [], function createEmotionStyledRules() { +/*#__PURE__*/css([], [], function createEmotionStyledRules() { return [{ \\"margin\\": \\"12px 48px\\", \\"color\\": \\"#ffffff\\" From 1846d4bb1c924e77a8429c7ca5f113427a4668f2 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 18:15:05 +1000 Subject: [PATCH 47/57] Fix ASTObject.fromAST --- src/ast-object.js | 18 +++++++++--------- test/__snapshots__/css.test.js.snap | 4 ++++ test/css.test.js | 3 ++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/ast-object.js b/src/ast-object.js index 9031967c6..369dac700 100644 --- a/src/ast-object.js +++ b/src/ast-object.js @@ -143,14 +143,14 @@ export default class ASTObject { let obj = {} let expressions = [] - function replaceExpressionsWithPlaceholders (thing) { - if (t.isArrayExpression(thing)) { - return thing.value.elements.map(replaceExpressionsWithPlaceholders) - } else if (isLiteral(thing)) { - return thing.value - } else if (t.isTemplateLiteral(thing)) { - const strs = thing.quasis.map(x => x.value.cooked) - const exprs = thing.expressions + function replaceExpressionsWithPlaceholders (node) { + if (t.isArrayExpression(node)) { + return node.elements.map(replaceExpressionsWithPlaceholders) + } else if (isLiteral(node)) { + return node.value + } else if (t.isTemplateLiteral(node)) { + const strs = node.quasis.map(x => x.value.cooked) + const exprs = node.expressions const value = reduce( strs, (arr, str, i) => { @@ -168,7 +168,7 @@ export default class ASTObject { return value } - expressions.push(thing) + expressions.push(node) return `xxx${expressions.length}xxx` } diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 57742b36b..06cf5d893 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -86,6 +86,10 @@ exports[`css handles objects 1`] = ` display: -webkit-box; display: -ms-flexbox; display: flex; + color: blue; + font-size: 20px; + height: 50px; + width: 20px; } <div diff --git a/test/css.test.js b/test/css.test.js index 700e27098..25f73d783 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -53,7 +53,8 @@ describe('css', () => { display: 'flex', color: `${'blue'}`, fontSize: `${'20px'}`, - height: `${50}` + height: `${50}`, + width: 20 }) const tree = renderer.create(<div className={cls1} />).toJSON() expect(tree).toMatchSnapshotWithGlamor() From 2e259aec09325fb50b156b793af38ae594f42fe2 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 20:11:10 +1000 Subject: [PATCH 48/57] It works for css calls --- src/ast-object.js | 29 ++++------ src/babel.js | 33 ++++++++---- src/parser.js | 8 ++- test/babel/__snapshots__/css.test.js.snap | 64 +++++++++++++++-------- test/babel/css.test.js | 18 ++++++- 5 files changed, 100 insertions(+), 52 deletions(-) diff --git a/src/ast-object.js b/src/ast-object.js index 369dac700..0e3c25656 100644 --- a/src/ast-object.js +++ b/src/ast-object.js @@ -140,7 +140,6 @@ export default class ASTObject { ) } - let obj = {} let expressions = [] function replaceExpressionsWithPlaceholders (node) { @@ -157,7 +156,7 @@ export default class ASTObject { arr.push(str) if (i !== strs.length - 1) { expressions.push(exprs[i]) - arr.push(`xxx${expressions.length}xxx`) + arr.push(`xxx${expressions.length - 1}xxx`) } return arr }, @@ -169,30 +168,22 @@ export default class ASTObject { } expressions.push(node) - return `xxx${expressions.length}xxx` + return `xxx${expressions.length - 1}xxx` } function toObj (astObj) { - astObj.properties.forEach(property => { + let obj = {} + forEach(astObj.properties, property => { // nested objects + let key + if (property.computed) { + key = replaceExpressionsWithPlaceholders(property.key) + } else { + key = t.isIdentifier(property.key) ? property.key.name : property.key.value + } if (t.isObjectExpression(property.value)) { - let key - if (property.computed) { - key = replaceExpressionsWithPlaceholders(property.key) - } else { - key = t.isStringLiteral(property.key) - ? t.stringLiteral(property.key.value) - : t.identifier(property.key.name) - } - obj[key] = toObj(property.value) } else { - let key - if (property.computed) { - key = replaceExpressionsWithPlaceholders(property.key) - } else { - key = property.key.name - } obj[key] = replaceExpressionsWithPlaceholders(property.value) } }) diff --git a/src/babel.js b/src/babel.js index fc024199c..895beb7ff 100644 --- a/src/babel.js +++ b/src/babel.js @@ -7,6 +7,7 @@ import autoprefixer from 'autoprefixer' import { inline } from './inline' import { parseCSS, expandCSSFallbacks } from './parser' import { getIdentifierName } from './babel-utils' +import { map } from './utils' import cssProps from './css-prop' import ASTObject from './ast-object' @@ -202,6 +203,16 @@ function prefixAst (args, t) { return args } +function buildProcessedStylesFromObjectAST (objectAST, t) { + if (t.isObjectExpression(objectAST)) { + const astObject = ASTObject.fromAST(objectAST, t) + const { styles } = parseCSS(astObject.obj, false) + astObject.obj = styles + return astObject.toAST() + } + return objectAST +} + const visited = Symbol('visited') export default function (babel) { @@ -267,17 +278,21 @@ export default function (babel) { : path.node.callee.object path.replaceWith(buildStyledObjectCallExpression(path, identifier, t)) } - - if (t.isCallExpression(path.node) && path.node.callee.name === 'css') { - path.node.arguments.forEach((arg) => { - if (t.isObjectExpression(arg)) { - console.log(JSON.stringify(ASTObject.fromAST(arg, t).expressions, null, 2)) + try { + if (t.isCallExpression(path.node) && path.node.callee.name === 'css' && !path.node.arguments[1]) { + const argWithStyles = path.node.arguments[0] + if (t.isObjectExpression(argWithStyles)) { + const styles = buildProcessedStylesFromObjectAST(argWithStyles, t) + path.replaceWith(t.callExpression(t.identifier('css'), [styles])) + } else if (t.isArrayExpression(argWithStyles)) { + const processedStyles = map(argWithStyles.elements, styles => buildProcessedStylesFromObjectAST(styles, t)) + path.replaceWith(t.callExpression(t.identifier('css'), [t.arrayExpression(processedStyles)])) } - }) - - const prefixedAst = prefixAst(path.node.arguments, t) - path.replaceWith(t.callExpression(t.identifier('css'), prefixedAst)) + } + } catch (e) { + throw path.buildCodeFrameError(e) } + path[visited] = true }, TaggedTemplateExpression (path, state) { diff --git a/src/parser.js b/src/parser.js index 027ddc6eb..bc5415715 100644 --- a/src/parser.js +++ b/src/parser.js @@ -3,6 +3,7 @@ import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' import postcssJs from 'postcss-js' +import objParse from 'postcss-js/parser' import autoprefixer from 'autoprefixer' import { processStyleName } from './glamor/CSSPropertyOperations' import { objStyle } from './index' @@ -31,7 +32,12 @@ export function parseCSS ( composesCount: number } { // todo - handle errors - let root = parse(css) + let root + if (typeof css === 'object') { + root = objParse(css) + } else { + root = parse(css) + } let vars = 0 let composes: number = 0 diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index 7222275bc..0a65d2543 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -2,7 +2,7 @@ exports[`babel css extract basic object support 1`] = ` "css({ - display: '-webkit-box; display: -ms-flexbox; display: flex' + 'display': '-webkit-box; display: -ms-flexbox; display: flex' });" `; @@ -53,7 +53,7 @@ exports[`babel css extract composes no dynamic 2`] = ` exports[`babel css extract composes with objects 1`] = ` " const cls1 = css({ - display: '-webkit-box; display: -ms-flexbox; display: flex' + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }); const cls2 = /*#__PURE__*/css([cls1], [], function createEmotionStyledRules() { return [{ @@ -91,36 +91,53 @@ exports[`babel css extract css basic 2`] = ` exports[`babel css extract prefixed array of objects 1`] = ` " css([{ - borderRadius: '50%', - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: '-webkit-box; display: -ms-flexbox; display: flex', + 'borderRadius': '50%', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' } }, { - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out' + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out' }]);" `; exports[`babel css extract prefixed objects 1`] = ` " css({ - borderRadius: '50%', - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: '-webkit-box; display: -ms-flexbox; display: flex', + 'borderRadius': '50%', + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' } });" `; +exports[`babel css inline array of objects 1`] = ` +" +const cls2 = css([{ + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', + 'msFlex': '1', + 'flex': '1', + 'WebkitBoxAlign': \`\${'center'}\`, + 'msFlexAlign': \`\${'center'}\`, + 'alignItems': \`\${'center'}\` +}, { + 'WebkitBoxPack': 'start', + 'msFlexPack': 'start', + 'justifyContent': 'flex-start' +}]);" +`; + exports[`babel css inline composes 1`] = ` " const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { @@ -197,10 +214,13 @@ exports[`babel css inline nested expanded properties 1`] = ` exports[`babel css inline object with a bunch of stuff 1`] = ` " const cls2 = css({ - display: '-webkit-box; display: -ms-flexbox; display: flex', - WebkitBoxFlex: '1', - msFlex: '1', - flex: '1' + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', + 'msFlex': '1', + 'flex': '1', + 'WebkitBoxAlign': \`\${'center'}\`, + 'msFlexAlign': \`\${'center'}\`, + 'alignItems': \`\${'center'}\` });" `; diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 0c846d799..e6b4fde56 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -177,7 +177,8 @@ describe('babel css', () => { const basic = ` const cls2 = css({ display: 'flex', - flex: 1 + flex: 1, + alignItems: \`\${'center'}\` }) ` const { code } = babel.transform(basic, { @@ -185,6 +186,21 @@ describe('babel css', () => { }) expect(code).toMatchSnapshot() }) + test('array of objects', () => { + const basic = ` + const cls2 = css([{ + display: 'flex', + flex: 1, + alignItems: \`\${'center'}\` + }, { + justifyContent: 'flex-start' + }]) + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) }) describe('extract', () => { test('css basic', () => { From 1982133ff39387c72a41692a275f4124779a9e11 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 20:12:37 +1000 Subject: [PATCH 49/57] Prettier and expand try --- src/ast-object.js | 4 +++- src/babel.js | 47 +++++++++++++++++++++++++++++++---------------- src/index.js | 13 ++++++------- src/parser.js | 7 ++++--- 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/ast-object.js b/src/ast-object.js index 0e3c25656..9245cf265 100644 --- a/src/ast-object.js +++ b/src/ast-object.js @@ -179,7 +179,9 @@ export default class ASTObject { if (property.computed) { key = replaceExpressionsWithPlaceholders(property.key) } else { - key = t.isIdentifier(property.key) ? property.key.name : property.key.value + key = t.isIdentifier(property.key) + ? property.key.name + : property.key.value } if (t.isObjectExpression(property.value)) { obj[key] = toObj(property.value) diff --git a/src/babel.js b/src/babel.js index 895beb7ff..d9e0a5c66 100644 --- a/src/babel.js +++ b/src/babel.js @@ -110,7 +110,9 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { vars.map((x, i) => t.identifier(`x${i}`)), t.blockStatement([ t.returnStatement( - t.arrayExpression([new ASTObject(styles, false, composesCount, t).toAST()]) + t.arrayExpression([ + new ASTObject(styles, false, composesCount, t).toAST() + ]) ) ]) ) @@ -266,27 +268,40 @@ export default function (babel) { if (path[visited]) { return } - if ( - (t.isCallExpression(path.node.callee) && - path.node.callee.callee.name === 'styled') || - (t.isMemberExpression(path.node.callee) && - t.isIdentifier(path.node.callee.object) && - path.node.callee.object.name === 'styled') - ) { - const identifier = t.isCallExpression(path.node.callee) - ? path.node.callee.callee - : path.node.callee.object - path.replaceWith(buildStyledObjectCallExpression(path, identifier, t)) - } try { - if (t.isCallExpression(path.node) && path.node.callee.name === 'css' && !path.node.arguments[1]) { + if ( + (t.isCallExpression(path.node.callee) && + path.node.callee.callee.name === 'styled') || + (t.isMemberExpression(path.node.callee) && + t.isIdentifier(path.node.callee.object) && + path.node.callee.object.name === 'styled') + ) { + const identifier = t.isCallExpression(path.node.callee) + ? path.node.callee.callee + : path.node.callee.object + path.replaceWith( + buildStyledObjectCallExpression(path, identifier, t) + ) + } + + if ( + t.isCallExpression(path.node) && + path.node.callee.name === 'css' && + !path.node.arguments[1] + ) { const argWithStyles = path.node.arguments[0] if (t.isObjectExpression(argWithStyles)) { const styles = buildProcessedStylesFromObjectAST(argWithStyles, t) path.replaceWith(t.callExpression(t.identifier('css'), [styles])) } else if (t.isArrayExpression(argWithStyles)) { - const processedStyles = map(argWithStyles.elements, styles => buildProcessedStylesFromObjectAST(styles, t)) - path.replaceWith(t.callExpression(t.identifier('css'), [t.arrayExpression(processedStyles)])) + const processedStyles = map(argWithStyles.elements, styles => + buildProcessedStylesFromObjectAST(styles, t) + ) + path.replaceWith( + t.callExpression(t.identifier('css'), [ + t.arrayExpression(processedStyles) + ]) + ) } } } catch (e) { diff --git a/src/index.js b/src/index.js index 05f247b9c..1fdb73d04 100644 --- a/src/index.js +++ b/src/index.js @@ -185,10 +185,8 @@ type EmotionClassName = { [string]: any } -let cachedCss: (rules: CSSRuleList) => EmotionClassName = typeof WeakMap !== - 'undefined' - ? multiIndexCache(_css) - : _css +let cachedCss: (rules: CSSRuleList) => EmotionClassName = + typeof WeakMap !== 'undefined' ? multiIndexCache(_css) : _css // 🍩 // https://github.com/threepointone/glamor @@ -450,9 +448,10 @@ Object.defineProperty(nullrule, 'toString', { } }) -let inputCaches = typeof WeakMap !== 'undefined' - ? [nullrule, new WeakMap(), new WeakMap(), new WeakMap()] - : [nullrule] +let inputCaches = + typeof WeakMap !== 'undefined' + ? [nullrule, new WeakMap(), new WeakMap(), new WeakMap()] + : [nullrule] let warnedWeakMapError = false diff --git a/src/parser.js b/src/parser.js index bc5415715..c29135c5d 100644 --- a/src/parser.js +++ b/src/parser.js @@ -63,9 +63,10 @@ export function parseCSS ( return { styles, - staticCSSRules: vars === 0 && extractStatic - ? stringifyCSSRoot(postcssJs.parse(styles)) - : [], + staticCSSRules: + vars === 0 && extractStatic + ? stringifyCSSRoot(postcssJs.parse(styles)) + : [], composesCount: composes } } From 50b59c256f6abae4067445c3ec049cbe8f79bd0d Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 21:23:37 +1000 Subject: [PATCH 50/57] Fix path removing and add pure comment to styled calls --- src/babel.js | 38 ++++++++++--------- .../__snapshots__/font-face.test.js.snap | 2 +- .../__snapshots__/inject-global.test.js.snap | 2 +- test/babel/__snapshots__/macro.test.js.snap | 4 +- test/babel/__snapshots__/styled.test.js.snap | 24 ++++++------ 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/babel.js b/src/babel.js index d9e0a5c66..b6f6b2fc2 100644 --- a/src/babel.js +++ b/src/babel.js @@ -30,25 +30,24 @@ export function replaceCssWithCallExpression ( const { staticCSSRules } = parseCSS(cssText, true) state.insertStaticRules(staticCSSRules) - return removePath - ? path.remove() - : path.replaceWith(t.stringLiteral(`${name}-${hash}`)) + if (!removePath) { + return path.replaceWith(t.stringLiteral(`${name}-${hash}`)) + } + if (t.isExpressionStatement(path.parent)) { + path.parentPath.remove() + } else { + path.replaceWith(t.identifier('undefined')) + } + return } const { styles, composesCount } = parseCSS(src, false) - const inputClasses = [] - const composeValues = [] - for (let i = 0; i < composesCount; i++) { - composeValues.push(path.node.quasi.expressions[i]) - } - if (!removePath) { path.addComment('leading', '#__PURE__') } - inputClasses.push(new ASTObject(styles, false, composesCount, t).toAST()) - + const composeValues = path.node.quasi.expressions.slice(0, composesCount) const vars = path.node.quasi.expressions.slice(composesCount) path.replaceWith( t.callExpression(identifier, [ @@ -57,7 +56,13 @@ export function replaceCssWithCallExpression ( t.functionExpression( t.identifier('createEmotionStyledRules'), vars.map((x, i) => t.identifier(`x${i}`)), - t.blockStatement([t.returnStatement(t.arrayExpression(inputClasses))]) + t.blockStatement([ + t.returnStatement( + t.arrayExpression([ + new ASTObject(styles, false, composesCount, t).toAST() + ]) + ) + ]) ) ]) ) @@ -90,14 +95,11 @@ export function buildStyledCallExpression (identifier, tag, path, state, t) { ]) } - const { src } = inline(path.node.quasi, getIdentifierName(path, t), 'css') + const { src } = inline(path.node.quasi, identifierName, 'css') + + path.addComment('leading', '#__PURE__') const { styles, composesCount } = parseCSS(src, false) - // const inputClasses = [] - // const composeValues = [] - // for (let i = 0; i < composesCount; i++) { - // composeValues.push(path.node.quasi.expressions[i]) - // } const objs = path.node.quasi.expressions.slice(0, composesCount) const vars = path.node.quasi.expressions.slice(composesCount) diff --git a/test/babel/__snapshots__/font-face.test.js.snap b/test/babel/__snapshots__/font-face.test.js.snap index fe6ed37a1..767317ad4 100644 --- a/test/babel/__snapshots__/font-face.test.js.snap +++ b/test/babel/__snapshots__/font-face.test.js.snap @@ -15,7 +15,7 @@ exports[`fontFace babel extract basic 2`] = ` exports[`fontFace babel extract basic assign to variable 1`] = ` "import \\"./font-face.test.emotion.css\\"; -const thisWillBeUndefined;" +const thisWillBeUndefined = undefined;" `; exports[`fontFace babel extract basic assign to variable 2`] = ` diff --git a/test/babel/__snapshots__/inject-global.test.js.snap b/test/babel/__snapshots__/inject-global.test.js.snap index fd7909a91..746032a1a 100644 --- a/test/babel/__snapshots__/inject-global.test.js.snap +++ b/test/babel/__snapshots__/inject-global.test.js.snap @@ -3,7 +3,7 @@ exports[`babel injectGlobal extract injectGlobal assign to variable 1`] = ` "import \\"./inject-global.test.emotion.css\\"; -const thisWillBeUndefined;" +const thisWillBeUndefined = undefined;" `; exports[`babel injectGlobal extract injectGlobal assign to variable 2`] = ` diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index 3b3726c05..203e9872f 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -160,7 +160,7 @@ const someOtherVar = _thisDoesNotExist;" exports[`babel macro styled tagged template literal function 1`] = ` "import _styled from '../../src/react'; -const SomeComponent = _styled('div', [], [], function createEmotionStyledRules() { +const SomeComponent = /*#__PURE__*/_styled('div', [], [], function createEmotionStyledRules() { return [{ 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; @@ -170,7 +170,7 @@ const SomeComponent = _styled('div', [], [], function createEmotionStyledRules() exports[`babel macro styled tagged template literal member 1`] = ` "import _styled from '../../src/react'; -const SomeComponent = _styled('div', [], [], function createEmotionStyledRules() { +const SomeComponent = /*#__PURE__*/_styled('div', [], [], function createEmotionStyledRules() { return [{ 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 468a677b4..4d940e25e 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -43,7 +43,7 @@ styled(\\"h1\\", [\\"styled-0\\"]);" exports[`babel styled component extract no use 2`] = `".styled-0 {}"`; exports[`babel styled component inline basic 1`] = ` -"const H1 = styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { +"const H1 = /*#__PURE__*/styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [{ 'fontSize': \`\${x0}\` }]; @@ -56,7 +56,7 @@ exports[`babel styled component inline composes based on props 1`] = ` 'width': '20px' }]; }); -const H1 = styled('h1', [props => { +const H1 = /*#__PURE__*/styled('h1', [props => { return props.a ? cssA : cssB; }], [fontSize + 'px', props => props.translateX], function createEmotionStyledRules(x0, x1) { return [{ @@ -69,7 +69,7 @@ const H1 = styled('h1', [props => { `; exports[`babel styled component inline dynamic fns 1`] = ` -"const Avatar = styled('img', [], [props => props.theme.borderRadius, props => props.theme.borderColor], function createEmotionStyledRules(x0, x1) { +"const Avatar = /*#__PURE__*/styled('img', [], [props => props.theme.borderRadius, props => props.theme.borderColor], function createEmotionStyledRules(x0, x1) { return [{ 'width': '96px', 'height': '96px', @@ -80,7 +80,7 @@ exports[`babel styled component inline dynamic fns 1`] = ` `; exports[`babel styled component inline function call 1`] = ` -"styled(MyComponent, [], [fontSize + 'px'], function createEmotionStyledRules(x0) { +"/*#__PURE__*/styled(MyComponent, [], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [{ 'fontSize': \`\${x0}\` }]; @@ -89,7 +89,7 @@ exports[`babel styled component inline function call 1`] = ` exports[`babel styled component inline interpolation in different places 1`] = ` " -const H1 = styled('h1', [], [fontSize + 'px', props => props.translateX, something, something, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) { +const H1 = /*#__PURE__*/styled('h1', [], [fontSize + 'px', props => props.translateX, something, something, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) { return [{ 'fontSize': \`\${x0}\`, 'height': '20px', @@ -104,7 +104,7 @@ const H1 = styled('h1', [], [fontSize + 'px', props => props.translateX, somethi `; exports[`babel styled component inline media query 1`] = ` -"const H1 = styled(\\"h1\\", [], [], function createEmotionStyledRules() { +"const H1 = /*#__PURE__*/styled(\\"h1\\", [], [], function createEmotionStyledRules() { return [{ \\"@media print\\": { \\"fontSize\\": \\"10pt\\" @@ -129,7 +129,7 @@ exports[`babel styled component inline media query 1`] = ` `; exports[`babel styled component inline more than 10 dynamic values 1`] = ` -"const H1 = styled('h1', [], ['underline', 54, 'white', 'black', 'block', '3px', '25px', '500px', 100, '18px', 'center', p => p.theme.blue], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) { +"const H1 = /*#__PURE__*/styled('h1', [], ['underline', 54, 'white', 'black', 'block', '3px', '25px', '500px', 100, '18px', 'center', p => p.theme.blue], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) { return [{ 'WebkitTextDecoration': \`\${x0}\`, 'textDecoration': \`\${x0}\`, @@ -159,7 +159,7 @@ exports[`babel styled component inline name is correct with no identifier 1`] = `; exports[`babel styled component inline nested 1`] = ` -"const H1 = styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { +"const H1 = /*#__PURE__*/styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { return [{ 'fontSize': \`\${x0}\`, '& div': { @@ -173,7 +173,7 @@ exports[`babel styled component inline nested 1`] = ` `; exports[`babel styled component inline no dynamic 1`] = ` -"styled(\\"h1\\", [], [], function createEmotionStyledRules() { +"/*#__PURE__*/styled(\\"h1\\", [], [], function createEmotionStyledRules() { return [{ \\"color\\": \\"blue\\" }]; @@ -181,7 +181,7 @@ exports[`babel styled component inline no dynamic 1`] = ` `; exports[`babel styled component inline no use 1`] = ` -"styled(\\"h1\\", [], [], function createEmotionStyledRules() { +"/*#__PURE__*/styled(\\"h1\\", [], [], function createEmotionStyledRules() { return [{}]; });" `; @@ -229,10 +229,10 @@ const H1 = styled('h1', [{ exports[`babel styled component inline styled component as selector 1`] = ` " -const SomeComponent = styled(\\"div\\", [], [], function createEmotionStyledRules() { +const SomeComponent = /*#__PURE__*/styled(\\"div\\", [], [], function createEmotionStyledRules() { return [{}]; }); -styled(\\"h1\\", [], [SomeComponent], function createEmotionStyledRules(x0) { +/*#__PURE__*/styled(\\"h1\\", [], [SomeComponent], function createEmotionStyledRules(x0) { return [{ \\"color\\": \\"blue\\", [\`.\${x0}\`]: { From 1568141314ef4304d0bd851807c5b3290a6e7f87 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 21:35:29 +1000 Subject: [PATCH 51/57] Remove prefixAst --- src/babel.js | 102 ++----------------- test/__snapshots__/react.test.js.snap | 8 ++ test/babel/__snapshots__/macro.test.js.snap | 4 +- test/babel/__snapshots__/styled.test.js.snap | 38 +++---- 4 files changed, 40 insertions(+), 112 deletions(-) diff --git a/src/babel.js b/src/babel.js index b6f6b2fc2..de484159e 100644 --- a/src/babel.js +++ b/src/babel.js @@ -2,10 +2,8 @@ import fs from 'fs' import { basename } from 'path' import { touchSync } from 'touch' -import postcssJs from 'postcss-js' -import autoprefixer from 'autoprefixer' import { inline } from './inline' -import { parseCSS, expandCSSFallbacks } from './parser' +import { parseCSS } from './parser' import { getIdentifierName } from './babel-utils' import { map } from './utils' import cssProps from './css-prop' @@ -129,84 +127,10 @@ export function buildStyledObjectCallExpression (path, identifier, t) { : t.stringLiteral(path.node.callee.property.name) return t.callExpression(identifier, [ tag, - t.arrayExpression(prefixAst(path.node.arguments, t)) + t.arrayExpression(buildProcessedStylesFromObjectAST(path.node.arguments, t)) ]) } -const prefixer = postcssJs.sync([autoprefixer]) -function prefixAst (args, t) { - function isLiteral (value) { - return ( - t.isStringLiteral(value) || - t.isNumericLiteral(value) || - t.isBooleanLiteral(value) - ) - } - - if (Array.isArray(args)) { - return args.map(element => prefixAst(element, t)) - } - - if (t.isObjectExpression(args)) { - let properties = [] - args.properties.forEach(property => { - // nested objects - if (t.isObjectExpression(property.value)) { - const key = t.isStringLiteral(property.key) - ? t.stringLiteral(property.key.value) - : t.identifier(property.key.name) - return properties.push( - t.objectProperty(key, prefixAst(property.value, t)) - ) - - // literal value or array of literal values - } else if ( - isLiteral(property.value) || - (t.isArrayExpression(property.value) && - property.value.elements.every(isLiteral)) - ) { - // handle array values: { display: ['flex', 'block'] } - const propertyValue = t.isArrayExpression(property.value) - ? property.value.elements.map(element => element.value) - : property.value.value - - const style = { [property.key.name]: propertyValue } - const prefixedStyle = expandCSSFallbacks(prefixer(style)) - - for (let k in prefixedStyle) { - const key = t.isStringLiteral(property.key) - ? t.stringLiteral(k) - : t.identifier(k) - const val = prefixedStyle[k] - let value - - if (typeof val === 'number') { - value = t.numericLiteral(val) - } else if (typeof val === 'string') { - value = t.stringLiteral(val) - } else if (Array.isArray(val)) { - value = t.arrayExpression(val.map(i => t.stringLiteral(i))) - } - - properties.push(t.objectProperty(key, value)) - } - - // expressions - } else { - properties.push(property) - } - }) - - return t.objectExpression(properties) - } - - if (t.isArrayExpression(args)) { - return t.arrayExpression(prefixAst(args.elements, t)) - } - - return args -} - function buildProcessedStylesFromObjectAST (objectAST, t) { if (t.isObjectExpression(objectAST)) { const astObject = ASTObject.fromAST(objectAST, t) @@ -214,6 +138,13 @@ function buildProcessedStylesFromObjectAST (objectAST, t) { astObject.obj = styles return astObject.toAST() } + if (t.isArrayExpression(objectAST)) { + return t.arrayExpression(buildProcessedStylesFromObjectAST(objectAST.elements, t)) + } + if (Array.isArray(objectAST)) { + return map(objectAST, obj => buildProcessedStylesFromObjectAST(obj, t)) + } + return objectAST } @@ -292,19 +223,8 @@ export default function (babel) { !path.node.arguments[1] ) { const argWithStyles = path.node.arguments[0] - if (t.isObjectExpression(argWithStyles)) { - const styles = buildProcessedStylesFromObjectAST(argWithStyles, t) - path.replaceWith(t.callExpression(t.identifier('css'), [styles])) - } else if (t.isArrayExpression(argWithStyles)) { - const processedStyles = map(argWithStyles.elements, styles => - buildProcessedStylesFromObjectAST(styles, t) - ) - path.replaceWith( - t.callExpression(t.identifier('css'), [ - t.arrayExpression(processedStyles) - ]) - ) - } + const styles = buildProcessedStylesFromObjectAST(argWithStyles, t) + path.replaceWith(t.callExpression(t.identifier('css'), [styles])) } } catch (e) { throw path.buildCodeFrameError(e) diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 6880587bf..39e0d26a5 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -223,6 +223,14 @@ exports[`styled input placeholder 1`] = ` `; exports[`styled input placeholder object 1`] = ` +.glamor-0::-webkit-input-placeholder { + background-color: green; +} + +.glamor-0:-ms-input-placeholder { + background-color: green; +} + .glamor-0::placeholder { background-color: green; } diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index 203e9872f..6519e8ab3 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -139,7 +139,7 @@ exports[`babel macro styled object function 1`] = ` "import _styled from '../../src/react'; const SomeComponent = _styled('div', [{ - display: '-webkit-box; display: -ms-flexbox; display: flex' + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]);" `; @@ -147,7 +147,7 @@ exports[`babel macro styled object member 1`] = ` "import _styled from '../../src/react'; const SomeComponent = _styled('div', [{ - display: '-webkit-box; display: -ms-flexbox; display: flex' + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]);" `; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index 4d940e25e..7740df455 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -196,7 +196,7 @@ const H1 = styled('h1', ['some-class', props => ({ exports[`babel styled component inline objects based on props 1`] = ` " const H1 = styled('h1', [{ - padding: '10px' + 'padding': '10px' }, props => ({ display: props.display })]);" @@ -205,22 +205,22 @@ const H1 = styled('h1', [{ exports[`babel styled component inline objects fn call 1`] = ` " const H1 = styled('h1', [{ - display: '-webkit-box; display: -ms-flexbox; display: flex' + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }]);" `; exports[`babel styled component inline objects prefixed 1`] = ` " const H1 = styled('h1', [{ - borderRadius: '50%', - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: '-webkit-box; display: -ms-flexbox; display: flex', + 'borderRadius': '50%', + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' } }, props => { padding: props.padding; @@ -245,15 +245,15 @@ const SomeComponent = /*#__PURE__*/styled(\\"div\\", [], [], function createEmot exports[`babel styled component inline styled objects prefixed 1`] = ` " const H1 = styled('h1', [{ - borderRadius: '50%', - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: '-webkit-box; display: -ms-flexbox; display: flex', + 'borderRadius': '50%', + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' } }, props => ({ display: props.display @@ -263,7 +263,7 @@ const H1 = styled('h1', [{ exports[`babel styled component inline styled. objects 1`] = ` " const H1 = styled(\\"h1\\", [{ - padding: \\"10px\\" + \\"padding\\": \\"10px\\" }, props => ({ display: props.display })]);" From a98de8191704be0117877158aa45af792bda3fa2 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 21:55:27 +1000 Subject: [PATCH 52/57] Prefix object styles in the macro --- src/babel.js | 17 ++++++++--------- src/macro.js | 4 +++- test/babel/__snapshots__/macro.test.js.snap | 4 +++- test/macro/__snapshots__/css.test.js.snap | 7 ++++++- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/babel.js b/src/babel.js index de484159e..77ca6db23 100644 --- a/src/babel.js +++ b/src/babel.js @@ -148,6 +148,12 @@ function buildProcessedStylesFromObjectAST (objectAST, t) { return objectAST } +export function replaceCssObjectCallExpression (path, identifier, t) { + const argWithStyles = path.node.arguments[0] + const styles = buildProcessedStylesFromObjectAST(argWithStyles, t) + path.replaceWith(t.callExpression(identifier, [styles])) +} + const visited = Symbol('visited') export default function (babel) { @@ -216,15 +222,8 @@ export default function (babel) { buildStyledObjectCallExpression(path, identifier, t) ) } - - if ( - t.isCallExpression(path.node) && - path.node.callee.name === 'css' && - !path.node.arguments[1] - ) { - const argWithStyles = path.node.arguments[0] - const styles = buildProcessedStylesFromObjectAST(argWithStyles, t) - path.replaceWith(t.callExpression(t.identifier('css'), [styles])) + if (path.node.callee.name === 'css' && !path.node.arguments[1]) { + replaceCssObjectCallExpression(path, t.identifier('css'), t) } } catch (e) { throw path.buildCodeFrameError(e) diff --git a/src/macro.js b/src/macro.js index f0f2560eb..fdfca7e02 100644 --- a/src/macro.js +++ b/src/macro.js @@ -1,4 +1,4 @@ -import { replaceCssWithCallExpression } from './babel' +import { replaceCssWithCallExpression, replaceCssObjectCallExpression } from './babel' import { buildMacroRuntimeNode, addRuntimeImports } from './babel-utils' import { forEach, keys } from './utils' @@ -53,6 +53,8 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isTemplateLiteral(path.node.quasi) ) { replaceCssWithCallExpression(path, runtimeNode, state, t) + } else if (!path.node.arguments[1]) { + replaceCssObjectCallExpression(path, runtimeNode, t) } else { cssReference.replaceWith(runtimeNode) } diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index 6519e8ab3..b322ebde3 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -19,7 +19,9 @@ exports[`babel macro css 1`] = ` exports[`babel macro css object 1`] = ` "import { css as _css } from '../../src'; -const cls1 = _css({ display: 'flex' });" +const cls1 = _css({ + 'display': '-webkit-box; display: -ms-flexbox; display: flex' +});" `; exports[`babel macro flush 1`] = ` diff --git a/test/macro/__snapshots__/css.test.js.snap b/test/macro/__snapshots__/css.test.js.snap index 6ca7d7cad..e9b97a96f 100644 --- a/test/macro/__snapshots__/css.test.js.snap +++ b/test/macro/__snapshots__/css.test.js.snap @@ -17,7 +17,10 @@ exports[`css macro composes 1`] = ` exports[`css macro composes with objects 1`] = ` .glamor-0 { - display: flex,block; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + display: block; width: 30px; height: calc(40vw - 50px); -webkit-box-pack: center; @@ -80,6 +83,8 @@ exports[`css macro handles more than 10 dynamic properties 1`] = ` exports[`css macro handles objects 1`] = ` .glamor-0 { + display: -webkit-box; + display: -ms-flexbox; display: flex; } From 1e88e3459e27c1f91bedffd2540203e50bc6967c Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 22:03:04 +1000 Subject: [PATCH 53/57] Add tests for calling css without any args --- src/babel.js | 2 +- src/macro.js | 2 +- test/__snapshots__/css.test.js.snap | 6 ++++++ test/css.test.js | 6 ++++++ test/macro/__snapshots__/css.test.js.snap | 6 ++++++ test/macro/css.test.js | 6 ++++++ 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/babel.js b/src/babel.js index 77ca6db23..6014a9f05 100644 --- a/src/babel.js +++ b/src/babel.js @@ -222,7 +222,7 @@ export default function (babel) { buildStyledObjectCallExpression(path, identifier, t) ) } - if (path.node.callee.name === 'css' && !path.node.arguments[1]) { + if (path.node.callee.name === 'css' && !path.node.arguments[1] && path.node.arguments[0]) { replaceCssObjectCallExpression(path, t.identifier('css'), t) } } catch (e) { diff --git a/src/macro.js b/src/macro.js index fdfca7e02..87f1e1ef6 100644 --- a/src/macro.js +++ b/src/macro.js @@ -53,7 +53,7 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isTemplateLiteral(path.node.quasi) ) { replaceCssWithCallExpression(path, runtimeNode, state, t) - } else if (!path.node.arguments[1]) { + } else if (!path.node.arguments[1] && path.node.arguments[0]) { replaceCssObjectCallExpression(path, runtimeNode, t) } else { cssReference.replaceWith(runtimeNode) diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 06cf5d893..3d6daee35 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -96,3 +96,9 @@ exports[`css handles objects 1`] = ` className="glamor-0" /> `; + +exports[`css null rule 1`] = ` +<div + className="css-nil" +/> +`; diff --git a/test/css.test.js b/test/css.test.js index 25f73d783..d223c473d 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -82,4 +82,10 @@ describe('css', () => { const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithGlamor() }) + test('null rule', () => { + const cls1 = css() + + const tree = renderer.create(<div className={cls1} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + }) }) diff --git a/test/macro/__snapshots__/css.test.js.snap b/test/macro/__snapshots__/css.test.js.snap index e9b97a96f..8b4d7e6f0 100644 --- a/test/macro/__snapshots__/css.test.js.snap +++ b/test/macro/__snapshots__/css.test.js.snap @@ -92,3 +92,9 @@ exports[`css macro handles objects 1`] = ` className="glamor-0" /> `; + +exports[`css macro null rule 1`] = ` +<div + className="css-nil" +/> +`; diff --git a/test/macro/css.test.js b/test/macro/css.test.js index 8a9038d9e..c82b5fb0a 100644 --- a/test/macro/css.test.js +++ b/test/macro/css.test.js @@ -77,4 +77,10 @@ describe('css macro', () => { const tree = renderer.create(<div className={cls2} />).toJSON() expect(tree).toMatchSnapshotWithGlamor() }) + test('null rule', () => { + const cls1 = css() + + const tree = renderer.create(<div className={cls1} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + }) }) From af323511d75ac0f19cd5763c3b44a985e376c2e0 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 22:12:21 +1000 Subject: [PATCH 54/57] Tests --- src/macro.js | 4 ++-- test/babel/__snapshots__/css.test.js.snap | 2 +- test/babel/__snapshots__/macro.test.js.snap | 6 ++++++ test/babel/css.test.js | 4 ++-- test/babel/macro.test.js | 12 ++++++++++++ 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/macro.js b/src/macro.js index 87f1e1ef6..9d5ef0b31 100644 --- a/src/macro.js +++ b/src/macro.js @@ -22,7 +22,7 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { ), state, t, - () => {}, + undefined, true ) } @@ -39,7 +39,7 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { buildMacroRuntimeNode(fontFaceReference, state, 'fontFace', t), state, t, - () => {}, + undefined, true ) } diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index 0a65d2543..1de0e3a8e 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -94,7 +94,7 @@ css([{ 'borderRadius': '50%', 'WebkitBoxSizing': 'border-box', 'boxSizing': 'border-box', - 'display': '-webkit-box; display: -ms-flexbox; display: flex', + [\`\${display}\`]: 'flex', ':hover': { 'WebkitTransform': 'scale(1.2)', 'transform': 'scale(1.2)' diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index b322ebde3..9103468fd 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -16,6 +16,12 @@ exports[`babel macro css 1`] = ` });" `; +exports[`babel macro css call with no args 1`] = ` +"import { css as _css } from '../../src'; + +const cls1 = _css();" +`; + exports[`babel macro css object 1`] = ` "import { css as _css } from '../../src'; diff --git a/test/babel/css.test.js b/test/babel/css.test.js index e6b4fde56..2b9706fa5 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -293,8 +293,8 @@ describe('babel css', () => { const basic = ` css([{ borderRadius: '50%', - boxSizing: 'border-box', - display: 'flex', + boxSizing: ['border-box'], + [display]: 'flex', ':hover': { transform: 'scale(1.2)' } diff --git a/test/babel/macro.test.js b/test/babel/macro.test.js index 72c15ca14..ef4b41f1c 100644 --- a/test/babel/macro.test.js +++ b/test/babel/macro.test.js @@ -207,6 +207,18 @@ describe('babel macro', () => { }) expect(code).toMatchSnapshot() }) + test('css call with no args', () => { + const basic = ` + import { css } from '../../src/macro' + const cls1 = css() + ` + const { code } = babel.transform(basic, { + plugins: ['babel-macros'], + filename: __filename, + babelrc: false + }) + expect(code).toMatchSnapshot() + }) test('some import that does not exist', () => { const basic = ` import { thisDoesNotExist } from '../../src/macro' From e8b612c5b944faba6a8c636960896843dce88ac4 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Mon, 24 Jul 2017 22:18:36 +1000 Subject: [PATCH 55/57] More tests --- test/__snapshots__/css.test.js.snap | 18 ++++++++++++++++++ test/css.test.js | 12 +++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 3d6daee35..9e652ab40 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -60,6 +60,24 @@ exports[`css composes with undefined values 1`] = ` /> `; +exports[`css flushes correctly 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +<div + className="glamor-0" +/> +`; + +exports[`css flushes correctly 2`] = ` +<div + className="css-71uu79" +/> +`; + exports[`css handles more than 10 dynamic properties 1`] = ` .glamor-0 { -webkit-text-decoration: underline; diff --git a/test/css.test.js b/test/css.test.js index d223c473d..5f5c00891 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -2,7 +2,7 @@ import React from 'react' import renderer from 'react-test-renderer' import { matcher, serializer } from 'jest-glamor-react' -import { sheet, css } from '../src/index' +import { sheet, css, flush } from '../src/index' expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) @@ -88,4 +88,14 @@ describe('css', () => { const tree = renderer.create(<div className={cls1} />).toJSON() expect(tree).toMatchSnapshotWithGlamor() }) + test('flushes correctly', () => { + const cls1 = css` + display: flex; + ` + const tree = renderer.create(<div className={cls1} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + flush() + const tree2 = renderer.create(<div className={cls1} />).toJSON() + expect(tree2).toMatchSnapshotWithGlamor() + }) }) From 1baa92723e0170ed04f9017e55d834f7e3e01944 Mon Sep 17 00:00:00 2001 From: mitchellhamilton <mitchell@mitchellhamilton.me> Date: Tue, 25 Jul 2017 11:30:57 +1000 Subject: [PATCH 56/57] Prettier and add more tests --- package.json | 2 + src/babel.js | 10 +++- src/macro.js | 5 +- test/__snapshots__/react.test.js.snap | 81 +++++++++++++++++++++++++++ test/react.test.js | 14 +++++ 5 files changed, 109 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 36408feb8..2878d966c 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,8 @@ "babel-preset-stage-0": "^6.24.1", "bundlesize": "^0.5.7", "caniuse-api": "^2.0.0", + "enzyme": "^2.9.1", + "enzyme-to-json": "^1.5.1", "jest": "^20.0.4", "jest-cli": "^20.0.4", "jest-glamor-react": "^2.0.0", diff --git a/src/babel.js b/src/babel.js index 6014a9f05..948992822 100644 --- a/src/babel.js +++ b/src/babel.js @@ -139,7 +139,9 @@ function buildProcessedStylesFromObjectAST (objectAST, t) { return astObject.toAST() } if (t.isArrayExpression(objectAST)) { - return t.arrayExpression(buildProcessedStylesFromObjectAST(objectAST.elements, t)) + return t.arrayExpression( + buildProcessedStylesFromObjectAST(objectAST.elements, t) + ) } if (Array.isArray(objectAST)) { return map(objectAST, obj => buildProcessedStylesFromObjectAST(obj, t)) @@ -222,7 +224,11 @@ export default function (babel) { buildStyledObjectCallExpression(path, identifier, t) ) } - if (path.node.callee.name === 'css' && !path.node.arguments[1] && path.node.arguments[0]) { + if ( + path.node.callee.name === 'css' && + !path.node.arguments[1] && + path.node.arguments[0] + ) { replaceCssObjectCallExpression(path, t.identifier('css'), t) } } catch (e) { diff --git a/src/macro.js b/src/macro.js index 9d5ef0b31..979cc5343 100644 --- a/src/macro.js +++ b/src/macro.js @@ -1,4 +1,7 @@ -import { replaceCssWithCallExpression, replaceCssObjectCallExpression } from './babel' +import { + replaceCssWithCallExpression, + replaceCssObjectCallExpression +} from './babel' import { buildMacroRuntimeNode, addRuntimeImports } from './babel-utils' import { forEach, keys } from './utils' diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 39e0d26a5..0a1233d60 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -37,6 +37,87 @@ exports[`styled call expression 1`] = ` </h1> `; +exports[`styled change theme 1`] = ` +.glamor-0 { + color: green; +} + +<TestComponent + renderChild={true} + theme={ + Object { + "primary": "green", + } + } +> + <ThemeProvider + theme={ + Object { + "primary": "green", + } + } + > + <styled(Component)> + <div + className="glamor-0" + > + this will be green then pink + </div> + </styled(Component)> + </ThemeProvider> +</TestComponent> +`; + +exports[`styled change theme 2`] = ` +.glamor-0 { + color: pink; +} + +<TestComponent + renderChild={true} + theme={ + Object { + "primary": "pink", + } + } +> + <ThemeProvider + theme={ + Object { + "primary": "pink", + } + } + > + <styled(Component)> + <div + className="glamor-0" + > + this will be green then pink + </div> + </styled(Component)> + </ThemeProvider> +</TestComponent> +`; + +exports[`styled change theme 3`] = ` +<TestComponent + renderChild={false} + theme={ + Object { + "primary": "pink", + } + } +> + <ThemeProvider + theme={ + Object { + "primary": "pink", + } + } + /> +</TestComponent> +`; + exports[`styled component as selector 1`] = ` .glamor-0 { font-size: 20px; diff --git a/test/react.test.js b/test/react.test.js index 282a6e92c..5d2ba51b9 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -5,6 +5,8 @@ import { matcher, serializer } from 'jest-glamor-react' import { css, sheet } from '../src/index' import styled from '../src/react' import { ThemeProvider } from '../src/react/theming' +import { mount } from 'enzyme' +import enzymeToJson from 'enzyme-to-json' import { lighten, hiDPI, modularScale } from 'polished' @@ -427,6 +429,18 @@ describe('styled', () => { expect(tree).toMatchSnapshotWithGlamor() }) + test('change theme', () => { + const Div = styled.div` + color: ${props => props.theme.primary} + ` + const TestComponent = (props) => (<ThemeProvider theme={props.theme}>{props.renderChild ? <Div>this will be green then pink</Div> : null}</ThemeProvider>) + const wrapper = mount(<TestComponent renderChild theme={{ primary: 'green' }} />) + expect(enzymeToJson(wrapper)).toMatchSnapshotWithGlamor() + wrapper.setProps({ theme: { primary: 'pink' } }) + expect(enzymeToJson(wrapper)).toMatchSnapshotWithGlamor() + wrapper.setProps({ renderChild: false }) + expect(enzymeToJson(wrapper)).toMatchSnapshotWithGlamor() + }) test('throws if undefined is passed as the component', () => { expect( () => styled(undefined)`display: flex;` From 1b7036ad9a5d670f62b0fb279580cfb224941eb0 Mon Sep 17 00:00:00 2001 From: Kye Hohenberger <kye@sideway.com> Date: Mon, 24 Jul 2017 20:44:12 -0600 Subject: [PATCH 57/57] Add tests for dynamic keys --- src/ast-object.js | 7 ++++--- test/__snapshots__/css.test.js.snap | 11 +++++++++++ test/babel/__snapshots__/css.test.js.snap | 8 ++++++++ test/babel/css.test.js | 15 ++++++++++++++- test/css.test.js | 9 +++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/ast-object.js b/src/ast-object.js index 9245cf265..a19a58ce1 100644 --- a/src/ast-object.js +++ b/src/ast-object.js @@ -150,7 +150,7 @@ export default class ASTObject { } else if (t.isTemplateLiteral(node)) { const strs = node.quasis.map(x => x.value.cooked) const exprs = node.expressions - const value = reduce( + return reduce( strs, (arr, str, i) => { arr.push(str) @@ -164,7 +164,6 @@ export default class ASTObject { ) .join('') .trim() - return value } expressions.push(node) @@ -173,6 +172,7 @@ export default class ASTObject { function toObj (astObj) { let obj = {} + forEach(astObj.properties, property => { // nested objects let key @@ -193,8 +193,9 @@ export default class ASTObject { return obj } + const obj = toObj(astObj) return new ASTObject( - toObj(astObj), + obj, expressions, 0, // composesCount: we should support this, t diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 9e652ab40..dfaec41dc 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -60,6 +60,17 @@ exports[`css composes with undefined values 1`] = ` /> `; +exports[`css computed key is only dynamic 1`] = ` +.glamor-0 { + font-size: 10px; + width: 20px; +} + +<div + className="glamor-0" +/> +`; + exports[`css flushes correctly 1`] = ` .glamor-0 { display: -webkit-box; diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index 1de0e3a8e..0529276a4 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -88,6 +88,14 @@ exports[`babel css extract css basic 2`] = ` }" `; +exports[`babel css extract dynamic property objects 1`] = ` +" +css({ + 'fontSize': '10px', + [\`w\${'idth'}\`]: '20px' +});" +`; + exports[`babel css extract prefixed array of objects 1`] = ` " css([{ diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 2b9706fa5..1b343a6a6 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -32,7 +32,7 @@ describe('babel css', () => { display: 'flex'; } \`` - const {code} = babel.transform(basic, { + const { code } = babel.transform(basic, { plugins: [[plugin]] }) expect(code).toMatchSnapshot() @@ -289,6 +289,19 @@ describe('babel css', () => { expect(code).toMatchSnapshot() }) + test('dynamic property objects', () => { + const basic = ` + css({ + fontSize: 10, + [\`w$\{'idth'}\`]: 20 + }) + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) + test('prefixed array of objects', () => { const basic = ` css([{ diff --git a/test/css.test.js b/test/css.test.js index 5f5c00891..fbbd69da4 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -60,6 +60,15 @@ describe('css', () => { expect(tree).toMatchSnapshotWithGlamor() }) + test('computed key is only dynamic', () => { + const cls1 = css({ + fontSize: 10, + [`w${'idth'}`]: 20 + }) + const tree = renderer.create(<div className={cls1} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + }) + test('composes with objects', () => { const cls1 = css({ display: ['flex', 'block'],