@compiled/react 0.8.0 → 0.10.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 (203) hide show
  1. package/dist/browser/class-names/index.d.ts +22 -16
  2. package/dist/browser/class-names/index.js +1 -27
  3. package/dist/browser/class-names/index.js.flow +2 -2
  4. package/dist/browser/class-names/index.js.map +1 -1
  5. package/dist/browser/css/index.d.ts +22 -19
  6. package/dist/browser/css/index.js +4 -3
  7. package/dist/browser/css/index.js.flow +4 -4
  8. package/dist/browser/css/index.js.map +1 -1
  9. package/dist/browser/index.d.ts +12 -22
  10. package/dist/browser/index.js +4 -0
  11. package/dist/browser/index.js.flow +5 -30
  12. package/dist/browser/index.js.map +1 -1
  13. package/dist/browser/jsx/jsx-dev-runtime.d.ts +2 -0
  14. package/dist/browser/jsx/jsx-dev-runtime.js +4 -0
  15. package/dist/browser/jsx/jsx-dev-runtime.js.map +1 -0
  16. package/dist/browser/jsx/jsx-local-namespace.d.ts +66 -0
  17. package/dist/browser/jsx/jsx-local-namespace.js +2 -0
  18. package/dist/browser/jsx/jsx-local-namespace.js.map +1 -0
  19. package/dist/browser/jsx/jsx-runtime.d.ts +2 -0
  20. package/dist/browser/jsx/jsx-runtime.js +4 -0
  21. package/dist/browser/jsx/jsx-runtime.js.map +1 -0
  22. package/dist/browser/keyframes/index.d.ts +20 -24
  23. package/dist/browser/keyframes/index.js.flow +1 -1
  24. package/dist/browser/keyframes/index.js.map +1 -1
  25. package/dist/browser/runtime/ax.js.flow +1 -1
  26. package/dist/browser/runtime/css-custom-property.d.ts +1 -1
  27. package/dist/browser/runtime/css-custom-property.js.flow +2 -2
  28. package/dist/browser/runtime/css-custom-property.js.map +1 -1
  29. package/dist/browser/runtime/dev-warnings.d.ts +0 -1
  30. package/dist/browser/runtime/dev-warnings.js +1 -1
  31. package/dist/browser/runtime/dev-warnings.js.flow +1 -2
  32. package/dist/browser/runtime/dev-warnings.js.map +1 -1
  33. package/dist/browser/runtime/index.js.flow +1 -1
  34. package/dist/browser/runtime/is-node.js.flow +1 -1
  35. package/dist/browser/runtime/sheet.d.ts +1 -1
  36. package/dist/browser/runtime/sheet.js.flow +2 -2
  37. package/dist/browser/runtime/style-cache.js.flow +1 -1
  38. package/dist/browser/runtime/style-cache.js.map +1 -1
  39. package/dist/browser/runtime/style.js +2 -2
  40. package/dist/browser/runtime/style.js.flow +1 -1
  41. package/dist/browser/runtime/style.js.map +1 -1
  42. package/dist/browser/runtime/types.js.flow +1 -1
  43. package/dist/browser/runtime.js.flow +1 -1
  44. package/dist/browser/styled/index.d.ts +38 -13
  45. package/dist/browser/styled/index.js +35 -7
  46. package/dist/browser/styled/index.js.flow +5 -8
  47. package/dist/browser/styled/index.js.map +1 -1
  48. package/dist/browser/types.d.ts +5 -2
  49. package/dist/browser/types.js.flow +6 -3
  50. package/dist/browser/utils/error.js.flow +1 -1
  51. package/dist/cjs/class-names/index.d.ts +22 -16
  52. package/dist/cjs/class-names/index.js +1 -27
  53. package/dist/cjs/class-names/index.js.flow +2 -2
  54. package/dist/cjs/class-names/index.js.map +1 -1
  55. package/dist/cjs/css/index.d.ts +22 -19
  56. package/dist/cjs/css/index.js +4 -3
  57. package/dist/cjs/css/index.js.flow +4 -4
  58. package/dist/cjs/css/index.js.map +1 -1
  59. package/dist/cjs/index.d.ts +12 -22
  60. package/dist/cjs/index.js +5 -1
  61. package/dist/cjs/index.js.flow +5 -30
  62. package/dist/cjs/index.js.map +1 -1
  63. package/dist/cjs/jsx/jsx-dev-runtime.d.ts +2 -0
  64. package/dist/cjs/jsx/jsx-dev-runtime.js +16 -0
  65. package/dist/cjs/jsx/jsx-dev-runtime.js.map +1 -0
  66. package/dist/cjs/jsx/jsx-local-namespace.d.ts +66 -0
  67. package/dist/cjs/jsx/jsx-local-namespace.js +3 -0
  68. package/dist/cjs/jsx/jsx-local-namespace.js.map +1 -0
  69. package/dist/cjs/jsx/jsx-runtime.d.ts +2 -0
  70. package/dist/cjs/jsx/jsx-runtime.js +16 -0
  71. package/dist/cjs/jsx/jsx-runtime.js.map +1 -0
  72. package/dist/cjs/keyframes/index.d.ts +20 -24
  73. package/dist/cjs/keyframes/index.js.flow +1 -1
  74. package/dist/cjs/keyframes/index.js.map +1 -1
  75. package/dist/cjs/runtime/ax.js.flow +1 -1
  76. package/dist/cjs/runtime/css-custom-property.d.ts +1 -1
  77. package/dist/cjs/runtime/css-custom-property.js.flow +2 -2
  78. package/dist/cjs/runtime/css-custom-property.js.map +1 -1
  79. package/dist/cjs/runtime/dev-warnings.d.ts +0 -1
  80. package/dist/cjs/runtime/dev-warnings.js +2 -3
  81. package/dist/cjs/runtime/dev-warnings.js.flow +1 -2
  82. package/dist/cjs/runtime/dev-warnings.js.map +1 -1
  83. package/dist/cjs/runtime/index.js.flow +1 -1
  84. package/dist/cjs/runtime/is-node.js.flow +1 -1
  85. package/dist/cjs/runtime/sheet.d.ts +1 -1
  86. package/dist/cjs/runtime/sheet.js.flow +2 -2
  87. package/dist/cjs/runtime/style-cache.js.flow +1 -1
  88. package/dist/cjs/runtime/style-cache.js.map +1 -1
  89. package/dist/cjs/runtime/style.js +2 -2
  90. package/dist/cjs/runtime/style.js.flow +1 -1
  91. package/dist/cjs/runtime/style.js.map +1 -1
  92. package/dist/cjs/runtime/types.js.flow +1 -1
  93. package/dist/cjs/runtime.js.flow +1 -1
  94. package/dist/cjs/styled/index.d.ts +38 -13
  95. package/dist/cjs/styled/index.js +35 -7
  96. package/dist/cjs/styled/index.js.flow +5 -8
  97. package/dist/cjs/styled/index.js.map +1 -1
  98. package/dist/cjs/types.d.ts +5 -2
  99. package/dist/cjs/types.js.flow +6 -3
  100. package/dist/cjs/utils/error.js.flow +1 -1
  101. package/dist/esm/class-names/index.d.ts +22 -16
  102. package/dist/esm/class-names/index.js +1 -27
  103. package/dist/esm/class-names/index.js.flow +2 -2
  104. package/dist/esm/class-names/index.js.map +1 -1
  105. package/dist/esm/css/index.d.ts +22 -19
  106. package/dist/esm/css/index.js +4 -3
  107. package/dist/esm/css/index.js.flow +4 -4
  108. package/dist/esm/css/index.js.map +1 -1
  109. package/dist/esm/index.d.ts +12 -22
  110. package/dist/esm/index.js +4 -0
  111. package/dist/esm/index.js.flow +5 -30
  112. package/dist/esm/index.js.map +1 -1
  113. package/dist/esm/jsx/jsx-dev-runtime.d.ts +2 -0
  114. package/dist/esm/jsx/jsx-dev-runtime.js +4 -0
  115. package/dist/esm/jsx/jsx-dev-runtime.js.map +1 -0
  116. package/dist/esm/jsx/jsx-local-namespace.d.ts +66 -0
  117. package/dist/esm/jsx/jsx-local-namespace.js +2 -0
  118. package/dist/esm/jsx/jsx-local-namespace.js.map +1 -0
  119. package/dist/esm/jsx/jsx-runtime.d.ts +2 -0
  120. package/dist/esm/jsx/jsx-runtime.js +4 -0
  121. package/dist/esm/jsx/jsx-runtime.js.map +1 -0
  122. package/dist/esm/keyframes/index.d.ts +20 -24
  123. package/dist/esm/keyframes/index.js.flow +1 -1
  124. package/dist/esm/keyframes/index.js.map +1 -1
  125. package/dist/esm/runtime/ax.js.flow +1 -1
  126. package/dist/esm/runtime/css-custom-property.d.ts +1 -1
  127. package/dist/esm/runtime/css-custom-property.js.flow +2 -2
  128. package/dist/esm/runtime/css-custom-property.js.map +1 -1
  129. package/dist/esm/runtime/dev-warnings.d.ts +0 -1
  130. package/dist/esm/runtime/dev-warnings.js +1 -1
  131. package/dist/esm/runtime/dev-warnings.js.flow +1 -2
  132. package/dist/esm/runtime/dev-warnings.js.map +1 -1
  133. package/dist/esm/runtime/index.js.flow +1 -1
  134. package/dist/esm/runtime/is-node.js.flow +1 -1
  135. package/dist/esm/runtime/sheet.d.ts +1 -1
  136. package/dist/esm/runtime/sheet.js.flow +2 -2
  137. package/dist/esm/runtime/style-cache.js.flow +1 -1
  138. package/dist/esm/runtime/style-cache.js.map +1 -1
  139. package/dist/esm/runtime/style.js +2 -2
  140. package/dist/esm/runtime/style.js.flow +1 -1
  141. package/dist/esm/runtime/style.js.map +1 -1
  142. package/dist/esm/runtime/types.js.flow +1 -1
  143. package/dist/esm/runtime.js.flow +1 -1
  144. package/dist/esm/styled/index.d.ts +38 -13
  145. package/dist/esm/styled/index.js +35 -7
  146. package/dist/esm/styled/index.js.flow +5 -8
  147. package/dist/esm/styled/index.js.map +1 -1
  148. package/dist/esm/types.d.ts +5 -2
  149. package/dist/esm/types.js.flow +6 -3
  150. package/dist/esm/utils/error.js.flow +1 -1
  151. package/jsx-dev-runtime/package.json +6 -0
  152. package/jsx-runtime/package.json +6 -0
  153. package/package.json +42 -14
  154. package/src/__tests__/browser.test.tsx +5 -5
  155. package/src/__tests__/jest-matcher.test.tsx +197 -0
  156. package/src/__tests__/server-side-hydrate.test.tsx +1 -0
  157. package/src/__tests__/ssr.test.tsx +3 -3
  158. package/src/class-names/__tests__/index.test.tsx +1 -1
  159. package/src/class-names/index.js.flow +2 -2
  160. package/src/class-names/index.tsx +25 -14
  161. package/src/css/__tests__/index.test.tsx +9 -3
  162. package/src/css/index.js.flow +4 -4
  163. package/src/css/index.tsx +36 -23
  164. package/src/index.js.flow +5 -30
  165. package/src/index.tsx +18 -26
  166. package/src/jsx/__tests__/index.test.tsx +1 -2
  167. package/src/jsx/__tests__/jsx-import-source-pragma.test.tsx +37 -0
  168. package/src/jsx/__tests__/jsx-pragma.test.tsx +39 -0
  169. package/src/jsx/jsx-dev-runtime.tsx +5 -0
  170. package/src/jsx/jsx-local-namespace.tsx +75 -0
  171. package/src/jsx/jsx-runtime.tsx +5 -0
  172. package/src/keyframes/__tests__/index.test.tsx +2 -1
  173. package/src/keyframes/index.js.flow +1 -1
  174. package/src/keyframes/index.tsx +24 -28
  175. package/src/runtime/__perf__/ax.test.ts +42 -0
  176. package/src/runtime/__perf__/cs.test.tsx +111 -0
  177. package/src/runtime/__perf__/sheet.test.ts +61 -0
  178. package/src/runtime/__perf__/utils/cs.tsx +60 -0
  179. package/src/runtime/__perf__/utils/sheet.tsx +151 -0
  180. package/src/runtime/__tests__/style-ssr.test.tsx +1 -0
  181. package/src/runtime/__tests__/style.test.tsx +92 -58
  182. package/src/runtime/ax.js.flow +1 -1
  183. package/src/runtime/css-custom-property.js.flow +2 -2
  184. package/src/runtime/css-custom-property.tsx +1 -1
  185. package/src/runtime/dev-warnings.js.flow +1 -2
  186. package/src/runtime/dev-warnings.tsx +1 -1
  187. package/src/runtime/index.js.flow +1 -1
  188. package/src/runtime/is-node.js.flow +1 -1
  189. package/src/runtime/sheet.js.flow +2 -2
  190. package/src/runtime/sheet.tsx +1 -1
  191. package/src/runtime/style-cache.js.flow +1 -1
  192. package/src/runtime/style-cache.tsx +1 -0
  193. package/src/runtime/style.js.flow +1 -1
  194. package/src/runtime/style.tsx +4 -3
  195. package/src/runtime/types.js.flow +1 -1
  196. package/src/runtime.js.flow +1 -1
  197. package/src/styled/__tests__/index.test.tsx +3 -3
  198. package/src/styled/index.js.flow +5 -8
  199. package/src/styled/index.tsx +39 -14
  200. package/src/types.js.flow +6 -3
  201. package/src/types.tsx +6 -2
  202. package/src/utils/error.js.flow +1 -1
  203. package/CHANGELOG.md +0 -68
