diff --git a/package.json b/package.json index 2be60fe..4cdd6c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-to-react-native", - "version": "2.3.0", + "version": "2.3.1", "description": "Convert CSS text to a React Native stylesheet object", "main": "index.js", "scripts": { diff --git a/src/__tests__/border.js b/src/__tests__/border.js index 369a112..b15241c 100644 --- a/src/__tests__/border.js +++ b/src/__tests__/border.js @@ -1,5 +1,13 @@ import transformCss from '..' +it('transforms border none', () => { + expect(transformCss([['border', 'none']])).toEqual({ + borderWidth: 0, + borderColor: 'black', + borderStyle: 'solid', + }) +}) + it('transforms border shorthand', () => { expect(transformCss([['border', '2px dashed #f00']])).toEqual({ borderWidth: 2, diff --git a/src/index.js b/src/index.js index 75a1486..a6fbe41 100644 --- a/src/index.js +++ b/src/index.js @@ -50,13 +50,11 @@ const transformShorthandValue = export const getStylesForProperty = (propName, inputValue, allowShorthand) => { const isRawValue = allowShorthand === false || !(propName in transforms) - const propValue = isRawValue - ? transformRawValue(inputValue) + const propValues = isRawValue + ? { [propName]: transformRawValue(inputValue) } : transformShorthandValue(propName, inputValue.trim()) - return propValue && propValue.$merge - ? propValue.$merge - : { [propName]: propValue } + return propValues } export const getPropertyName = propName => { diff --git a/src/transforms/border.js b/src/transforms/border.js new file mode 100644 index 0000000..e7a348e --- /dev/null +++ b/src/transforms/border.js @@ -0,0 +1,48 @@ +import { regExpToken, tokens } from '../tokenTypes' + +const { NONE, COLOR, LENGTH, UNSUPPORTED_LENGTH_UNIT, SPACE } = tokens + +const BORDER_STYLE = regExpToken(/^(solid|dashed|dotted)$/) + +const defaultBorderWidth = 1 +const defaultBorderColor = 'black' +const defaultBorderStyle = 'solid' + +export default tokenStream => { + let borderWidth + let borderColor + let borderStyle + + if (tokenStream.matches(NONE)) { + tokenStream.expectEmpty() + return { borderWidth: 0, borderColor: 'black', borderStyle: 'solid' } + } + + let partsParsed = 0 + while (partsParsed < 3 && tokenStream.hasTokens()) { + if (partsParsed !== 0) tokenStream.expect(SPACE) + + if ( + (borderWidth === undefined && tokenStream.matches(LENGTH)) || + tokenStream.matches(UNSUPPORTED_LENGTH_UNIT) + ) { + borderWidth = tokenStream.lastValue + } else if (borderColor === undefined && tokenStream.matches(COLOR)) { + borderColor = tokenStream.lastValue + } else if (borderStyle === undefined && tokenStream.matches(BORDER_STYLE)) { + borderStyle = tokenStream.lastValue + } else { + tokenStream.throw() + } + + partsParsed += 1 + } + + tokenStream.expectEmpty() + + if (borderWidth === undefined) borderWidth = defaultBorderWidth + if (borderColor === undefined) borderColor = defaultBorderColor + if (borderStyle === undefined) borderStyle = defaultBorderStyle + + return { borderWidth, borderColor, borderStyle } +} diff --git a/src/transforms/boxShadow.js b/src/transforms/boxShadow.js index f100a3f..b581658 100644 --- a/src/transforms/boxShadow.js +++ b/src/transforms/boxShadow.js @@ -3,11 +3,9 @@ import { parseShadow } from './util' export default tokenStream => { const { offset, radius, color } = parseShadow(tokenStream) return { - $merge: { - shadowOffset: offset, - shadowRadius: radius, - shadowColor: color, - shadowOpacity: 1, - }, + shadowOffset: offset, + shadowRadius: radius, + shadowColor: color, + shadowOpacity: 1, } } diff --git a/src/transforms/flex.js b/src/transforms/flex.js index b54c06f..df5c5fd 100644 --- a/src/transforms/flex.js +++ b/src/transforms/flex.js @@ -13,12 +13,12 @@ export default tokenStream => { if (tokenStream.matches(NONE)) { tokenStream.expectEmpty() - return { $merge: { flexGrow: 0, flexShrink: 0, flexBasis: 'auto' } } + return { flexGrow: 0, flexShrink: 0, flexBasis: 'auto' } } tokenStream.saveRewindPoint() if (tokenStream.matches(AUTO) && !tokenStream.hasTokens()) { - return { $merge: { flexGrow: 1, flexShrink: 1, flexBasis: 'auto' } } + return { flexGrow: 1, flexShrink: 1, flexBasis: 'auto' } } tokenStream.rewind() @@ -52,5 +52,5 @@ export default tokenStream => { if (flexShrink === undefined) flexShrink = defaultFlexShrink if (flexBasis === undefined) flexBasis = defaultFlexBasis - return { $merge: { flexGrow, flexShrink, flexBasis } } + return { flexGrow, flexShrink, flexBasis } } diff --git a/src/transforms/flexFlow.js b/src/transforms/flexFlow.js new file mode 100644 index 0000000..fccb2c1 --- /dev/null +++ b/src/transforms/flexFlow.js @@ -0,0 +1,39 @@ +import { regExpToken, tokens } from '../tokenTypes' + +const { SPACE } = tokens + +const FLEX_WRAP = regExpToken(/(nowrap|wrap|wrap-reverse)/) +const FLEX_DIRECTION = regExpToken(/(row|row-reverse|column|column-reverse)/) + +const defaultFlexWrap = 'nowrap' +const defaultFlexDirection = 'row' + +export default tokenStream => { + let flexWrap + let flexDirection + + let partsParsed = 0 + while (partsParsed < 2 && tokenStream.hasTokens()) { + if (partsParsed !== 0) tokenStream.expect(SPACE) + + if (flexWrap === undefined && tokenStream.matches(FLEX_WRAP)) { + flexWrap = tokenStream.lastValue + } else if ( + flexDirection === undefined && + tokenStream.matches(FLEX_DIRECTION) + ) { + flexDirection = tokenStream.lastValue + } else { + tokenStream.throw() + } + + partsParsed += 1 + } + + tokenStream.expectEmpty() + + if (flexWrap === undefined) flexWrap = defaultFlexWrap + if (flexDirection === undefined) flexDirection = defaultFlexDirection + + return { flexWrap, flexDirection } +} diff --git a/src/transforms/font.js b/src/transforms/font.js index 92b2229..9ce7d0e 100644 --- a/src/transforms/font.js +++ b/src/transforms/font.js @@ -49,7 +49,7 @@ export default tokenStream => { tokenStream.expect(SPACE) - const fontFamily = parseFontFamily(tokenStream) + const { fontFamily } = parseFontFamily(tokenStream) if (fontStyle === undefined) fontStyle = defaultFontStyle if (fontWeight === undefined) fontWeight = defaultFontWeight @@ -58,5 +58,5 @@ export default tokenStream => { const out = { fontStyle, fontWeight, fontVariant, fontSize, fontFamily } if (lineHeight !== undefined) out.lineHeight = lineHeight - return { $merge: out } + return out } diff --git a/src/transforms/fontFamily.js b/src/transforms/fontFamily.js index 240f271..53b5bac 100644 --- a/src/transforms/fontFamily.js +++ b/src/transforms/fontFamily.js @@ -18,5 +18,5 @@ export default tokenStream => { tokenStream.expectEmpty() - return fontFamily + return { fontFamily } } diff --git a/src/transforms/index.js b/src/transforms/index.js index 0431b2f..1185858 100644 --- a/src/transforms/index.js +++ b/src/transforms/index.js @@ -1,13 +1,15 @@ -import { regExpToken, tokens } from '../tokenTypes' +import { tokens } from '../tokenTypes' +import border from './border' import boxShadow from './boxShadow' import flex from './flex' +import flexFlow from './flexFlow' import font from './font' import fontFamily from './fontFamily' import textShadow from './textShadow' import textDecoration from './textDecoration' import textDecorationLine from './textDecorationLine' import transform from './transform' -import { directionFactory, anyOrderFactory, shadowOffsetFactory } from './util' +import { directionFactory, parseShadowOffset } from './util' const { IDENT, @@ -20,21 +22,7 @@ const { } = tokens const background = tokenStream => ({ - $merge: { backgroundColor: tokenStream.expect(COLOR) }, -}) -const border = anyOrderFactory({ - borderWidth: { - tokens: [LENGTH, UNSUPPORTED_LENGTH_UNIT], - default: 1, - }, - borderColor: { - tokens: [COLOR], - default: 'black', - }, - borderStyle: { - tokens: [regExpToken(/^(solid|dashed|dotted)$/)], - default: 'solid', - }, + backgroundColor: tokenStream.expect(COLOR), }) const borderColor = directionFactory({ types: [WORD], @@ -52,20 +40,18 @@ const margin = directionFactory({ prefix: 'margin', }) const padding = directionFactory({ prefix: 'padding' }) -const flexFlow = anyOrderFactory({ - flexWrap: { - tokens: [regExpToken(/(nowrap|wrap|wrap-reverse)/)], - default: 'nowrap', - }, - flexDirection: { - tokens: [regExpToken(/(row|row-reverse|column|column-reverse)/)], - default: 'row', - }, +const fontVariant = tokenStream => ({ + fontVariant: [tokenStream.expect(IDENT)], +}) +const fontWeight = tokenStream => ({ + fontWeight: tokenStream.expect(WORD), // Also match numbers as strings +}) +const shadowOffset = tokenStream => ({ + shadowOffset: parseShadowOffset(tokenStream), +}) +const textShadowOffset = tokenStream => ({ + textShadowOffset: parseShadowOffset(tokenStream), }) -const fontVariant = tokenStream => [tokenStream.expect(IDENT)] -const fontWeight = tokenStream => tokenStream.expect(WORD) // Also match numbers as strings -const shadowOffset = shadowOffsetFactory() -const textShadowOffset = shadowOffsetFactory() export default { background, diff --git a/src/transforms/textDecoration.js b/src/transforms/textDecoration.js index d287d93..17303d5 100644 --- a/src/transforms/textDecoration.js +++ b/src/transforms/textDecoration.js @@ -45,12 +45,11 @@ export default tokenStream => { didParseFirst = true } - const $merge = { + return { textDecorationLine: line !== undefined ? line : defaultTextDecorationLine, textDecorationColor: color !== undefined ? color : defaultTextDecorationColor, textDecorationStyle: style !== undefined ? style : defaultTextDecorationStyle, } - return { $merge } } diff --git a/src/transforms/textDecorationLine.js b/src/transforms/textDecorationLine.js index 1260175..a629869 100644 --- a/src/transforms/textDecorationLine.js +++ b/src/transforms/textDecorationLine.js @@ -16,5 +16,5 @@ export default tokenStream => { lines.sort().reverse() - return lines.join(' ') + return { textDecorationLine: lines.join(' ') } } diff --git a/src/transforms/textShadow.js b/src/transforms/textShadow.js index 5a1fc50..46f00fd 100644 --- a/src/transforms/textShadow.js +++ b/src/transforms/textShadow.js @@ -3,10 +3,8 @@ import { parseShadow } from './util' export default tokenStream => { const { offset, radius, color } = parseShadow(tokenStream) return { - $merge: { - textShadowOffset: offset, - textShadowRadius: radius, - textShadowColor: color, - }, + textShadowOffset: offset, + textShadowRadius: radius, + textShadowColor: color, } } diff --git a/src/transforms/transform.js b/src/transforms/transform.js index 0d1d061..269cb0e 100644 --- a/src/transforms/transform.js +++ b/src/transforms/transform.js @@ -72,5 +72,5 @@ export default tokenStream => { didParseFirst = true } - return transforms + return { transform: transforms } } diff --git a/src/transforms/util.js b/src/transforms/util.js index a3a8214..b7b616f 100644 --- a/src/transforms/util.js +++ b/src/transforms/util.js @@ -24,55 +24,15 @@ export const directionFactory = ({ const keyFor = n => `${prefix}${directions[n]}${suffix}` - const output = { + return { [keyFor(0)]: top, [keyFor(1)]: right, [keyFor(2)]: bottom, [keyFor(3)]: left, } - - return { $merge: output } -} - -export const anyOrderFactory = (properties, delim = SPACE) => tokenStream => { - const propertyNames = Object.keys(properties) - const values = propertyNames.reduce((accum, propertyName) => { - accum[propertyName] === undefined // eslint-disable-line - return accum - }, {}) - - let numParsed = 0 - while (numParsed < propertyNames.length && tokenStream.hasTokens()) { - if (numParsed) tokenStream.expect(delim) - - const matchedPropertyName = propertyNames.find( - propertyName => - values[propertyName] === undefined && - properties[propertyName].tokens.some(token => - tokenStream.matches(token) - ) - ) - - if (!matchedPropertyName) { - tokenStream.throw() - } else { - values[matchedPropertyName] = tokenStream.lastValue - } - - numParsed += 1 - } - - tokenStream.expectEmpty() - - propertyNames.forEach(propertyName => { - if (values[propertyName] === undefined) - values[propertyName] = properties[propertyName].default - }) - - return { $merge: values } } -export const shadowOffsetFactory = () => tokenStream => { +export const parseShadowOffset = tokenStream => { const width = tokenStream.expect(LENGTH) const height = tokenStream.matches(SPACE) ? tokenStream.expect(LENGTH) : width tokenStream.expectEmpty()