styled-components 2.1.1 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/CODE_OF_CONDUCT.md +1 -1
  3. package/README.md +12 -67
  4. package/dist/styled-components.es.js +273 -110
  5. package/dist/styled-components.js +325 -119
  6. package/dist/styled-components.min.js +2 -2
  7. package/lib/hoc/withTheme.js +13 -7
  8. package/lib/models/BrowserStyleSheet.js +11 -0
  9. package/lib/models/ComponentStyle.js +45 -2
  10. package/lib/models/InlineStyle.js +1 -1
  11. package/lib/models/ServerStyleSheet.js +33 -17
  12. package/lib/models/StyleSheet.js +9 -0
  13. package/lib/models/StyledComponent.js +82 -38
  14. package/lib/models/StyledNativeComponent.js +31 -15
  15. package/lib/models/ThemeProvider.js +44 -12
  16. package/lib/native/index.js +1 -1
  17. package/lib/test/utils.js +5 -2
  18. package/lib/utils/create-broadcast.js +34 -24
  19. package/lib/utils/domElements.js +1 -1
  20. package/lib/utils/flatten.js +4 -1
  21. package/lib/utils/generateAlphabeticName.js +1 -1
  22. package/lib/utils/nonce.js +10 -0
  23. package/lib/utils/once.js +17 -0
  24. package/package.json +10 -10
  25. package/src/hoc/withTheme.js +14 -7
  26. package/src/models/BrowserStyleSheet.js +8 -0
  27. package/src/models/ComponentStyle.js +42 -2
  28. package/src/models/InlineStyle.js +1 -1
  29. package/src/models/ServerStyleSheet.js +27 -12
  30. package/src/models/StyleSheet.js +9 -0
  31. package/src/models/StyledComponent.js +81 -26
  32. package/src/models/StyledNativeComponent.js +30 -10
  33. package/src/models/ThemeProvider.js +38 -9
  34. package/src/models/test/ThemeProvider.test.js +7 -8
  35. package/src/native/index.js +1 -1
  36. package/src/native/test/native.test.js +14 -0
  37. package/src/test/__snapshots__/ssr.test.js.snap +147 -0
  38. package/src/test/expanded-api.test.js +24 -0
  39. package/src/test/props.test.js +14 -3
  40. package/src/test/ssr.test.js +90 -123
  41. package/src/test/styles.test.js +52 -0
  42. package/src/test/utils.js +5 -2
  43. package/src/utils/create-broadcast.js +31 -17
  44. package/src/utils/domElements.js +1 -0
  45. package/src/utils/flatten.js +16 -6
  46. package/src/utils/generateAlphabeticName.js +1 -1
  47. package/src/utils/nonce.js +6 -0
  48. package/src/utils/once.js +12 -0
  49. package/typings/styled-components.d.ts +15 -21
  50. package/typings/tests/issue1068.tsx +226 -0
  51. package/typings/tests/main-test.tsx +1 -1
  52. package/typings/tests/string-tags-test.tsx +62 -0
  53. package/typings/tests/themed-tests/issue1068.tsx +226 -0
  54. package/typings/tests/themed-tests/mytheme-styled-components.tsx +1 -1
  55. package/typings/tests/themed-tests/with-theme-test.tsx +2 -1
  56. package/typings/tests/with-theme-test.tsx +17 -0
  57. package/lib/constructors/test/injectGlobal.test.js +0 -63
  58. package/lib/constructors/test/keyframes.test.js +0 -48
  59. package/lib/constructors/test/styled.test.js +0 -19
  60. package/lib/models/AbstractStyledComponent.js +0 -43
  61. package/lib/models/test/ThemeProvider.test.js +0 -200
  62. package/lib/native/test/native.test.js +0 -290
  63. package/lib/no-parser/test/basic.test.js +0 -46
  64. package/lib/no-parser/test/flatten.test.js +0 -125
  65. package/lib/no-parser/test/keyframes.test.js +0 -45
  66. package/lib/primitives/test/primitives.test.js +0 -289
  67. package/lib/test/attrs.test.js +0 -158
  68. package/lib/test/basic.test.js +0 -267
  69. package/lib/test/css.test.js +0 -43
  70. package/lib/test/expanded-api.test.js +0 -90
  71. package/lib/test/extending.test.js +0 -198
  72. package/lib/test/overriding.test.js +0 -35
  73. package/lib/test/props.test.js +0 -38
  74. package/lib/test/rehydration.test.js +0 -306
  75. package/lib/test/ssr.test.js +0 -187
  76. package/lib/test/styles.test.js +0 -146
  77. package/lib/test/theme.test.js +0 -497
  78. package/lib/test/warnTooManyClasses.test.js +0 -71
  79. package/lib/utils/test/extractCompsFromCSS.test.js +0 -46
  80. package/lib/utils/test/flatten.test.js +0 -109
  81. package/lib/utils/test/generateAlphabeticName.test.js +0 -14
  82. package/lib/utils/test/interleave.test.js +0 -22
  83. package/lib/utils/test/validAttr.test.js +0 -560
  84. package/src/models/AbstractStyledComponent.js +0 -21
  85. package/typings/tags.d.ts +0 -137
