From f4946d1e7774ea7e51916fe73246c3a3c67ce556 Mon Sep 17 00:00:00 2001 From: mitchellhamilton Date: Sun, 30 Jul 2017 14:21:18 +1000 Subject: [PATCH 1/2] Fix prefixed selectors in objects Closes #201 --- src/ast-object.js | 25 +++++-- test/__snapshots__/react.test.js.snap | 12 ++++ test/babel/__snapshots__/css.test.js.snap | 80 +++++++++++++++++++++++ test/babel/css.test.js | 40 ++++++++++++ 4 files changed, 152 insertions(+), 5 deletions(-) diff --git a/src/ast-object.js b/src/ast-object.js index 1fbedd9c5..24270c7cf 100644 --- a/src/ast-object.js +++ b/src/ast-object.js @@ -20,13 +20,28 @@ function prefixAst (args, t) { // nested objects if (t.isObjectExpression(property.value)) { const key = property.computed - ? property.key - : t.isStringLiteral(property.key) - ? t.stringLiteral(property.key.value) - : t.identifier(property.key.name) + ? property.key + : t.isStringLiteral(property.key) + ? t.stringLiteral(property.key.value) + : t.identifier(property.key.name) + + const prefixedPseudoSelectors = { + '::placeholder': ['::-webkit-input-placeholder', '::-moz-placeholder', ':-ms-input-placeholder'], + ':fullscreen': [':-webkit-full-screen', ':-moz-full-screen', ':-ms-fullscreen'] + } + + const prefixedValue = prefixAst(property.value, t) + + if (!property.computed) { + if (prefixedPseudoSelectors[key.value]) { + forEach(prefixedPseudoSelectors[key.value], (prefixedKey) => { + properties.push(t.objectProperty(t.stringLiteral(prefixedKey), prefixedValue, false)) + }) + } + } return properties.push( - t.objectProperty(key, prefixAst(property.value, t), property.computed) + t.objectProperty(key, prefixedValue, property.computed) ) // literal value or array of literal values diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index ccddacbf0..1957916c9 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -356,6 +356,18 @@ exports[`styled input placeholder 1`] = ` `; exports[`styled input placeholder object 1`] = ` +.glamor-1::-webkit-input-placeholder { + background-color: green; +} + +.glamor-1::-moz-placeholder { + background-color: green; +} + +.glamor-1:-ms-input-placeholder { + background-color: green; +} + .glamor-1::placeholder { background-color: green; } diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index 4eac4257b..038f4e44c 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -129,6 +129,86 @@ css({ });" `; +exports[`babel css inline ::placeholder 1`] = ` +" +const cls1 = css({ + '::-webkit-input-placeholder': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }, + '::-moz-placeholder': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }, + ':-ms-input-placeholder': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }, + '::placeholder': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + } +}); +const cls2 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + '::-webkit-input-placeholder': { + 'color': 'green', + 'display': '-webkit-box; display: flex' + }, + ':-ms-input-placeholder': { + 'color': 'green', + 'display': '-ms-flexbox; display: flex' + }, + '::placeholder': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + } + }]; +});" +`; + +exports[`babel css inline :fullscreen 1`] = ` +" +const cls1 = css({ + ':-webkit-full-screen': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }, + ':-moz-full-screen': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }, + ':-ms-fullscreen': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }, + ':fullscreen': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + } +}); +const cls2 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + ':-webkit-full-screen': { + 'color': 'green', + 'display': '-webkit-box; display: flex' + }, + ':-moz-full-screen': { + 'color': 'green', + 'display': 'flex' + }, + ':-ms-fullscreen': { + 'color': 'green', + 'display': '-ms-flexbox; display: flex' + }, + ':fullscreen': { + 'color': 'green', + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + } + }]; +});" +`; + exports[`babel css inline array of objects 1`] = ` " const cls2 = css([{ diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 1b343a6a6..abc83a4b9 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -85,6 +85,46 @@ describe('babel css', () => { expect(code).toMatchSnapshot() }) + test('::placeholder', () => { + const basic = ` + const cls1 = css({ + '::placeholder': { + color: 'green', + display: 'flex' + } + }) + const cls2 = css\` + ::placeholder { + color: green; + display: flex; + } + \` + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) + test(':fullscreen', () => { + const basic = ` + const cls1 = css({ + ':fullscreen': { + color: 'green', + display: 'flex' + } + }) + const cls2 = css\` + :fullscreen { + color: green; + display: flex; + } + \` + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) test('only composes', () => { const basic = ` const cls1 = css\` From ed86ab959f17ae0c96873c75e03722fcd80ae146 Mon Sep 17 00:00:00 2001 From: mitchellhamilton Date: Sun, 30 Jul 2017 14:23:52 +1000 Subject: [PATCH 2/2] Prettier --- src/ast-object.js | 30 ++++++++++++++++++++++-------- src/babel.js | 23 ++++++++++++++++------- src/macro-styled.js | 4 +++- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/ast-object.js b/src/ast-object.js index 24270c7cf..4a90a5ba0 100644 --- a/src/ast-object.js +++ b/src/ast-object.js @@ -20,22 +20,36 @@ function prefixAst (args, t) { // nested objects if (t.isObjectExpression(property.value)) { const key = property.computed - ? property.key - : t.isStringLiteral(property.key) - ? t.stringLiteral(property.key.value) - : t.identifier(property.key.name) + ? property.key + : t.isStringLiteral(property.key) + ? t.stringLiteral(property.key.value) + : t.identifier(property.key.name) const prefixedPseudoSelectors = { - '::placeholder': ['::-webkit-input-placeholder', '::-moz-placeholder', ':-ms-input-placeholder'], - ':fullscreen': [':-webkit-full-screen', ':-moz-full-screen', ':-ms-fullscreen'] + '::placeholder': [ + '::-webkit-input-placeholder', + '::-moz-placeholder', + ':-ms-input-placeholder' + ], + ':fullscreen': [ + ':-webkit-full-screen', + ':-moz-full-screen', + ':-ms-fullscreen' + ] } const prefixedValue = prefixAst(property.value, t) if (!property.computed) { if (prefixedPseudoSelectors[key.value]) { - forEach(prefixedPseudoSelectors[key.value], (prefixedKey) => { - properties.push(t.objectProperty(t.stringLiteral(prefixedKey), prefixedValue, false)) + forEach(prefixedPseudoSelectors[key.value], prefixedKey => { + properties.push( + t.objectProperty( + t.stringLiteral(prefixedKey), + prefixedValue, + false + ) + ) }) } } diff --git a/src/babel.js b/src/babel.js index 04512d5e3..66c8fc69e 100644 --- a/src/babel.js +++ b/src/babel.js @@ -1,6 +1,12 @@ // @flow weak import fs from 'fs' -import { basename, dirname, join as pathJoin, sep as pathSep, relative } from 'path' +import { + basename, + dirname, + join as pathJoin, + sep as pathSep, + relative +} from 'path' import { touchSync } from 'touch' import { inline } from './inline' import { parseCSS } from './parser' @@ -83,7 +89,7 @@ export function replaceCssWithCallExpression ( // babel-plugin-styled-components // https://github.com/styled-components/babel-plugin-styled-components/blob/37a13e9c21c52148ce6e403100df54c0b1561a88/src/visitors/displayNameAndId.js#L49-L93 -const findModuleRoot = (filename) => { +const findModuleRoot = filename => { if (!filename || filename === 'unknown') { return null } @@ -100,7 +106,7 @@ const findModuleRoot = (filename) => { const FILE_HASH = 'emotion-file-hash' const COMPONENT_POSITION = 'emotion-component-position' -const getFileHash = (state) => { +const getFileHash = state => { const { file } = state // hash calculation is costly due to fs operations, so we'll cache it per file. if (file.get(FILE_HASH)) { @@ -109,10 +115,13 @@ const getFileHash = (state) => { const filename = file.opts.filename // find module root directory const moduleRoot = findModuleRoot(filename) - const filePath = moduleRoot && relative(moduleRoot, filename).replace(pathSep, '/') + const filePath = + moduleRoot && relative(moduleRoot, filename).replace(pathSep, '/') let moduleName = '' if (moduleRoot) { - const packageJsonString = fs.readFileSync(pathJoin(moduleRoot, 'package.json')) + const packageJsonString = fs.readFileSync( + pathJoin(moduleRoot, 'package.json') + ) if (packageJsonString) { try { moduleName = JSON.parse(packageJsonString).name @@ -126,13 +135,13 @@ const getFileHash = (state) => { return fileHash } -const getNextId = (state) => { +const getNextId = state => { const id = state.file.get(COMPONENT_POSITION) || 0 state.file.set(COMPONENT_POSITION, id + 1) return id } -const getComponentId = (state) => { +const getComponentId = state => { // Prefix the identifier with css- because CSS classes cannot start with a number // Also in snapshots with jest-glamor-react the hash will be replaced with an index return `css-${getFileHash(state)}${getNextId(state)}` diff --git a/src/macro-styled.js b/src/macro-styled.js index d11f10539..7244eb9b7 100644 --- a/src/macro-styled.js +++ b/src/macro-styled.js @@ -47,7 +47,9 @@ module.exports = function macro (options) { (t.isCallExpression(path.node.callee) || t.isIdentifier(path.node.callee.object)) ) { - path.replaceWith(buildStyledObjectCallExpression(path, state, runtimeNode, t)) + path.replaceWith( + buildStyledObjectCallExpression(path, state, runtimeNode, t) + ) } }) }