Skip to content

Commit a28e45d

Browse files
authored
Merge pull request #120 from styled-components/housekeeping
Housekeeping
2 parents 6dce838 + 31839b8 commit a28e45d

24 files changed

+3710
-2585
lines changed

.babelrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "presets": ["es2015"] }
1+
{ "presets": [["@babel/preset-env", { "loose": true }]] }

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
/node_modules
22
/index.js
3-
/package-lock.json

.npmrc

-1
This file was deleted.

index.d.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1+
export type StyleTuple = [string, string]
2+
13
export interface Style {
2-
[key: string]: string | number | Style;
4+
[key: string]: string | number | Style
35
}
46

5-
export function getPropertyName(name: string): string;
6-
export function getStylesForProperty(name: string, value: string, allowShorthand?: boolean): Style;
7+
export function getPropertyName(name: string): string
8+
export function getStylesForProperty(
9+
name: string,
10+
value: string,
11+
allowShorthand?: boolean
12+
): Style
713

8-
export default function transform(css: Array<[string, string]>, shorthandBlacklist?: string[]): Style;
14+
export default function transform(
15+
styleTuples: StyleTuple[],
16+
shorthandBlacklist?: string[]
17+
): Style

package.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "css-to-react-native",
3-
"version": "2.3.2",
3+
"version": "3.0.0",
44
"description": "Convert CSS text to a React Native stylesheet object",
55
"main": "index.js",
66
"scripts": {
@@ -37,22 +37,22 @@
3737
"testURL": "http://localhost"
3838
},
3939
"devDependencies": {
40-
"babel-cli": "^6.26.0",
41-
"babel-preset-es2015": "^6.24.1",
40+
"@babel/cli": "^7.6.2",
41+
"@babel/preset-env": "^7.6.2",
4242
"eslint": "^4.17.0",
4343
"eslint-config-airbnb-base": "^12.1.0",
4444
"eslint-config-prettier": "^2.9.0",
4545
"eslint-plugin-import": "^2.8.0",
4646
"eslint-plugin-prettier": "^2.6.0",
47-
"jest": "^22.2.2",
47+
"jest": "^24.9.0",
4848
"lint-staged": "^6.1.0",
49-
"prettier": "^1.10.2",
50-
"rollup": "^0.55.5"
49+
"prettier": "^1.18.2",
50+
"rollup": "^1.22.0"
5151
},
5252
"dependencies": {
5353
"camelize": "^1.0.0",
5454
"css-color-keywords": "^1.0.0",
55-
"postcss-value-parser": "^3.3.0"
55+
"postcss-value-parser": "^4.0.2"
5656
},
5757
"lint-staged": {
5858
"*.js": [

src/__tests__/border.js

+12
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,15 @@ it('transforms border shorthand missing color & style', () => {
7171
borderStyle: 'solid',
7272
})
7373
})
74+
75+
it('transforms border for unsupported units', () => {
76+
expect(transformCss([['border', '3em solid black']])).toEqual({
77+
borderWidth: '3em',
78+
borderColor: 'black',
79+
borderStyle: 'solid',
80+
})
81+
})
82+
83+
it('does not transform border with percentage width', () => {
84+
expect(() => transformCss([['border', '3% solid black']])).toThrow()
85+
})

src/__tests__/flex.js

+16
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ it('transforms flex shorthand with flex-basis set to auto', () => {
7777
})
7878
})
7979