@@ -1,11 +1,18 @@
1
+ /**
2
+ * @jest-environment node
3
+ */
4
+
1
5
  import React from 'react'
2
- import { renderToString } from 'react-dom/server'
6
+ import { renderToString } from 'react-dom/server'
3
7
  import ServerStyleSheet from '../models/ServerStyleSheet'
4
- import { resetStyled, stripWhitespace, nameGenerator } from './utils'
8
+ import { resetStyled } from './utils'
5
9
  import _injectGlobal from '../constructors/injectGlobal'
6
10
  import _keyframes from '../constructors/keyframes'
7
11
  import stringifyRules from '../utils/stringifyRules'
8
12
  import css from '../constructors/css'
13
+
14
+ jest.mock('../utils/nonce')
15
+
9
16
  const injectGlobal = _injectGlobal(stringifyRules, css)
10
17
 
11
18
  let index = 0
@@ -13,13 +20,11 @@ const keyframes = _keyframes(() => `keyframe_${index++}`, stringifyRules, css)
13
20
 
14
21
  let styled
15
22
 
16
- const format = css => stripWhitespace(css)
17
- .replace(/ {/g, "{")
18
- .replace(/(\*\/|[}>])/g, "$1\n")
19
- .replace(/\n\s+/g, "\n")
20
-
21
23
  describe('ssr', () => {
22
24
  beforeEach(() => {
25
+ // eslint-disable-next-line
26
+ require('../utils/nonce').mockReset()
27
+
23
28
  styled = resetStyled(true)
24
29
  })
25
30
 
@@ -30,17 +35,10 @@ describe('ssr', () => {
30
35
 
31
36
  const sheet = new ServerStyleSheet()
32
37
  const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
33
- const css = format(sheet.getStyleTags())
34
-
35
- expect(html).toEqual('<h1 class="sc-a b" data-reactroot="" data-reactid="1" data-react-checksum="197727696">Hello SSR!</h1>')
36
- expect(css).toEqual(format(`
37
- <style type="text/css" data-styled-components="b" data-styled-components-is-local="true">
38
- /* sc-component-id: sc-a */
39
- .sc-a {}
40
- .b { color: red; }
38
+ const css = sheet.getStyleTags()
41
39
 
42
- </style>
43
- `))
40
+ expect(html).toMatchSnapshot()
41
+ expect(css).toMatchSnapshot()
44
42
  })
45
43
 
46
44
  it('should extract both global and local CSS', () => {
@@ -53,22 +51,29 @@ describe('ssr', () => {
53
51
 
54
52
  const sheet = new ServerStyleSheet()
55
53
  const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
56
- const css = format(sheet.getStyleTags())
54
+ const css = sheet.getStyleTags()
57
55
 
58
- expect(html).toEqual('<h1 class="sc-a b" data-reactroot="" data-reactid="1" data-react-checksum="197727696">Hello SSR!</h1>')
59
- expect(css).toEqual(format(`
60
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
61
- /* sc-component-id: sc-global-2303210225 */
56
+ expect(html).toMatchSnapshot()
57
+ expect(css).toMatchSnapshot()
58
+ })
59
+
60
+ it('should add a nonce to the stylesheet if webpack nonce is detected in the global scope', () => {
61
+ // eslint-disable-next-line
62
+ require('../utils/nonce').mockImplementation(() => 'foo')
63
+
64
+ injectGlobal`
62
65
  body { background: papayawhip; }
66
+ `
67
+ const Heading = styled.h1`
68
+ color: red;
69
+ `
63
70
 
64
- </style>
65
- <style type="text/css" data-styled-components="b" data-styled-components-is-local="true">
66
- /* sc-component-id: sc-a */
67
- .sc-a {}
68
- .b { color: red; }
71
+ const sheet = new ServerStyleSheet()
72
+ const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
73
+ const css = sheet.getStyleTags()
69
74
 
70
- </style>
71
- `))
75
+ expect(html).toMatchSnapshot()
76
+ expect(css).toMatchSnapshot()
72
77
  })
73
78
 
74
79
  it('should render CSS in the order the components were defined, not rendered', () => {
@@ -86,20 +91,10 @@ describe('ssr', () => {
86
91
  <ONE/>
87
92
  </div>
88
93
  ))
89
- const css = format(sheet.getStyleTags())
90
-
91
- expect(html).toEqual('<div data-reactroot="" data-reactid="1" data-react-checksum="275982144"><h2 class="TWO a" data-reactid="2"></h2><h1 class="ONE b" data-reactid="3"></h1></div>')
92
- expect(css).toEqual(format(`
93
- <style type="text/css" data-styled-components="a b" data-styled-components-is-local="true">
94
- /* sc-component-id: ONE */
95
- .ONE {}
96
- .b {color: red;}
97
- /* sc-component-id: TWO */
98
- .TWO {}
99
- .a {color: blue;}
100
-
101
- </style>
102
- `))
94
+ const css = sheet.getStyleTags()
95
+
96
+ expect(html).toMatchSnapshot()
97
+ expect(css).toMatchSnapshot()
103
98
  })
104
99
 
105
100
  it('should share global styles but keep renders separate', () => {
@@ -115,37 +110,16 @@ describe('ssr', () => {
115
110
 
116
111
  const sheetOne = new ServerStyleSheet()
117
112
  const htmlOne = renderToString(sheetOne.collectStyles(<PageOne>Camera One!</PageOne>))
118
- const cssOne = format(sheetOne.getStyleTags())
113
+ const cssOne = sheetOne.getStyleTags()
119
114
 
120
115
  const sheetTwo = new ServerStyleSheet()
121
116
  const htmlTwo = renderToString(sheetTwo.collectStyles(<PageTwo>Camera Two!</PageTwo>))
122
- const cssTwo = format(sheetTwo.getStyleTags())
117
+ const cssTwo = sheetTwo.getStyleTags()
123
118
 
124
- expect(htmlOne).toEqual('<h1 class="PageOne a" data-reactroot="" data-reactid="1" data-react-checksum="2014320521">Camera One!</h1>')
125
- expect(cssOne).toEqual(format(`
126
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
127
- /* sc-component-id: sc-global-2303210225 */
128
- body { background: papayawhip; }
129
- </style>
130
- <style type="text/css" data-styled-components="a" data-styled-components-is-local="true">
131
- /* sc-component-id: PageOne */
132
- .PageOne {}
133
- .a { color: red; }
134
- </style>
135
- `))
136
-
137
- expect(htmlTwo).toEqual('<h2 class="PageTwo b" data-reactroot="" data-reactid="1" data-react-checksum="2124224444">Camera Two!</h2>')
138
- expect(cssTwo).toEqual(format(`
139
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
140
- /* sc-component-id: sc-global-2303210225 */
141
- body { background: papayawhip; }
142
- </style>
143
- <style type="text/css" data-styled-components="b" data-styled-components-is-local="true">
144
- /* sc-component-id: PageTwo */
145
- .PageTwo {}
146
- .b { color: blue; }
147
- </style>
148
- `))
119
+ expect(htmlOne).toMatchSnapshot()
120
+ expect(cssOne).toMatchSnapshot()
121
+ expect(htmlTwo).toMatchSnapshot()
122
+ expect(cssTwo).toMatchSnapshot()
149
123
  })
150
124
 
151
125
  it('should allow global styles to be injected during rendering', () => {
@@ -160,54 +134,21 @@ describe('ssr', () => {
160
134
  const sheetOne = new ServerStyleSheet()
161
135
  const htmlOne = renderToString(sheetOne.collectStyles(<PageOne>Camera One!</PageOne>))
162
136
  injectGlobal`html::before { content: 'During first render'; }`
163
- const cssOne = format(sheetOne.getStyleTags())
137
+ const cssOne = sheetOne.getStyleTags()
164
138
 
165
139
  injectGlobal`html::before { content: 'Between renders'; }`
166
140
 
167
141
  const sheetTwo = new ServerStyleSheet()
168
142
  injectGlobal`html::before { content: 'During second render'; }`
169
143
  const htmlTwo = renderToString(sheetTwo.collectStyles(<PageTwo>Camera Two!</PageTwo>))
170
- const cssTwo = format(sheetTwo.getStyleTags())
144
+ const cssTwo = sheetTwo.getStyleTags()
171
145
 
172
146
  injectGlobal`html::before { content: 'After both renders'; }`
173
147
 
174
- expect(htmlOne).toEqual('<h1 class="PageOne a" data-reactroot="" data-reactid="1" data-react-checksum="2014320521">Camera One!</h1>')
175
- expect(cssOne).toEqual(format(`
176
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
177
- /* sc-component-id: sc-global-737874422 */
178
- html::before { content: 'Before both renders'; }
179
- </style>
180
- <style type="text/css" data-styled-components="a" data-styled-components-is-local="true">
181
- /* sc-component-id: PageOne */
182
- .PageOne {}
183
- .a { color: red; }
184
- </style>
185
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
186
- /* sc-component-id: sc-global-2914197427 */
187
- html::before { content: 'During first render'; }
188
- </style>
189
- `))
190
-
191
- expect(htmlTwo).toEqual('<h2 class="PageTwo b" data-reactroot="" data-reactid="1" data-react-checksum="2124224444">Camera Two!</h2>')
192
- expect(cssTwo).toEqual(format(`
193
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
194
- /* sc-component-id: sc-global-737874422 */
195
- html::before { content: 'Before both renders'; }
196
- </style>
197
- <style type="text/css" data-styled-components="b" data-styled-components-is-local="true">
198
- /* sc-component-id: PageTwo */
199
- .PageTwo {}
200
- .b { color: blue; }
201
- </style>
202
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
203
- /* sc-component-id: sc-global-2914197427 */
204
- html::before { content: 'During first render'; }
205
- /* sc-component-id: sc-global-1207956261 */
206
- html::before { content: 'Between renders'; }
207
- /* sc-component-id: sc-global-3990873394 */
208
- html::before { content: 'During second render'; }
209
- </style>
210
- `))
148
+ expect(htmlOne).toMatchSnapshot()
149
+ expect(cssOne).toMatchSnapshot()
150
+ expect(htmlTwo).toMatchSnapshot()
151
+ expect(cssTwo).toMatchSnapshot()
211
152
  })
212
153
 
213
154
  it('should dispatch global styles to each ServerStyleSheet', () => {
@@ -222,21 +163,47 @@ describe('ssr', () => {
222
163
  const html = renderToString(sheet.collectStyles(
223
164
  <Header animation={keyframes`0% { opacity: 0; }`}/>
224
165
  ))
225
- const css = format(sheet.getStyleTags())
166
+ const css = sheet.getStyleTags()
167
+
168
+ expect(html).toMatchSnapshot()
169
+ expect(css).toMatchSnapshot()
170
+ })
171
+
172
+ it('should return a generated React style element', () => {
173
+ injectGlobal`
174
+ body { background: papayawhip; }
175
+ `
176
+ const Heading = styled.h1`
177
+ color: red;
178
+ `
179
+
180
+ const sheet = new ServerStyleSheet()
181
+ const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
182
+ const elements = sheet.getStyleElement()
183
+
184
+ expect(elements).toHaveLength(2);
185
+
186
+ expect(elements[0].props).toMatchSnapshot()
187
+ expect(elements[1].props).toMatchSnapshot()
188
+ })
189
+
190
+ it('should return a generated React style element with nonce if webpack nonce is preset in the global scope', () => {
191
+ // eslint-disable-next-line
192
+ require('../utils/nonce').mockImplementation(() => 'foo')
226
193
 
227
- expect(html).toEqual('<h1 class="Header a" data-reactroot="" data-reactid="1" data-react-checksum="1829114759"></h1>')
228
- expect(css).toEqual(format(`
229
- <style type="text/css" data-styled-components="" data-styled-components-is-local="false">
230
- /* sc-component-id: sc-global-2303210225 */
194
+ injectGlobal`
231
195
  body { background: papayawhip; }
232
- </style>
233
- <style type="text/css" data-styled-components="a" data-styled-components-is-local="true">
234
- /* sc-component-id: Header */
235
- .Header {}
236
- .a { -webkit-animation:keyframe_0 1s both; animation:keyframe_0 1s both; }
237
- /* sc-component-id: sc-keyframes-keyframe_0 */
238
- @-webkit-keyframes keyframe_0 {0% {opacity: 0;}}@keyframes keyframe_0 {0% {opacity: 0;}}
239
- </style>
240
- `))
196
+ `
197
+ const Heading = styled.h1`
198
+ color: red;
199
+ `
200
+
201
+ const sheet = new ServerStyleSheet()
202
+ const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
203
+ const elements = sheet.getStyleElement()
204
+
205
+ expect(elements).toHaveLength(2);
206
+ expect(elements[0].props.nonce).toBe('foo');
207
+ expect(elements[1].props.nonce).toBe('foo');
241
208
  })
242
209
  })
@@ -2,7 +2,14 @@
2
2
  import React from 'react'
3
3
  import { shallow } from 'enzyme'
4
4
 
5
+ import * as nonce from '../utils/nonce';
5
6
  import { resetStyled, expectCSSMatches } from './utils'
7
+ import _injectGlobal from '../constructors/injectGlobal'
8
+ import stringifyRules from '../utils/stringifyRules'
9
+ import css from '../constructors/css'
10
+ const injectGlobal = _injectGlobal(stringifyRules, css)
11
+
12
+ jest.mock('../utils/nonce')
6
13
 
7
14
  let styled
8
15
 
@@ -169,4 +176,49 @@ describe('with styles', () => {
169
176
  }
170
177
  `)
171
178
  })
179
+
180
+ it('should add a webpack nonce to the style tags if one is available in the global scope', () => {
181
+ jest.spyOn(nonce, 'default').mockImplementation(() => 'foo')
182
+
183
+ const rule = 'color: blue;'
184
+ const Comp = styled.div`
185
+ ${rule}
186
+ `
187
+ shallow(<Comp />)
188
+ expectCSSMatches(`
189
+ .sc-a {}
190
+ .b {
191
+ color: blue;
192
+ }
193
+ `)
194
+
195
+ Array.from(document.querySelectorAll('style')).forEach(el => expect(el.getAttribute('nonce')).toBe('foo'))
196
+ })
197
+
198
+ it('should add a webpack nonce to the global style tags if one is available in the global scope', () => {
199
+ // eslint-disable-next-line no-underscore-dangle
200
+ window.__webpack_nonce__ = 'foo'
201
+
202
+ injectGlobal`
203
+ html {
204
+ background: red;
205
+ }
206
+ `
207
+ const rule = 'color: blue;'
208
+ const Comp = styled.div`
209
+ ${rule}
210
+ `
211
+ shallow(<Comp />)
212
+ expectCSSMatches(`
213
+ html {
214
+ background: red;
215
+ }
216
+ .sc-a {}
217
+ .b {
218
+ color: blue;
219
+ }
220
+ `)
221
+
222
+ Array.from(document.querySelectorAll('style')).forEach(el => expect(el.getAttribute('nonce')).toBe('foo'))
223
+ })
172
224
  })
package/src/test/utils.js CHANGED
@@ -23,8 +23,11 @@ const classNames = () => seededClassnames.shift() || String.fromCodePoint(97 + i
23
23
 
24
24
  export const seedNextClassnames = (names: Array<string>) => seededClassnames = names
25
25
  export const resetStyled = (isServer: boolean = false) => {
26
- if (!document.head) throw new Error("Missing document <head>")
27
- document.head.innerHTML = ''
26
+ if (!isServer) {
27
+ if (!document.head) throw new Error("Missing document <head>")
28
+ document.head.innerHTML = ''
29
+ }
30
+
28
31
  StyleSheet.reset(isServer)
29
32
  index = 0
30
33
 
@@ -7,29 +7,43 @@
7
7
 
8
8
  export type Broadcast = {
9
9
  publish: (value: mixed) => void,
10
- subscribe: (listener: (currentValue: mixed) => void) => () => void
10
+ subscribe: (listener: (currentValue: mixed) => void) => number,
11
+ unsubscribe: (number) => void,
11
12
  }
12
13
 
13
- const createBroadcast = (initialValue: mixed): Broadcast => {
14
- let listeners = []
15
- let currentValue = initialValue
14
+ const createBroadcast = (initialState: mixed): Broadcast => {
15
+ const listeners = {}
16
+ let id = 0
17
+ let state = initialState
16
18
 
17
- return {
18
- publish(value: mixed) {
19
- currentValue = value
20
- listeners.forEach(listener => listener(currentValue))
21
- },
22
- subscribe(listener) {
23
- listeners.push(listener)
19
+ function publish(nextState: mixed) {
20
+ state = nextState
24
21
 
25
- // Publish to this subscriber once immediately.
26
- listener(currentValue)
27
-
28
- return () => {
29
- listeners = listeners.filter(item => item !== listener)
22
+ // eslint-disable-next-line guard-for-in, no-restricted-syntax
23
+ for (const key in listeners) {
24
+ const listener = listeners[key]
25
+ if (listener === undefined) {
26
+ // eslint-disable-next-line no-continue
27
+ continue
30
28
  }
31
- },
29
+
30
+ listener(state)
31
+ }
32
+ }
33
+
34
+ function subscribe(listener) {
35
+ const currentId = id
36
+ listeners[currentId] = listener
37
+ id += 1
38
+ listener(state)
39
+ return currentId
32
40
  }
41
+
42
+ function unsubscribe(unsubID: number) {
43
+ listeners[unsubID] = undefined
44
+ }
45
+
46
+ return { publish, subscribe, unsubscribe }
33
47
  }
34
48
 
35
49
  export default createBroadcast
@@ -66,6 +66,7 @@ export default [
66
66
  'main',
67
67
  'map',
68
68
  'mark',
69
+ 'marquee',
69
70
  'menu',
70
71
  'menuitem',
71
72
  'meta',
@@ -5,13 +5,23 @@ import isPlainObject from 'is-plain-object'
5
5
  import type { Interpolation } from '../types'
6
6
 
7
7
  export const objToCss = (obj: Object, prevKey?: string): string => {
8
- const css = Object.keys(obj).map(key => {
9
- if (isPlainObject(obj[key])) return objToCss(obj[key], key)
10
- return `${hyphenate(key)}: ${obj[key]};`
11
- }).join(' ')
12
- return prevKey ? `${prevKey} {
8
+ const css = Object.keys(obj)
9
+ .filter(key => {
10
+ const chunk = obj[key]
11
+ return (
12
+ chunk !== undefined && chunk !== null && chunk !== false && chunk !== ''
13
+ )
14
+ })
15
+ .map(key => {
16
+ if (isPlainObject(obj[key])) return objToCss(obj[key], key)
17
+ return `${hyphenate(key)}: ${obj[key]};`
18
+ })
19
+ .join(' ')
20
+ return prevKey
21
+ ? `${prevKey} {
13
22
  ${css}
14
- }` : css
23
+ }`
24
+ : css
15
25
  }
16
26
 
17
27
  const flatten = (chunks: Array<Interpolation>, executionContext: ?Object): Array<Interpolation> => (
@@ -10,7 +10,7 @@ const generateAlphabeticName = (code: number): string => {
10
10
  for (
11
11
  x = code;
12
12
  x > charsLength;
13
- x = Math.floor(x / chars.length)
13
+ x = Math.floor(x / charsLength)
14
14
  ) {
15
15
  name = chars[x % charsLength] + name
16
16
  }
@@ -0,0 +1,6 @@
1
+ // @flow
2
+ /* eslint-disable camelcase, no-undef */
3
+
4
+ declare var __webpack_nonce__: string
5
+
6
+ export default () => typeof __webpack_nonce__ !== 'undefined' ? __webpack_nonce__ : null
@@ -0,0 +1,12 @@
1
+ // @flow
2
+ // Helper to call a given function, only once
3
+ export default (cb: () => any) => {
4
+ let called = false
5
+
6
+ return () => {
7
+ if (!called) {
8
+ called = true
9
+ cb()
10
+ }
11
+ }
12
+ }
@@ -1,8 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { StatelessComponent, ComponentClass, PureComponent, ReactElement } from "react";
3
3
 
4
- import { HTMLTags, SVGTags } from "./tags";
5
-
6
4
  type Component<P> = ComponentClass<P> | StatelessComponent<P>;
7
5
 
8
6
  export interface ThemeProps<T> {
@@ -33,8 +31,7 @@ type Attrs<P, A extends Partial<P>, T> = {
33
31
  export interface StyledComponentClass<P, T, O = P> extends ComponentClass<ThemedOuterStyledProps<O, T>> {
34
32
  extend: ThemedStyledFunction<P, T, O>;
35
33
 
36
- withComponent<K extends keyof HTMLTags>(tag: K): StyledComponentClass<React.HTMLProps<HTMLTags[K]>, T, O>;
37
- withComponent<K extends keyof SVGTags>(tag: K): StyledComponentClass<React.SVGAttributes<SVGTags[K]>, T, O>;
34
+ withComponent<K extends keyof JSX.IntrinsicElements>(tag: K): StyledComponentClass<JSX.IntrinsicElements[K], T, O>;
38
35
  withComponent(element: ComponentClass<P>): StyledComponentClass<P, T, O>;
39
36
  }
40
37
 
@@ -46,24 +43,16 @@ export interface ThemedStyledFunction<P, T, O = P> {
46
43
 
47
44
  export type StyledFunction<P> = ThemedStyledFunction<P, any>;
48
45
 
49
- export type ThemedHtmlStyledFunction<E, T> = ThemedStyledFunction<React.HTMLProps<E>, T>;
50
- export type HtmlStyledFunction<E> = ThemedHtmlStyledFunction<E, any>;
51
-
52
- export type ThemedSvgStyledFunction<E extends SVGElement, T> = ThemedStyledFunction<React.SVGAttributes<E>, T>;
53
- export type SvgStyledFunction<E extends SVGElement> = ThemedSvgStyledFunction<E, any>;
54
-
55
- type ThemedStyledComponentFactoriesHTML<T> = {
56
- [K in keyof HTMLTags]: ThemedHtmlStyledFunction<HTMLTags[K], T>;
46
+ type ThemedStyledComponentFactories<T> = {
47
+ [TTag in keyof JSX.IntrinsicElements]: ThemedStyledFunction<JSX.IntrinsicElements[TTag], T>;
57
48
  };
58
49
 
59
- type ThemedStyledComponentFactoriesSVG<T> = {
60
- [K in keyof SVGTags]: ThemedSvgStyledFunction<SVGTags[K], T>;
61
- };
62
-
63
- type ThemedStyledComponentFactories<T> = ThemedStyledComponentFactoriesHTML<T> & ThemedStyledComponentFactoriesSVG<T>;
64
-
65
50
  export interface ThemedBaseStyledInterface<T> extends ThemedStyledComponentFactories<T> {
66
- <P>(component: Component<P>): ThemedStyledFunction<P, T>;
51
+ <P, TTag extends keyof JSX.IntrinsicElements>(tag: TTag): ThemedStyledFunction<P, T, P & JSX.IntrinsicElements[TTag]>;
52
+ <P, O>(component: StyledComponentClass<P, T, O>): ThemedStyledFunction<P, T, O>;
53
+ <P extends { theme: T; }>(component: React.ComponentClass<P>): ThemedStyledFunction<P, T, WithOptionalTheme<P, T>>;
54
+ <P>(component: React.ComponentClass<P>): ThemedStyledFunction<P, T>;
55
+ <P extends { [prop: string]: any; theme?: T; }>(component: React.StatelessComponent<P>): ThemedStyledFunction<P, T, WithOptionalTheme<P, T>>;
67
56
  }
68
57
  export type BaseStyledInterface = ThemedBaseStyledInterface<any>;
69
58
 
@@ -80,13 +69,18 @@ export interface ThemedCssFunction<T> {
80
69
  <P>(strings: TemplateStringsArray, ...interpolations: Interpolation<ThemedStyledProps<P, T>>[]): FlattenInterpolation<ThemedStyledProps<P, T>>[];
81
70
  }
82
71
 
72
+ // Helper type operators
73
+ type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
74
+ type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
75
+ type WithOptionalTheme<P extends { theme?: T; }, T> = Omit<P, "theme"> & { theme?: T; };
76
+
83
77
  export interface ThemedStyledComponentsModule<T> {
84
78
  default: ThemedStyledInterface<T>;
85
79
 
86
80
  css: ThemedCssFunction<T>;
87
81
  keyframes(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): string;
88
82
  injectGlobal(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): void;
89
- withTheme<P extends { theme?: T; }, T>(component: Component<P>): ComponentClass<P>;
83
+ withTheme<P extends { theme?: T; }>(component: Component<P>): ComponentClass<WithOptionalTheme<P, T>>;
90
84
 
91
85
  ThemeProvider: ThemeProviderComponent<T>;
92
86
  }
@@ -94,7 +88,7 @@ export interface ThemedStyledComponentsModule<T> {
94
88
  declare const styled: StyledInterface;
95
89
 
96
90
  export const css: ThemedCssFunction<any>;
97
- export function withTheme<P extends { theme?: T; }, T>(component: Component<P>): ComponentClass<P>;
91
+ export function withTheme<P extends { theme?: T; }, T>(component: Component<P>): ComponentClass<WithOptionalTheme<P, T>>;
98
92
 
99
93
  export function keyframes(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): string;
100
94
  export function injectGlobal(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): void;