@@ -0,0 +1,111 @@
1
+ import { runBenchmark } from '@compiled/benchmark';
2
+ import * as React from 'react';
3
+ import { renderToString } from 'react-dom/server';
4
+
5
+ import { CC, CS } from '../index';
6
+
7
+ import { StyleArr, StyleStr } from './utils/cs';
8
+
9
+ describe('CS benchmark', () => {
10
+ it('completes with CS (1 array element) or StyleArr as the fastest', async () => {
11
+ const stylesArr = [
12
+ '._s7n4jp4b{vertical-align:top}',
13
+ '._1reo15vq{overflow-x:hidden}',
14
+ '._18m915vq{overflow-y:hidden}',
15
+ '._1bto1l2s{text-overflow:ellipsis}',
16
+ '._o5721q9c{white-space:nowrap}',
17
+ '._ca0qidpf{padding-top:0}',
18
+ '._u5f31y44{padding-right:4px}',
19
+ '._n3tdidpf{padding-bottom:0}',
20
+ '._19bv1y44{padding-left:4px}',
21
+ '._p12f12xx{max-width:100px}',
22
+ '._1bsb1osq{width:100%}',
23
+ ];
24
+
25
+ const stylesStr = stylesArr.join('');
26
+
27
+ const className = [
28
+ '_bfhk1jys',
29
+ '_2rko1l7b',
30
+ '_vchhusvi',
31
+ '_syaz4rde',
32
+ '_1e0c1o8l',
33
+ '_1wyb1skh',
34
+ '_k48p1fw0',
35
+ '_vwz4kb7n',
36
+ '_p12f1osq',
37
+ '_ca0qyh40',
38
+ '_u5f3idpf',
39
+ '_n3td1l7b',
40
+ '_19bvidpf',
41
+ '_1p1dangw',
42
+ '_s7n41q9y',
43
+ ].join(' ');
44
+
45
+ const style = {
46
+ '--_16owtcm': 'rgb(227, 252, 239)',
47
+ '--_kmurgp': 'rgb(0, 102, 68)',
48
+ } as any;
49
+
50
+ const nonce = 'k0Mp1lEd';
51
+
52
+ const benchmark = await runBenchmark('CS', [
53
+ {
54
+ name: 'CS (1 array element)',
55
+ fn: () => {
56
+ renderToString(
57
+ <CC>
58
+ <CS nonce={nonce}>{[stylesStr]}</CS>
59
+ <span className={className} style={style}>
60
+ hello world
61
+ </span>
62
+ </CC>
63
+ );
64
+ },
65
+ },
66
+ {
67
+ name: 'CS (n array elements)',
68
+ fn: () => {
69
+ renderToString(
70
+ <CC>
71
+ <CS nonce={nonce}>{stylesArr}</CS>
72
+ <span className={className} style={style}>
73
+ hello world
74
+ </span>
75
+ </CC>
76
+ );
77
+ },
78
+ },
79
+ {
80
+ name: 'StyleArr',
81
+ fn: () => {
82
+ renderToString(
83
+ <CC>
84
+ <StyleArr nonce={nonce}>{stylesArr}</StyleArr>
85
+ <span className={className} style={style}>
86
+ hello world
87
+ </span>
88
+ </CC>
89
+ );
90
+ },
91
+ },
92
+ {
93
+ name: 'StyleStr',
94
+ fn: () => {
95
+ renderToString(
96
+ <CC>
97
+ <StyleStr nonce={nonce}>{stylesStr}</StyleStr>
98
+ <span className={className} style={style}>
99
+ hello world
100
+ </span>
101
+ </CC>
102
+ );
103
+ },
104
+ },
105
+ ]);
106
+
107
+ expect(benchmark).toMatchObject({
108
+ fastest: expect.not.arrayContaining(['StyleStr', 'CS (n array elements)']),
109
+ });
110
+ }, 30000);
111
+ });
@@ -0,0 +1,61 @@
1
+ import { runBenchmark } from '@compiled/benchmark';
2
+
3
+ import insertRule from '../sheet';
4
+
5
+ import { createStyleSheet } from './utils/sheet';
6
+
7
+ global.document = {
8
+ // @ts-expect-error
9
+ createTextNode: () => {},
10
+ head: {
11
+ // @ts-expect-error
12
+ insertBefore: () => {},
13
+ },
14
+ // @ts-expect-error
15
+ createElement: () => ({
16
+ appendChild: () => {},
17
+ }),
18
+ };
19
+
20
+ describe('sheet benchmark', () => {
21
+ it('completes with insertRule as the fastest', async () => {
22
+ const rules = [
23
+ '._s7n4jp4b{vertical-align:top}',
24
+ '._1reo15vq{overflow-x:hidden}',
25
+ '._18m915vq{overflow-y:hidden}',
26
+ '._1bto1l2s{text-overflow:ellipsis}',
27
+ '._o5721q9c{white-space:nowrap}',
28
+ '._ca0qidpf{padding-top:0}',
29
+ '._u5f31y44{padding-right:4px}',
30
+ '._n3tdidpf{padding-bottom:0}',
31
+ '._19bv1y44{padding-left:4px}',
32
+ '._p12f12xx{max-width:100px}',
33
+ '._1bsb1osq{width:100%}',
34
+ ];
35
+
36
+ const benchmark = await runBenchmark('sheet', [
37
+ {
38
+ name: 'insertRule',
39
+ fn: () => {
40
+ for (const rule of rules) {
41
+ insertRule(rule, {});
42
+ }
43
+ },
44
+ },
45
+ {
46
+ name: 'createStyleSheet',
47
+ fn: () => {
48
+ const sheet = createStyleSheet({});
49
+
50
+ for (const rule of rules) {
51
+ sheet(rule);
52
+ }
53
+ },
54
+ },
55
+ ]);
56
+
57
+ expect(benchmark).toMatchObject({
58
+ fastest: expect.arrayContaining(['insertRule']),
59
+ });
60
+ }, 30000);
61
+ });
@@ -0,0 +1,60 @@
1
+ import React, { createContext, useContext } from 'react';
2
+
3
+ const Cache = createContext<Record<string, true> | null>(null);
4
+
5
+ export const useCache = (): Record<string, true> => {
6
+ return useContext(Cache) || {};
7
+ };
8
+
9
+ export type StyleStrProps = {
10
+ children: string;
11
+ nonce: string;
12
+ };
13
+
14
+ export function StyleStr({ children, nonce }: StyleStrProps): JSX.Element | null {
15
+ const inserted = useCache();
16
+
17
+ // The following code will not exist in the browser bundle.
18
+ const sheets = children.split('.');
19
+ let toInsert = '';
20
+
21
+ for (let i = 0; i < sheets.length; i++) {
22
+ const sheet = sheets[i];
23
+ if (inserted[sheet]) {
24
+ continue;
25
+ }
26
+ toInsert += sheet;
27
+ }
28
+
29
+ if (!toInsert) {
30
+ return null;
31
+ }
32
+
33
+ return <style nonce={nonce}>{toInsert}</style>;
34
+ }
35
+
36
+ export type StyleArrProps = {
37
+ children: string[];
38
+ nonce: string;
39
+ };
40
+
41
+ export function StyleArr({ children: sheets, nonce }: StyleArrProps): JSX.Element | null {
42
+ const inserted = useCache();
43
+
44
+ // The following code will not exist in the browser bundle.
45
+ let toInsert = '';
46
+
47
+ for (let i = 0; i < sheets.length; i++) {
48
+ const sheet = sheets[i];
49
+ if (inserted[sheet]) {
50
+ continue;
51
+ }
52
+ toInsert += sheet;
53
+ }
54
+
55
+ if (!toInsert) {
56
+ return null;
57
+ }
58
+
59
+ return <style nonce={nonce}>{toInsert}</style>;
60
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Ordered style buckets using their short psuedo name.
3
+ * If changes are needed make sure that it aligns with the definition in `sort-at-rule-pseudos.tsx`.
4
+ */
5
+ export const styleBucketOrdering: string[] = [
6
+ // catch-all
7
+ '',
8
+ // link
9
+ 'l',
10
+ // visited
11
+ 'v',
12
+ // focus-within
13
+ 'w',
14
+ // focus
15
+ 'f',
16
+ // focus-visible
17
+ 'i',
18
+ // hover
19
+ 'h',
20
+ // active
21
+ 'a',
22
+ // at-rules
23
+ 'm',
24
+ ];
25
+
26
+ /**
27
+ * Holds all style buckets in memory that have been added to the head.
28
+ */
29
+ const styleBucketsInHead: Record<string, HTMLStyleElement> = {};
30
+
31
+ /**
32
+ * Maps the long pseudo name to the short pseudo name.
33
+ * Pseudos that match here will be ordered,
34
+ * everythin else will make their way to the catch all style bucket.
35
+ * We reduce the pseduo name to save bundlesize.
36
+ * Thankfully there aren't any overlaps, see: https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes.
37
+ */
38
+ const pseudosMap: Record<string, string> = {
39
+ // link
40
+ k: 'l',
41
+ // visited
42
+ ited: 'v',
43
+ // focus-within
44
+ 'us-within': 'w',
45
+ // focus
46
+ us: 'f',
47
+ // focus-visible
48
+ 'us-visible': 'i',
49
+ // hover
50
+ er: 'h',
51
+ // active
52
+ ive: 'a',
53
+ };
54
+
55
+ type StyleBucketOptions = {
56
+ nonce?: string;
57
+ };
58
+
59
+ /**
60
+ * Lazily adds a `<style>` bucket to the `<head>`.
61
+ * This will ensure that the style buckets are ordered.
62
+ *
63
+ * @param bucketName Bucket to insert in the head.
64
+ * @param opts
65
+ */
66
+ function lazyAddStyleBucketToHead(
67
+ bucketName: string,
68
+ { nonce }: StyleBucketOptions
69
+ ): HTMLStyleElement {
70
+ if (!styleBucketsInHead[bucketName]) {
71
+ let currentBucketIndex = styleBucketOrdering.indexOf(bucketName) + 1;
72
+ let nextBucketFromCache = null;
73
+
74
+ for (; currentBucketIndex < styleBucketOrdering.length; currentBucketIndex++) {
75
+ // Find the next bucket which we will add our new style bucket before.
76
+ const nextBucket = styleBucketsInHead[styleBucketOrdering[currentBucketIndex]];
77
+ if (nextBucket) {
78
+ nextBucketFromCache = nextBucket;
79
+ break;
80
+ }
81
+ }
82
+
83
+ const tag = document.createElement('style');
84
+ nonce && tag.setAttribute('nonce', nonce);
85
+ tag.appendChild(document.createTextNode(''));
86
+ styleBucketsInHead[bucketName] = tag;
87
+ document.head.insertBefore(tag, nextBucketFromCache);
88
+ }
89
+
90
+ return styleBucketsInHead[bucketName]!;
91
+ }
92
+
93
+ /**
94
+ * Gets the bucket depending on the sheet.
95
+ * This function makes assumptions as to the form of the input class name.
96
+ *
97
+ * Input:
98
+ *
99
+ * ```
100
+ * "._a1234567:hover{ color: red; }"
101
+ * ```
102
+ *
103
+ * Output:
104
+ *
105
+ * ```
106
+ * "h"
107
+ * ```
108
+ *
109
+ * @param sheet styles for which we are getting the bucket
110
+ */
111
+ const getStyleBucketName = (sheet: string): string => {
112
+ // We are grouping all the at-rules like @media, @supports etc under `m` bucket.
113
+ if (sheet.charCodeAt(0) === 64 /* "@" */) {
114
+ return 'm';
115
+ }
116
+
117
+ /**
118
+ * We assume that classname will always be 9 character long,
119
+ * using this the 10th character could be a pseudo declaration.
120
+ */
121
+ if (sheet.charCodeAt(10) === 58 /* ":" */) {
122
+ // We send through a subset of the string instead of the full pseudo name.
123
+ // For example `"focus-visible"` name would instead of `"us-visible"`.
124
+ // Return a mapped pseudo else the default catch all bucket.
125
+ return pseudosMap[sheet.slice(14, sheet.indexOf('{'))] || '';
126
+ }
127
+
128
+ // Return default catch all bucket
129
+ return '';
130
+ };
131
+
132
+ export type CreateStyleSheetOptions = StyleBucketOptions;
133
+
134
+ /**
135
+ * Returns a style sheet object that is used to move styles to the head of the application during runtime.
136
+ *
137
+ * @param opts StyleSheetOpts
138
+ */
139
+ export function createStyleSheet(opts: CreateStyleSheetOptions) {
140
+ return (css: string): void => {
141
+ const bucketName = getStyleBucketName(css);
142
+ const style = lazyAddStyleBucketToHead(bucketName, opts);
143
+
144
+ if (process.env.NODE_ENV === 'production') {
145
+ const sheet = style.sheet as CSSStyleSheet;
146
+ sheet.insertRule(css, sheet.cssRules.length);
147
+ } else {
148
+ style.appendChild(document.createTextNode(css));
149
+ }
150
+ };
151
+ }
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { renderToStaticMarkup, renderToString } from 'react-dom/server';
3
+
3
4
  import Style from '../style';
4
5
 
5
6
  describe('<Style />', () => {
@@ -1,103 +1,137 @@
1
- import React from 'react';
2
1
  import { render } from '@testing-library/react';
3
- import Style from '../style';
2
+ import React from 'react';
3
+ import type { ComponentType } from 'react';
4
4
 
5
5
  jest.mock('../is-node', () => ({
6
6
  isNodeEnvironment: () => false,
7
7
  }));
8
8
 
9
9
  describe('<Style />', () => {
10
+ let consoleErrorSpy: jest.SpyInstance;
11
+
10
12
  beforeEach(() => {
11
- // Reset style tags in head before each test so that it will remove styles
12
- // injected by test
13
- document.head.querySelectorAll('style').forEach((styleElement) => {
14
- styleElement.textContent = '';
15
- });
13
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
16
14
  });
17
15
 
16
+ afterEach(() => {
17
+ consoleErrorSpy.mockRestore();
18
+ document.head.innerHTML = '';
19
+ });
20
+
21
+ // We want to isolate the test to correctly mimic the environment being loaded in once
22
+ const createIsolatedTest = (callback: (Style: ComponentType) => void) => {
23
+ jest.isolateModules(() => {
24
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
25
+ const Style = require('../style');
26
+
27
+ callback(Style.default);
28
+ });
29
+ };
30
+
18
31
  it('should render nothing on the client', () => {
19
- const { baseElement } = render(<Style>{[`.a { display: block; }`]}</Style>);
32
+ createIsolatedTest((Style) => {
33
+ const { baseElement } = render(<Style>{[`.a { display: block; }`]}</Style>);
20
34
 
21
- expect(baseElement.getElementsByTagName('style')).toHaveLength(0);
35
+ expect(baseElement.getElementsByTagName('style')).toHaveLength(0);
36
+ expect(console.error).not.toHaveBeenCalled();
37
+ });
22
38
  });
23
39
 
24
40
  it('should add style to the head on the client', () => {
25
- render(<Style>{[`.b { display: block; }`]}</Style>);
41
+ createIsolatedTest((Style) => {
42
+ render(<Style>{[`.b { display: block; }`]}</Style>);
26
43
 
27
- expect(document.head.innerHTML).toInclude('<style>.b { display: block; }</style>');
44
+ expect(document.head.innerHTML).toInclude('<style>.b { display: block; }</style>');
45
+ expect(console.error).not.toHaveBeenCalled();
46
+ });
28
47
  });
29
48
 
30
49
  it('should only add one style if it was already added', () => {
31
- render(<Style>{[`.c { display: block; }`]}</Style>);
32
- render(<Style>{[`.c { display: block; }`]}</Style>);
50
+ createIsolatedTest((Style) => {
51
+ render(<Style>{[`.c { display: block; }`]}</Style>);
52
+ render(<Style>{[`.c { display: block; }`]}</Style>);
33
53
 
34
- expect(document.head.innerHTML).toIncludeRepeated('<style>.c { display: block; }</style>', 1);
54
+ expect(document.head.innerHTML).toIncludeRepeated('<style>.c { display: block; }</style>', 1);
55
+ expect(console.error).not.toHaveBeenCalled();
56
+ });
35
57
  });
36
58
 
37
59
  it('should noop in prod', () => {
38
- jest.spyOn(console, 'error');
39
- process.env.NODE_ENV = 'production';
60
+ createIsolatedTest((Style) => {
61
+ process.env.NODE_ENV = 'production';
40
62
 
41
- render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
63
+ render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
42
64
 
43
- expect(console.error).not.toHaveBeenCalled();
65
+ expect(console.error).not.toHaveBeenCalled();
66
+ });
44
67
  });
45
68
 
46
69
  it('should warn in dev when using a dangerous pseduo selector', () => {
47
- jest.spyOn(console, 'error');
48
- process.env.NODE_ENV = 'development';
70
+ createIsolatedTest((Style) => {
71
+ process.env.NODE_ENV = 'development';
49
72
 
50
- render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
73
+ render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
51
74
 
52
- expect(console.error).toHaveBeenCalled();
75
+ expect(console.error).toHaveBeenCalledTimes(1);
76
+ });
53
77
  });
54
78
 
55
79
  it('should warn in dev only once', () => {
56
- jest.spyOn(console, 'error');
57
- process.env.NODE_ENV = 'development';
80
+ createIsolatedTest((Style) => {
81
+ process.env.NODE_ENV = 'development';
58
82
 
59
- render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
60
- render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
83
+ render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
84
+ render(<Style>{[`.c:first-child { display: block; }`]}</Style>);
61
85
 
62
- expect(console.error).toHaveBeenCalledTimes(1);
86
+ expect(console.error).toHaveBeenCalledTimes(1);
87
+ expect(console.error).toHaveBeenCalledWith(
88
+ expect.stringMatching('Selectors ":first-child, :nth-child" are dangerous to use')
89
+ );
90
+ });
63
91
  });
64
92
 
65
93
  it('should render style tags in buckets', () => {
66
- render(
67
- <Style>
68
- {[
69
- `._a1234567:hover{ color: red; }`,
70
- `._b1234567:active{ color: blue; }`,
71
- `._c1234567:link{ color: green; }`,
72
- `._d1234567{ display: block; }`,
73
- `@media (max-width: 800px){ ._e1234567{ color: yellow; } }`,
74
- `._f1234567:focus{ color: pink; }`,
75
- `._g1234567:visited{ color: grey; }`,
76
- `._h1234567:focus-visible{ color: white; }`,
77
- `._i1234567:focus-within{ color: black; }`,
78
- ]}
79
- </Style>
80
- );
81
-
82
- expect(document.head.innerHTML.split('</style>').join('</style>\n')).toMatchInlineSnapshot(`
83
- "<style>._d1234567{ display: block; }</style>
84
- <style>._c1234567:link{ color: green; }</style>
85
- <style>._g1234567:visited{ color: grey; }</style>
86
- <style>._i1234567:focus-within{ color: black; }</style>
87
- <style>._f1234567:focus{ color: pink; }</style>
88
- <style>._h1234567:focus-visible{ color: white; }</style>
89
- <style>._a1234567:hover{ color: red; }</style>
90
- <style>._b1234567:active{ color: blue; }</style>
91
- <style>@media (max-width: 800px){ ._e1234567{ color: yellow; } }</style>
92
- "
93
- `);
94
+ createIsolatedTest((Style) => {
95
+ render(
96
+ <Style>
97
+ {[
98
+ `._a1234567:hover{ color: red; }`,
99
+ `._b1234567:active{ color: blue; }`,
100
+ `._c1234567:link{ color: green; }`,
101
+ `._d1234567{ display: block; }`,
102
+ `@media (max-width: 800px){ ._e1234567{ color: yellow; } }`,
103
+ `._f1234567:focus{ color: pink; }`,
104
+ `._g1234567:visited{ color: grey; }`,
105
+ `._h1234567:focus-visible{ color: white; }`,
106
+ `._i1234567:focus-within{ color: black; }`,
107
+ ]}
108
+ </Style>
109
+ );
110
+
111
+ expect(document.head.innerHTML.split('</style>').join('</style>\n')).toMatchInlineSnapshot(`
112
+ "<style>._d1234567{ display: block; }</style>
113
+ <style>._c1234567:link{ color: green; }</style>
114
+ <style>._g1234567:visited{ color: grey; }</style>
115
+ <style>._i1234567:focus-within{ color: black; }</style>
116
+ <style>._f1234567:focus{ color: pink; }</style>
117
+ <style>._h1234567:focus-visible{ color: white; }</style>
118
+ <style>._a1234567:hover{ color: red; }</style>
119
+ <style>._b1234567:active{ color: blue; }</style>
120
+ <style>@media (max-width: 800px){ ._e1234567{ color: yellow; } }</style>
121
+ "
122
+ `);
123
+ expect(console.error).not.toHaveBeenCalled();
124
+ });
94
125
  });
95
126
 
96
127
  it('should update styles', () => {
97
- const { rerender } = render(<Style>{[`.first-render { display: block; }`]}</Style>);
128
+ createIsolatedTest((Style) => {
129
+ const { rerender } = render(<Style>{[`.first-render { display: block; }`]}</Style>);
98
130
 
99
- rerender(<Style>{[`.second-render { display: block; }`]}</Style>);
131
+ rerender(<Style>{[`.second-render { display: block; }`]}</Style>);
100
132
 
101
- expect(document.head.innerHTML).toInclude('.second-render { display: block; }');
133
+ expect(document.head.innerHTML).toInclude('.second-render { display: block; }');
134
+ expect(console.error).not.toHaveBeenCalled();
135
+ });
102
136
  });
103
137
  });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Flowtype definitions for ax
3
3
  * Generated by Flowgen from a Typescript Definition
4
- * Flowgen v1.14.1
4
+ * Flowgen v1.15.0
5
5
  * @flow
6
6
  */
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Flowtype definitions for css-custom-property
3
3
  * Generated by Flowgen from a Typescript Definition
4
- * Flowgen v1.14.1
4
+ * Flowgen v1.15.0
5
5
  * @flow
6
6
  */
7
7
  /**
@@ -13,7 +13,7 @@
13
13
  * @param prefix
14
14
  */
15
15
  declare export default function cssCustomPropertyValue(
16
- value: string | number | number | null,
16
+ value: string | number | null | void,
17
17
  suffix?: string | void | null,
18
18
  prefix?: string | void | null
19
19
  ): string | number;
@@ -8,7 +8,7 @@
8
8
  * @param prefix
9
9
  */
10
10
  export default function cssCustomPropertyValue(
11
- value: string | number | number | null,
11
+ value: string | number | null | undefined,
12
12
  suffix?: string | undefined | null,
13
13
  prefix?: string | undefined | null
14
14
  ): string | number {
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Flowtype definitions for dev-warnings
3
3
  * Generated by Flowgen from a Typescript Definition
4
- * Flowgen v1.14.1
4
+ * Flowgen v1.15.0
5
5
  * @flow
6
6
  */
7
- declare export var warn: (str: string, ...args: any[]) => void;
8
7
  declare export var analyzeCssInDev: (sheet: string) => void;
@@ -1,7 +1,7 @@
1
1
  const selectorsToWarn = [':first-child', ':nth-child'];
2
2
  const hasWarned: Record<string, true> = {};
3
3
 
4
- export const warn = (str: string, ...args: any[]): void =>
4
+ const warn = (str: string, ...args: any[]): void =>
5
5
  console.error(
6
6
  `
7
7
  ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗██╗ ███████╗██████╗
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Flowtype definitions for index
3
3
  * Generated by Flowgen from a Typescript Definition
4
- * Flowgen v1.14.1
4
+ * Flowgen v1.15.0
5
5
  * @flow
6
6
  */
7
7
  declare export { default as CS } from './style';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Flowtype definitions for is-node
3
3
  * Generated by Flowgen from a Typescript Definition
4
- * Flowgen v1.14.1
4
+ * Flowgen v1.15.0
5
5
  * @flow
6
6
  */
7
7
  /**
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Flowtype definitions for sheet
3
3
  * Generated by Flowgen from a Typescript Definition
4
- * Flowgen v1.14.1
4
+ * Flowgen v1.15.0
5
5
  * @flow
6
6
  */
7
- import type { StyleSheetOpts, Bucket } from './types';
7
+ import type { Bucket, StyleSheetOpts } from './types';
8
8
  /**
9
9
  * Ordered style buckets using their short psuedo name.
10
10
  * If changes are needed make sure that it aligns with the definition in `sort-at-rule-pseudos.tsx`.