80+
it('transforms flex shorthand with flex-basis set to percent', () => {
81+
expect(transformCss([['flex', '1 2 30%']])).toEqual({
82+
flexGrow: 1,
83+
flexShrink: 2,
84+
flexBasis: '30%',
85+
})
86+
})
87+
88+
it('transforms flex shorthand with flex-basis set to unsupported unit', () => {
89+
expect(transformCss([['flex', '1 2 30em']])).toEqual({
90+
flexGrow: 1,
91+
flexShrink: 2,
92+
flexBasis: '30em',
93+
})
94+
})
95+
8096
it('transforms flex shorthand with flex-basis set to auto appearing first', () => {
8197
expect(transformCss([['flex', 'auto 0 1']])).toEqual({
8298
flexGrow: 0,

src/__tests__/index.js

+56-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,55 @@
11
import transformCss, { getStylesForProperty } from '..'
22

33
it('transforms numbers', () => {
4-
expect(
5-
transformCss([['top', '0'], ['left', '0'], ['right', '0'], ['bottom', '0']])
6-
).toEqual({
7-
top: 0,
8-
left: 0,
9-
right: 0,
10-
bottom: 0,
11-
})
4+
expect(transformCss([['z-index', '0']])).toEqual({ zIndex: 0 })
5+
})
6+
7+
it('warns if missing units on unspecialized transform', () => {
8+
const consoleSpy = jest
9+
.spyOn(global.console, 'warn')
10+
.mockImplementation(() => {
11+
// Silence the warning from the test output
12+
})
13+
14+
transformCss([['top', '1']])
15+
expect(consoleSpy).toHaveBeenCalledWith(
16+
'Expected style "top: 1" to contain units'
17+
)
18+
19+
consoleSpy.mockRestore()
20+
})
21+
22+
it('does not warn for unitless 0 length on unspecialized transform', () => {
23+
const consoleSpy = jest.spyOn(global.console, 'warn')
24+
25+
transformCss([['top', '0']])
26+
expect(consoleSpy).not.toHaveBeenCalled()
27+
28+
consoleSpy.mockRestore()
29+
})
30+
31+
it('warns if adding etraneous units on unspecialized transform', () => {
32+
const consoleSpy = jest
33+
.spyOn(global.console, 'warn')
34+
.mockImplementation(() => {
35+
// Silence the warning from the test output
36+
})
37+
38+
transformCss([['opacity', '1px']])
39+
expect(consoleSpy).toHaveBeenCalledWith(
40+
'Expected style "opacity: 1px" to be unitless'
41+
)
42+
43+
consoleSpy.mockRestore()
44+
})
45+
46+
it('does not warn for unitless 0 length on unitless transform', () => {
47+
const consoleSpy = jest.spyOn(global.console, 'warn')
48+
49+
transformCss([['opacity', '0']])
50+
expect(consoleSpy).not.toHaveBeenCalled()
51+
52+
consoleSpy.mockRestore()
1253
})
1354

1455
it('allows pixels in unspecialized transform', () => {
@@ -86,6 +127,13 @@ it('allows negative values in transformed values', () => {
86127
})
87128
})
88129

130+
it('allows uppercase units', () => {
131+
expect(transformCss([['top', '0PX']])).toEqual({ top: 0 })
132+
expect(transformCss([['transform', 'rotate(30DEG)']])).toEqual({
133+
transform: [{ rotate: '30deg' }],
134+
})
135+
})
136+
89137
it('allows percent values in transformed values', () => {
90138
expect(transformCss([['margin', '10%']])).toEqual({
91139
marginTop: '10%',

src/__tests__/placeContent.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import transformCss from '..'
2+
3+
it('transforms place content', () => {
4+
expect(transformCss([['place-content', 'center center']])).toEqual({
5+
alignContent: 'center',
6+
justifyContent: 'center',
7+
})
8+
})
9+
10+
it('transforms place content with one value', () => {
11+
expect(transformCss([['place-content', 'center']])).toEqual({
12+
alignContent: 'center',
13+
justifyContent: 'stretch',
14+
})
15+
})
16+
17+
it('does not allow justify content without align content', () => {
18+
expect(() => transformCss([['place-content', 'space-evenly']])).toThrow()
19+
})
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
let propertiesWithoutUnits
2+
if (process.env.NODE_ENV !== 'production') {
3+
propertiesWithoutUnits = [
4+
'aspectRatio',
5+
'elevation',
6+
'flexGrow',
7+
'flexShrink',
8+
'opacity',
9+
'shadowOpacity',
10+
'zIndex',
11+
]
12+
}
13+
14+
const devPropertiesWithUnitsRegExp =
15+
propertiesWithoutUnits != null
16+
? new RegExp(propertiesWithoutUnits.join('|'))
17+
: null
18+
19+
export default devPropertiesWithUnitsRegExp

src/index.js

+28-15
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,70 @@
22
import parse from 'postcss-value-parser'
33
import camelizeStyleName from 'camelize'
44
import transforms from './transforms/index'
5+
import devPropertiesWithoutUnitsRegExp from './devPropertiesWithoutUnitsRegExp'
56
import TokenStream from './TokenStream'
67

78
// Note if this is wrong, you'll need to change tokenTypes.js too
8-
const numberOrLengthRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)(?:px)?$/i
9+
const numberOrLengthRe = /^([+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?)(?:px)?$/i
10+
const numberOnlyRe = /^[+-]?(?:\d*\.\d*|[1-9]\d*)(?:e[+-]?\d+)?$/i
911
const boolRe = /^true|false$/i
1012
const nullRe = /^null$/i
1113
const undefinedRe = /^undefined$/i
1214

1315
// Undocumented export
14-
export const transformRawValue = input => {
15-
const value = input.trim()
16+
export const transformRawValue = (propName, value) => {
17+
if (process.env.NODE_ENV !== 'production') {
18+
const needsUnit = !devPropertiesWithoutUnitsRegExp.test(propName)
19+
const isNumberWithoutUnit = numberOnlyRe.test(value)
20+
if (needsUnit && isNumberWithoutUnit) {
21+
// eslint-disable-next-line no-console
22+
console.warn(`Expected style "${propName}: ${value}" to contain units`)
23+
}
24+
if (!needsUnit && value !== '0' && !isNumberWithoutUnit) {
25+
// eslint-disable-next-line no-console
26+
console.warn(`Expected style "${propName}: ${value}" to be unitless`)
27+
}
28+
}
1629

1730
const numberMatch = value.match(numberOrLengthRe)
1831
if (numberMatch !== null) return Number(numberMatch[1])
1932

20-
const boolMatch = input.match(boolRe)
33+
const boolMatch = value.match(boolRe)
2134
if (boolMatch !== null) return boolMatch[0].toLowerCase() === 'true'
2235

23-
const nullMatch = input.match(nullRe)
36+
const nullMatch = value.match(nullRe)
2437
if (nullMatch !== null) return null
2538

26-
const undefinedMatch = input.match(undefinedRe)
39+
const undefinedMatch = value.match(undefinedRe)
2740
if (undefinedMatch !== null) return undefined
2841

2942
return value
3043
}
3144

32-
const baseTransformShorthandValue = (propName, inputValue) => {
33-
const ast = parse(inputValue.trim())
45+
const baseTransformShorthandValue = (propName, value) => {
46+
const ast = parse(value)
3447
const tokenStream = new TokenStream(ast.nodes)
3548
return transforms[propName](tokenStream)
3649
}
3750

3851
const transformShorthandValue =
3952
process.env.NODE_ENV === 'production'
4053
? baseTransformShorthandValue
41-
: (propName, inputValue) => {
54+
: (propName, value) => {
4255
try {
43-
return baseTransformShorthandValue(propName, inputValue)
56+
return baseTransformShorthandValue(propName, value)
4457
} catch (e) {
45-
throw new Error(
46-
`Failed to parse declaration "${propName}: ${inputValue}"`
47-
)
58+
throw new Error(`Failed to parse declaration "${propName}: ${value}"`)
4859
}
4960
}
5061

5162
export const getStylesForProperty = (propName, inputValue, allowShorthand) => {
5263
const isRawValue = allowShorthand === false || !(propName in transforms)
64+
const value = inputValue.trim()
65+
5366
const propValues = isRawValue
54-
? { [propName]: transformRawValue(inputValue) }
55-
: transformShorthandValue(propName, inputValue.trim())
67+
? { [propName]: transformRawValue(propName, value) }
68+
: transformShorthandValue(propName, value)
5669

5770
return propValues
5871
}

src/tokenTypes.js

+24-22
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ const noneRe = /^(none)$/i
3131
const autoRe = /^(auto)$/i
3232
const identRe = /(^-?[_a-z][_a-z0-9-]*$)/i
3333
// Note if these are wrong, you'll need to change index.js too
34-
const numberRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)$/
34+
const numberRe = /^([+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?)$/i
3535
// Note lengthRe is sneaky: you can omit units for 0
36-
const lengthRe = /^(0$|(?:[+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?)(?=px$))/
37-
const unsupportedUnitRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?(ch|em|ex|rem|vh|vw|vmin|vmax|cm|mm|in|pc|pt))$/
38-
const angleRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?(?:deg|rad))$/
39-
const percentRe = /^([+-]?(?:\d*\.)?\d+(?:[Ee][+-]?\d+)?%)$/
36+
const lengthRe = /^(0$|(?:[+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?)(?=px$))/i
37+
const unsupportedUnitRe = /^([+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?(ch|em|ex|rem|vh|vw|vmin|vmax|cm|mm|in|pc|pt))$/i
38+
const angleRe = /^([+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?(?:deg|rad))$/i
39+
const percentRe = /^([+-]?(?:\d*\.)?\d+(?:e[+-]?\d+)?%)$/i
4040

4141
const noopToken = predicate => node => (predicate(node) ? '<token>' : null)
4242

@@ -54,20 +54,22 @@ export const regExpToken = (regExp, transform = String) => node => {
5454
return value
5555
}
5656

57-
export const tokens = {
58-
SPACE: noopToken(node => node.type === 'space'),
59-
SLASH: noopToken(node => node.type === 'div' && node.value === '/'),
60-
COMMA: noopToken(node => node.type === 'div' && node.value === ','),
61-
WORD: valueForTypeToken('word'),
62-
NONE: regExpToken(noneRe),
63-
AUTO: regExpToken(autoRe),
64-
NUMBER: regExpToken(numberRe, Number),
65-
LENGTH: regExpToken(lengthRe, Number),
66-
UNSUPPORTED_LENGTH_UNIT: regExpToken(unsupportedUnitRe),
67-
ANGLE: regExpToken(angleRe),
68-
PERCENT: regExpToken(percentRe),
69-
IDENT: regExpToken(identRe),
70-
STRING: matchString,
71-
COLOR: matchColor,
72-
LINE: regExpToken(/^(none|underline|line-through)$/i),
73-
}
57+
export const SPACE = noopToken(node => node.type === 'space')
58+
export const SLASH = noopToken(
59+
node => node.type === 'div' && node.value === '/'
60+
)
61+
export const COMMA = noopToken(
62+
node => node.type === 'div' && node.value === ','
63+
)
64+
export const WORD = valueForTypeToken('word')
65+
export const NONE = regExpToken(noneRe)
66+
export const AUTO = regExpToken(autoRe)
67+
export const NUMBER = regExpToken(numberRe, Number)
68+
export const LENGTH = regExpToken(lengthRe, Number)
69+
export const UNSUPPORTED_LENGTH_UNIT = regExpToken(unsupportedUnitRe)
70+
export const ANGLE = regExpToken(angleRe, angle => angle.toLowerCase())
71+
export const PERCENT = regExpToken(percentRe)
72+
export const IDENT = regExpToken(identRe)
73+
export const STRING = matchString
74+
export const COLOR = matchColor
75+
export const LINE = regExpToken(/^(none|underline|line-through)$/i)

src/transforms/border.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
import { regExpToken, tokens } from '../tokenTypes'
2-
3-
const { NONE, COLOR, LENGTH, UNSUPPORTED_LENGTH_UNIT, SPACE } = tokens
1+
import {
2+
regExpToken,
3+
NONE,
4+
COLOR,
5+
LENGTH,
6+
UNSUPPORTED_LENGTH_UNIT,
7+
SPACE,
8+
} from '../tokenTypes'
49

510
const BORDER_STYLE = regExpToken(/^(solid|dashed|dotted)$/)
611

@@ -23,8 +28,8 @@ export default tokenStream => {
2328
if (partsParsed !== 0) tokenStream.expect(SPACE)
2429

2530
if (
26-
(borderWidth === undefined && tokenStream.matches(LENGTH)) ||
27-
tokenStream.matches(UNSUPPORTED_LENGTH_UNIT)
31+
borderWidth === undefined &&
32+
tokenStream.matches(LENGTH, UNSUPPORTED_LENGTH_UNIT)
2833
) {
2934
borderWidth = tokenStream.lastValue
3035
} else if (borderColor === undefined && tokenStream.matches(COLOR)) {

0 commit comments

Comments
 (0)