@compiled/react 0.18.3 → 0.18.4
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.
- package/dist/browser/runtime/ax.d.ts +13 -14
- package/dist/browser/runtime/ax.js +65 -32
- package/dist/browser/runtime/ax.js.map +1 -1
- package/dist/cjs/runtime/ax.d.ts +13 -14
- package/dist/cjs/runtime/ax.js +65 -32
- package/dist/cjs/runtime/ax.js.map +1 -1
- package/dist/esm/runtime/ax.d.ts +13 -14
- package/dist/esm/runtime/ax.js +65 -32
- package/dist/esm/runtime/ax.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/jest-matcher.test.tsx +13 -0
- package/src/runtime/__perf__/ax.test.ts +45 -26
- package/src/runtime/__tests__/ax.test.ts +4 -2
- package/src/runtime/ax.ts +68 -36
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* where both `group` and `value` are hashes **four characters long**.
|
|
5
|
-
* Class names can be of any length,
|
|
6
|
-
* this function can take both atomic declarations and class names.
|
|
2
|
+
* Create a single string containing all the classnames provided, separated by a space (`" "`).
|
|
3
|
+
* The result will only contain the _last_ atomic style classname for each atomic `group`.
|
|
7
4
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* ax(['_aaaabbbb', '_aaaacccc']);
|
|
7
|
+
* // output
|
|
8
|
+
* '_aaaacccc'
|
|
12
9
|
* ```
|
|
13
10
|
*
|
|
14
|
-
*
|
|
11
|
+
* Format of Atomic style classnames: `_{group}{value}` (`_\w{4}\w{4}`)
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
* '_aaaacccc'
|
|
18
|
-
* ```
|
|
13
|
+
* `ax` will preserve any non atomic style classnames (eg `"border-red"`)
|
|
19
14
|
*
|
|
20
|
-
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* ax(['_aaaabbbb', '_aaaacccc', 'border-red']);
|
|
17
|
+
* // output
|
|
18
|
+
* '_aaaacccc border-red'
|
|
19
|
+
* ```
|
|
21
20
|
*/
|
|
22
21
|
export default function ax(classNames: (string | undefined | null | false)[]): string | undefined;
|
|
@@ -1,53 +1,86 @@
|
|
|
1
|
-
const UNDERSCORE_UNICODE = 95;
|
|
2
1
|
/**
|
|
3
2
|
* This length includes the underscore,
|
|
4
3
|
* e.g. `"_1s4A"` would be a valid atomic group hash.
|
|
5
4
|
*/
|
|
6
5
|
const ATOMIC_GROUP_LENGTH = 5;
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* where both `group` and `value` are hashes **four characters long**.
|
|
11
|
-
* Class names can be of any length,
|
|
12
|
-
* this function can take both atomic declarations and class names.
|
|
7
|
+
* Create a single string containing all the classnames provided, separated by a space (`" "`).
|
|
8
|
+
* The result will only contain the _last_ atomic style classname for each atomic `group`.
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* ax(['_aaaabbbb', '_aaaacccc']);
|
|
12
|
+
* // output
|
|
13
|
+
* '_aaaacccc'
|
|
18
14
|
* ```
|
|
19
15
|
*
|
|
20
|
-
*
|
|
16
|
+
* Format of Atomic style classnames: `_{group}{value}` (`_\w{4}\w{4}`)
|
|
21
17
|
*
|
|
22
|
-
*
|
|
23
|
-
* '_aaaacccc'
|
|
24
|
-
* ```
|
|
18
|
+
* `ax` will preserve any non atomic style classnames (eg `"border-red"`)
|
|
25
19
|
*
|
|
26
|
-
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* ax(['_aaaabbbb', '_aaaacccc', 'border-red']);
|
|
22
|
+
* // output
|
|
23
|
+
* '_aaaacccc border-red'
|
|
24
|
+
* ```
|
|
27
25
|
*/
|
|
28
26
|
export default function ax(classNames) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
27
|
+
// Shortcut: nothing to do
|
|
28
|
+
if (!classNames.length) {
|
|
29
|
+
return;
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if
|
|
31
|
+
// Shortcut: don't need to do anything if we only have a single classname
|
|
32
|
+
if (classNames.length === 1 &&
|
|
33
|
+
classNames[0] &&
|
|
34
|
+
// checking to see if `classNames[0]` is a string that contains other classnames
|
|
35
|
+
!classNames[0].includes(' ')) {
|
|
36
|
+
return classNames[0];
|
|
37
|
+
}
|
|
38
|
+
// Using an object rather than a `Map` as it performed better in our benchmarks.
|
|
39
|
+
// Would be happy to move to `Map` if it proved to be better under real conditions.
|
|
40
|
+
const map = {};
|
|
41
|
+
// Note: using loops to minimize iterations over the collection
|
|
42
|
+
for (const value of classNames) {
|
|
43
|
+
// Exclude all falsy values, which leaves us with populated strings
|
|
44
|
+
if (!value) {
|
|
37
45
|
continue;
|
|
38
46
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
// a `value` can contain multiple classnames
|
|
48
|
+
const list = value.split(' ');
|
|
49
|
+
for (const className of list) {
|
|
50
|
+
/**
|
|
51
|
+
* For atomic style classnames: the `key` is the `group`
|
|
52
|
+
*
|
|
53
|
+
* - Later atomic classnames with the same `group` will override earlier ones
|
|
54
|
+
* (which is what we want).
|
|
55
|
+
* - Assumes atomic classnames are the only things that start with `_`
|
|
56
|
+
* - Could use a regex to ensure that atomic classnames are structured how we expect,
|
|
57
|
+
* but did not add that for now as it did slow things down a bit.
|
|
58
|
+
*
|
|
59
|
+
* For other classnames: the `key` is the whole classname
|
|
60
|
+
* - Okay to remove duplicates as doing so does not impact specificity
|
|
61
|
+
*
|
|
62
|
+
* */
|
|
63
|
+
const key = className.startsWith('_') ? className.slice(0, ATOMIC_GROUP_LENGTH) : className;
|
|
64
|
+
map[key] = className;
|
|
44
65
|
}
|
|
45
66
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
/**
|
|
68
|
+
* We are converting the `map` into a string.
|
|
69
|
+
*
|
|
70
|
+
* The simple way to do this would be `Object.values(map).join(' ')`.
|
|
71
|
+
* However, the approach below performs 10%-20% better in benchmarks.
|
|
72
|
+
*
|
|
73
|
+
* For `ax()` it feels right to squeeze as much runtime performance out as we can.
|
|
74
|
+
*/
|
|
75
|
+
let result = '';
|
|
76
|
+
for (const key in map) {
|
|
77
|
+
result += map[key] + ' ';
|
|
78
|
+
}
|
|
79
|
+
// If we have an empty string, then our `map` was empty.
|
|
80
|
+
if (!result) {
|
|
81
|
+
return;
|
|
50
82
|
}
|
|
51
|
-
|
|
83
|
+
// remove last " " from the result (we added " " at the end of every value)
|
|
84
|
+
return result.trimEnd();
|
|
52
85
|
}
|
|
53
86
|
//# sourceMappingURL=ax.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ax.js","sourceRoot":"","sources":["../../../src/runtime/ax.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ax.js","sourceRoot":"","sources":["../../../src/runtime/ax.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,OAAO,UAAU,EAAE,CAAC,UAAiD;IAC1E,0BAA0B;IAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;QACtB,OAAO;KACR;IAED,yEAAyE;IACzE,IACE,UAAU,CAAC,MAAM,KAAK,CAAC;QACvB,UAAU,CAAC,CAAC,CAAC;QACb,gFAAgF;QAChF,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAC5B;QACA,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;KACtB;IAED,gFAAgF;IAChF,mFAAmF;IACnF,MAAM,GAAG,GAA2B,EAAE,CAAC;IAEvC,+DAA+D;IAC/D,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE;QAC9B,mEAAmE;QACnE,IAAI,CAAC,KAAK,EAAE;YACV,SAAS;SACV;QAED,4CAA4C;QAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE9B,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE;YAC5B;;;;;;;;;;;;iBAYK;YACL,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5F,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;SACtB;KACF;IAED;;;;;;;OAOG;IACH,IAAI,MAAM,GAAW,EAAE,CAAC;IACxB,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE;QACrB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;KAC1B;IAED,wDAAwD;IACxD,IAAI,CAAC,MAAM,EAAE;QACX,OAAO;KACR;IAED,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC"}
|
package/dist/cjs/runtime/ax.d.ts
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* where both `group` and `value` are hashes **four characters long**.
|
|
5
|
-
* Class names can be of any length,
|
|
6
|
-
* this function can take both atomic declarations and class names.
|
|
2
|
+
* Create a single string containing all the classnames provided, separated by a space (`" "`).
|
|
3
|
+
* The result will only contain the _last_ atomic style classname for each atomic `group`.
|
|
7
4
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* ax(['_aaaabbbb', '_aaaacccc']);
|
|
7
|
+
* // output
|
|
8
|
+
* '_aaaacccc'
|
|
12
9
|
* ```
|
|
13
10
|
*
|
|
14
|
-
*
|
|
11
|
+
* Format of Atomic style classnames: `_{group}{value}` (`_\w{4}\w{4}`)
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
* '_aaaacccc'
|
|
18
|
-
* ```
|
|
13
|
+
* `ax` will preserve any non atomic style classnames (eg `"border-red"`)
|
|
19
14
|
*
|
|
20
|
-
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* ax(['_aaaabbbb', '_aaaacccc', 'border-red']);
|
|
17
|
+
* // output
|
|
18
|
+
* '_aaaacccc border-red'
|
|
19
|
+
* ```
|
|
21
20
|
*/
|
|
22
21
|
export default function ax(classNames: (string | undefined | null | false)[]): string | undefined;
|
package/dist/cjs/runtime/ax.js
CHANGED
|
@@ -1,56 +1,89 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const UNDERSCORE_UNICODE = 95;
|
|
4
3
|
/**
|
|
5
4
|
* This length includes the underscore,
|
|
6
5
|
* e.g. `"_1s4A"` would be a valid atomic group hash.
|
|
7
6
|
*/
|
|
8
7
|
const ATOMIC_GROUP_LENGTH = 5;
|
|
9
8
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* where both `group` and `value` are hashes **four characters long**.
|
|
13
|
-
* Class names can be of any length,
|
|
14
|
-
* this function can take both atomic declarations and class names.
|
|
9
|
+
* Create a single string containing all the classnames provided, separated by a space (`" "`).
|
|
10
|
+
* The result will only contain the _last_ atomic style classname for each atomic `group`.
|
|
15
11
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* ax(['_aaaabbbb', '_aaaacccc']);
|
|
14
|
+
* // output
|
|
15
|
+
* '_aaaacccc'
|
|
20
16
|
* ```
|
|
21
17
|
*
|
|
22
|
-
*
|
|
18
|
+
* Format of Atomic style classnames: `_{group}{value}` (`_\w{4}\w{4}`)
|
|
23
19
|
*
|
|
24
|
-
*
|
|
25
|
-
* '_aaaacccc'
|
|
26
|
-
* ```
|
|
20
|
+
* `ax` will preserve any non atomic style classnames (eg `"border-red"`)
|
|
27
21
|
*
|
|
28
|
-
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* ax(['_aaaabbbb', '_aaaacccc', 'border-red']);
|
|
24
|
+
* // output
|
|
25
|
+
* '_aaaacccc border-red'
|
|
26
|
+
* ```
|
|
29
27
|
*/
|
|
30
28
|
function ax(classNames) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return
|
|
29
|
+
// Shortcut: nothing to do
|
|
30
|
+
if (!classNames.length) {
|
|
31
|
+
return;
|
|
34
32
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if
|
|
33
|
+
// Shortcut: don't need to do anything if we only have a single classname
|
|
34
|
+
if (classNames.length === 1 &&
|
|
35
|
+
classNames[0] &&
|
|
36
|
+
// checking to see if `classNames[0]` is a string that contains other classnames
|
|
37
|
+
!classNames[0].includes(' ')) {
|
|
38
|
+
return classNames[0];
|
|
39
|
+
}
|
|
40
|
+
// Using an object rather than a `Map` as it performed better in our benchmarks.
|
|
41
|
+
// Would be happy to move to `Map` if it proved to be better under real conditions.
|
|
42
|
+
const map = {};
|
|
43
|
+
// Note: using loops to minimize iterations over the collection
|
|
44
|
+
for (const value of classNames) {
|
|
45
|
+
// Exclude all falsy values, which leaves us with populated strings
|
|
46
|
+
if (!value) {
|
|
39
47
|
continue;
|
|
40
48
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
// a `value` can contain multiple classnames
|
|
50
|
+
const list = value.split(' ');
|
|
51
|
+
for (const className of list) {
|
|
52
|
+
/**
|
|
53
|
+
* For atomic style classnames: the `key` is the `group`
|
|
54
|
+
*
|
|
55
|
+
* - Later atomic classnames with the same `group` will override earlier ones
|
|
56
|
+
* (which is what we want).
|
|
57
|
+
* - Assumes atomic classnames are the only things that start with `_`
|
|
58
|
+
* - Could use a regex to ensure that atomic classnames are structured how we expect,
|
|
59
|
+
* but did not add that for now as it did slow things down a bit.
|
|
60
|
+
*
|
|
61
|
+
* For other classnames: the `key` is the whole classname
|
|
62
|
+
* - Okay to remove duplicates as doing so does not impact specificity
|
|
63
|
+
*
|
|
64
|
+
* */
|
|
65
|
+
const key = className.startsWith('_') ? className.slice(0, ATOMIC_GROUP_LENGTH) : className;
|
|
66
|
+
map[key] = className;
|
|
46
67
|
}
|
|
47
68
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
/**
|
|
70
|
+
* We are converting the `map` into a string.
|
|
71
|
+
*
|
|
72
|
+
* The simple way to do this would be `Object.values(map).join(' ')`.
|
|
73
|
+
* However, the approach below performs 10%-20% better in benchmarks.
|
|
74
|
+
*
|
|
75
|
+
* For `ax()` it feels right to squeeze as much runtime performance out as we can.
|
|
76
|
+
*/
|
|
77
|
+
let result = '';
|
|
78
|
+
for (const key in map) {
|
|
79
|
+
result += map[key] + ' ';
|
|
80
|
+
}
|
|
81
|
+
// If we have an empty string, then our `map` was empty.
|
|
82
|
+
if (!result) {
|
|
83
|
+
return;
|
|
52
84
|
}
|
|
53
|
-
|
|
85
|
+
// remove last " " from the result (we added " " at the end of every value)
|
|
86
|
+
return result.trimEnd();
|
|
54
87
|
}
|
|
55
88
|
exports.default = ax;
|
|
56
89
|
//# sourceMappingURL=ax.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ax.js","sourceRoot":"","sources":["../../../src/runtime/ax.ts"],"names":[],"mappings":";;AAAA
|
|
1
|
+
{"version":3,"file":"ax.js","sourceRoot":"","sources":["../../../src/runtime/ax.ts"],"names":[],"mappings":";;AAAA;;;GAGG;AACH,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAwB,EAAE,CAAC,UAAiD;IAC1E,0BAA0B;IAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;QACtB,OAAO;KACR;IAED,yEAAyE;IACzE,IACE,UAAU,CAAC,MAAM,KAAK,CAAC;QACvB,UAAU,CAAC,CAAC,CAAC;QACb,gFAAgF;QAChF,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAC5B;QACA,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;KACtB;IAED,gFAAgF;IAChF,mFAAmF;IACnF,MAAM,GAAG,GAA2B,EAAE,CAAC;IAEvC,+DAA+D;IAC/D,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE;QAC9B,mEAAmE;QACnE,IAAI,CAAC,KAAK,EAAE;YACV,SAAS;SACV;QAED,4CAA4C;QAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE9B,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE;YAC5B;;;;;;;;;;;;iBAYK;YACL,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5F,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;SACtB;KACF;IAED;;;;;;;OAOG;IACH,IAAI,MAAM,GAAW,EAAE,CAAC;IACxB,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE;QACrB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;KAC1B;IAED,wDAAwD;IACxD,IAAI,CAAC,MAAM,EAAE;QACX,OAAO;KACR;IAED,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC;AArED,qBAqEC"}
|
package/dist/esm/runtime/ax.d.ts
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* where both `group` and `value` are hashes **four characters long**.
|
|
5
|
-
* Class names can be of any length,
|
|
6
|
-
* this function can take both atomic declarations and class names.
|
|
2
|
+
* Create a single string containing all the classnames provided, separated by a space (`" "`).
|
|
3
|
+
* The result will only contain the _last_ atomic style classname for each atomic `group`.
|
|
7
4
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* ax(['_aaaabbbb', '_aaaacccc']);
|
|
7
|
+
* // output
|
|
8
|
+
* '_aaaacccc'
|
|
12
9
|
* ```
|
|
13
10
|
*
|
|
14
|
-
*
|
|
11
|
+
* Format of Atomic style classnames: `_{group}{value}` (`_\w{4}\w{4}`)
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
* '_aaaacccc'
|
|
18
|
-
* ```
|
|
13
|
+
* `ax` will preserve any non atomic style classnames (eg `"border-red"`)
|
|
19
14
|
*
|
|
20
|
-
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* ax(['_aaaabbbb', '_aaaacccc', 'border-red']);
|
|
17
|
+
* // output
|
|
18
|
+
* '_aaaacccc border-red'
|
|
19
|
+
* ```
|
|
21
20
|
*/
|
|
22
21
|
export default function ax(classNames: (string | undefined | null | false)[]): string | undefined;
|
package/dist/esm/runtime/ax.js
CHANGED
|
@@ -1,53 +1,86 @@
|
|
|
1
|
-
const UNDERSCORE_UNICODE = 95;
|
|
2
1
|
/**
|
|
3
2
|
* This length includes the underscore,
|
|
4
3
|
* e.g. `"_1s4A"` would be a valid atomic group hash.
|
|
5
4
|
*/
|
|
6
5
|
const ATOMIC_GROUP_LENGTH = 5;
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* where both `group` and `value` are hashes **four characters long**.
|
|
11
|
-
* Class names can be of any length,
|
|
12
|
-
* this function can take both atomic declarations and class names.
|
|
7
|
+
* Create a single string containing all the classnames provided, separated by a space (`" "`).
|
|
8
|
+
* The result will only contain the _last_ atomic style classname for each atomic `group`.
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* ax(['_aaaabbbb', '_aaaacccc']);
|
|
12
|
+
* // output
|
|
13
|
+
* '_aaaacccc'
|
|
18
14
|
* ```
|
|
19
15
|
*
|
|
20
|
-
*
|
|
16
|
+
* Format of Atomic style classnames: `_{group}{value}` (`_\w{4}\w{4}`)
|
|
21
17
|
*
|
|
22
|
-
*
|
|
23
|
-
* '_aaaacccc'
|
|
24
|
-
* ```
|
|
18
|
+
* `ax` will preserve any non atomic style classnames (eg `"border-red"`)
|
|
25
19
|
*
|
|
26
|
-
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* ax(['_aaaabbbb', '_aaaacccc', 'border-red']);
|
|
22
|
+
* // output
|
|
23
|
+
* '_aaaacccc border-red'
|
|
24
|
+
* ```
|
|
27
25
|
*/
|
|
28
26
|
export default function ax(classNames) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
27
|
+
// Shortcut: nothing to do
|
|
28
|
+
if (!classNames.length) {
|
|
29
|
+
return;
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if
|
|
31
|
+
// Shortcut: don't need to do anything if we only have a single classname
|
|
32
|
+
if (classNames.length === 1 &&
|
|
33
|
+
classNames[0] &&
|
|
34
|
+
// checking to see if `classNames[0]` is a string that contains other classnames
|
|
35
|
+
!classNames[0].includes(' ')) {
|
|
36
|
+
return classNames[0];
|
|
37
|
+
}
|
|
38
|
+
// Using an object rather than a `Map` as it performed better in our benchmarks.
|
|
39
|
+
// Would be happy to move to `Map` if it proved to be better under real conditions.
|
|
40
|
+
const map = {};
|
|
41
|
+
// Note: using loops to minimize iterations over the collection
|
|
42
|
+
for (const value of classNames) {
|
|
43
|
+
// Exclude all falsy values, which leaves us with populated strings
|
|
44
|
+
if (!value) {
|
|
37
45
|
continue;
|
|
38
46
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
// a `value` can contain multiple classnames
|
|
48
|
+
const list = value.split(' ');
|
|
49
|
+
for (const className of list) {
|
|
50
|
+
/**
|
|
51
|
+
* For atomic style classnames: the `key` is the `group`
|
|
52
|
+
*
|
|
53
|
+
* - Later atomic classnames with the same `group` will override earlier ones
|
|
54
|
+
* (which is what we want).
|
|
55
|
+
* - Assumes atomic classnames are the only things that start with `_`
|
|
56
|
+
* - Could use a regex to ensure that atomic classnames are structured how we expect,
|
|
57
|
+
* but did not add that for now as it did slow things down a bit.
|
|
58
|
+
*
|
|
59
|
+
* For other classnames: the `key` is the whole classname
|
|
60
|
+
* - Okay to remove duplicates as doing so does not impact specificity
|
|
61
|
+
*
|
|
62
|
+
* */
|
|
63
|
+
const key = className.startsWith('_') ? className.slice(0, ATOMIC_GROUP_LENGTH) : className;
|
|
64
|
+
map[key] = className;
|
|
44
65
|
}
|
|
45
66
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
/**
|
|
68
|
+
* We are converting the `map` into a string.
|
|
69
|
+
*
|
|
70
|
+
* The simple way to do this would be `Object.values(map).join(' ')`.
|
|
71
|
+
* However, the approach below performs 10%-20% better in benchmarks.
|
|
72
|
+
*
|
|
73
|
+
* For `ax()` it feels right to squeeze as much runtime performance out as we can.
|
|
74
|
+
*/
|
|
75
|
+
let result = '';
|
|
76
|
+
for (const key in map) {
|
|
77
|
+
result += map[key] + ' ';
|
|
78
|
+
}
|
|
79
|
+
// If we have an empty string, then our `map` was empty.
|
|
80
|
+
if (!result) {
|
|
81
|
+
return;
|
|
50
82
|
}
|
|
51
|
-
|
|
83
|
+
// remove last " " from the result (we added " " at the end of every value)
|
|
84
|
+
return result.trimEnd();
|
|
52
85
|
}
|
|
53
86
|
//# sourceMappingURL=ax.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ax.js","sourceRoot":"","sources":["../../../src/runtime/ax.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ax.js","sourceRoot":"","sources":["../../../src/runtime/ax.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,OAAO,UAAU,EAAE,CAAC,UAAiD;IAC1E,0BAA0B;IAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;QACtB,OAAO;KACR;IAED,yEAAyE;IACzE,IACE,UAAU,CAAC,MAAM,KAAK,CAAC;QACvB,UAAU,CAAC,CAAC,CAAC;QACb,gFAAgF;QAChF,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAC5B;QACA,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;KACtB;IAED,gFAAgF;IAChF,mFAAmF;IACnF,MAAM,GAAG,GAA2B,EAAE,CAAC;IAEvC,+DAA+D;IAC/D,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE;QAC9B,mEAAmE;QACnE,IAAI,CAAC,KAAK,EAAE;YACV,SAAS;SACV;QAED,4CAA4C;QAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE9B,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE;YAC5B;;;;;;;;;;;;iBAYK;YACL,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5F,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;SACtB;KACF;IAED;;;;;;;OAOG;IACH,IAAI,MAAM,GAAW,EAAE,CAAC;IACxB,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE;QACrB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;KAC1B;IAED,wDAAwD;IACxD,IAAI,CAAC,MAAM,EAAE;QACX,OAAO;KACR;IAED,2EAA2E;IAC3E,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC"}
|
package/package.json
CHANGED
|
@@ -18,6 +18,19 @@ describe('toHaveCompliedCss', () => {
|
|
|
18
18
|
expect(getByText('hello world')).toHaveCompiledCss('font-size', '12px');
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
it('should detect styles (SVG)', () => {
|
|
22
|
+
const { getByText } = render(
|
|
23
|
+
<svg
|
|
24
|
+
css={{
|
|
25
|
+
fontSize: '12px',
|
|
26
|
+
}}>
|
|
27
|
+
hello world
|
|
28
|
+
</svg>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expect(getByText('hello world')).toHaveCompiledCss('font-size', '12px');
|
|
32
|
+
});
|
|
33
|
+
|
|
21
34
|
it('should detect missing styles', () => {
|
|
22
35
|
const { getByText } = render(<div css={{ fontSize: '12px' }}>hello world</div>);
|
|
23
36
|
|
|
@@ -3,42 +3,61 @@ import { runBenchmark } from '@compiled/benchmark';
|
|
|
3
3
|
import { ax } from '../index';
|
|
4
4
|
|
|
5
5
|
describe('ax benchmark', () => {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
'_19bv19bv',
|
|
13
|
-
'_bfhk1mzw',
|
|
14
|
-
'_syazu67f',
|
|
15
|
-
'_k48p1nn1',
|
|
16
|
-
'_ect41kw7',
|
|
17
|
-
'_1wybdlk8',
|
|
18
|
-
'_irr3mlcl',
|
|
19
|
-
'_1di6vctu',
|
|
20
|
-
// `undefined` is an acceptable parameter so we want to include it in the test case.
|
|
21
|
-
// Example: ax(['aaaabbbb', foo() && "aaaacccc"])
|
|
22
|
-
undefined,
|
|
6
|
+
const chunks: string[] = ['aaaa', 'bbbb', 'cccc', 'dddd', 'eeee', 'ffff', 'gggg'];
|
|
7
|
+
const uniques: string[] = chunks.map((chunk) => `_${chunk}${chunk}`);
|
|
8
|
+
const withClashes: string[] = [
|
|
9
|
+
...Array.from({ length: 4 }, () => `_${chunks[0]}${chunks[0]}`),
|
|
10
|
+
...Array.from({ length: 6 }, () => `_${chunks[0]}${chunks[1]}`),
|
|
11
|
+
...Array.from({ length: 8 }, () => `_${chunks[0]}${chunks[2]}`),
|
|
23
12
|
];
|
|
24
13
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
const getRandomRules = (() => {
|
|
15
|
+
function randomChunk() {
|
|
16
|
+
return chunks[Math.floor(Math.random() * chunks.length)];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return function create(): string[] {
|
|
20
|
+
return Array.from({ length: 20 }, () => `_${randomChunk()}${randomChunk()}`);
|
|
21
|
+
};
|
|
22
|
+
})();
|
|
28
23
|
|
|
24
|
+
it('completes with ax() string as the fastest', async () => {
|
|
29
25
|
const benchmark = await runBenchmark('ax', [
|
|
30
26
|
{
|
|
31
|
-
name: 'ax()
|
|
32
|
-
fn: () => ax(
|
|
27
|
+
name: 'ax() single',
|
|
28
|
+
fn: () => ax(['_aaaabbbb']),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'ax() uniques (array)',
|
|
32
|
+
fn: () => ax(uniques),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'ax() uniques (as a string)',
|
|
36
|
+
fn: () => ax([uniques.join(' ')]),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'ax() clashes',
|
|
40
|
+
fn: () => ax(withClashes),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'ax() clashes (as a string)',
|
|
44
|
+
fn: () => ax([withClashes.join(' ')]),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'ax() random keys (no clashes)',
|
|
48
|
+
fn: () => ax(getRandomRules()),
|
|
33
49
|
},
|
|
34
50
|
{
|
|
35
|
-
name: 'ax()
|
|
36
|
-
fn: () =>
|
|
51
|
+
name: 'ax() random keys (with clashes)',
|
|
52
|
+
fn: () => {
|
|
53
|
+
const random = getRandomRules();
|
|
54
|
+
ax([...random, ...random, ...random]);
|
|
55
|
+
},
|
|
37
56
|
},
|
|
38
57
|
]);
|
|
39
58
|
|
|
40
59
|
expect(benchmark).toMatchObject({
|
|
41
|
-
fastest: ['ax()
|
|
60
|
+
fastest: ['ax() single'],
|
|
42
61
|
});
|
|
43
|
-
},
|
|
62
|
+
}, 90000);
|
|
44
63
|
});
|
|
@@ -6,6 +6,7 @@ describe('ax', () => {
|
|
|
6
6
|
it.each([
|
|
7
7
|
['should handle empty array', [], undefined],
|
|
8
8
|
['should handle array with undefined', [undefined], undefined],
|
|
9
|
+
['should handle array with falsy values', [undefined, null, false as const, ''], undefined],
|
|
9
10
|
['should join single classes together', ['foo', 'bar'], 'foo bar'],
|
|
10
11
|
['should join multi classes together', ['foo baz', 'bar'], 'foo baz bar'],
|
|
11
12
|
['should remove undefined', ['foo', 'bar', undefined], 'foo bar'],
|
|
@@ -50,7 +51,8 @@ describe('ax', () => {
|
|
|
50
51
|
['hello_there', 'hello_world', '_aaaabbbb'],
|
|
51
52
|
'hello_there hello_world _aaaabbbb',
|
|
52
53
|
],
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
['should remove duplicate custom class names', ['a', 'a'], 'a'],
|
|
55
|
+
])('%s', (_, params, expected) => {
|
|
56
|
+
expect(ax(params)).toEqual(expected);
|
|
55
57
|
});
|
|
56
58
|
});
|
package/src/runtime/ax.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const UNDERSCORE_UNICODE = 95;
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* This length includes the underscore,
|
|
5
3
|
* e.g. `"_1s4A"` would be a valid atomic group hash.
|
|
@@ -7,58 +5,92 @@ const UNDERSCORE_UNICODE = 95;
|
|
|
7
5
|
const ATOMIC_GROUP_LENGTH = 5;
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* where both `group` and `value` are hashes **four characters long**.
|
|
13
|
-
* Class names can be of any length,
|
|
14
|
-
* this function can take both atomic declarations and class names.
|
|
15
|
-
*
|
|
16
|
-
* Input:
|
|
8
|
+
* Create a single string containing all the classnames provided, separated by a space (`" "`).
|
|
9
|
+
* The result will only contain the _last_ atomic style classname for each atomic `group`.
|
|
17
10
|
*
|
|
18
|
-
* ```
|
|
19
|
-
* ax(['_aaaabbbb', '_aaaacccc'])
|
|
11
|
+
* ```ts
|
|
12
|
+
* ax(['_aaaabbbb', '_aaaacccc']);
|
|
13
|
+
* // output
|
|
14
|
+
* '_aaaacccc'
|
|
20
15
|
* ```
|
|
21
16
|
*
|
|
22
|
-
*
|
|
17
|
+
* Format of Atomic style classnames: `_{group}{value}` (`_\w{4}\w{4}`)
|
|
23
18
|
*
|
|
24
|
-
*
|
|
25
|
-
* '_aaaacccc'
|
|
26
|
-
* ```
|
|
19
|
+
* `ax` will preserve any non atomic style classnames (eg `"border-red"`)
|
|
27
20
|
*
|
|
28
|
-
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* ax(['_aaaabbbb', '_aaaacccc', 'border-red']);
|
|
23
|
+
* // output
|
|
24
|
+
* '_aaaacccc border-red'
|
|
25
|
+
* ```
|
|
29
26
|
*/
|
|
30
27
|
export default function ax(classNames: (string | undefined | null | false)[]): string | undefined {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return
|
|
28
|
+
// Shortcut: nothing to do
|
|
29
|
+
if (!classNames.length) {
|
|
30
|
+
return;
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
// Shortcut: don't need to do anything if we only have a single classname
|
|
34
|
+
if (
|
|
35
|
+
classNames.length === 1 &&
|
|
36
|
+
classNames[0] &&
|
|
37
|
+
// checking to see if `classNames[0]` is a string that contains other classnames
|
|
38
|
+
!classNames[0].includes(' ')
|
|
39
|
+
) {
|
|
40
|
+
return classNames[0];
|
|
41
|
+
}
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
// Using an object rather than a `Map` as it performed better in our benchmarks.
|
|
44
|
+
// Would be happy to move to `Map` if it proved to be better under real conditions.
|
|
45
|
+
const map: Record<string, string> = {};
|
|
46
|
+
|
|
47
|
+
// Note: using loops to minimize iterations over the collection
|
|
48
|
+
for (const value of classNames) {
|
|
49
|
+
// Exclude all falsy values, which leaves us with populated strings
|
|
50
|
+
if (!value) {
|
|
41
51
|
continue;
|
|
42
52
|
}
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
// a `value` can contain multiple classnames
|
|
55
|
+
const list = value.split(' ');
|
|
45
56
|
|
|
46
|
-
for (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
for (const className of list) {
|
|
58
|
+
/**
|
|
59
|
+
* For atomic style classnames: the `key` is the `group`
|
|
60
|
+
*
|
|
61
|
+
* - Later atomic classnames with the same `group` will override earlier ones
|
|
62
|
+
* (which is what we want).
|
|
63
|
+
* - Assumes atomic classnames are the only things that start with `_`
|
|
64
|
+
* - Could use a regex to ensure that atomic classnames are structured how we expect,
|
|
65
|
+
* but did not add that for now as it did slow things down a bit.
|
|
66
|
+
*
|
|
67
|
+
* For other classnames: the `key` is the whole classname
|
|
68
|
+
* - Okay to remove duplicates as doing so does not impact specificity
|
|
69
|
+
*
|
|
70
|
+
* */
|
|
71
|
+
const key = className.startsWith('_') ? className.slice(0, ATOMIC_GROUP_LENGTH) : className;
|
|
72
|
+
map[key] = className;
|
|
53
73
|
}
|
|
54
74
|
}
|
|
55
75
|
|
|
56
|
-
|
|
76
|
+
/**
|
|
77
|
+
* We are converting the `map` into a string.
|
|
78
|
+
*
|
|
79
|
+
* The simple way to do this would be `Object.values(map).join(' ')`.
|
|
80
|
+
* However, the approach below performs 10%-20% better in benchmarks.
|
|
81
|
+
*
|
|
82
|
+
* For `ax()` it feels right to squeeze as much runtime performance out as we can.
|
|
83
|
+
*/
|
|
84
|
+
let result: string = '';
|
|
85
|
+
for (const key in map) {
|
|
86
|
+
result += map[key] + ' ';
|
|
87
|
+
}
|
|
57
88
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
89
|
+
// If we have an empty string, then our `map` was empty.
|
|
90
|
+
if (!result) {
|
|
91
|
+
return;
|
|
61
92
|
}
|
|
62
93
|
|
|
63
|
-
|
|
94
|
+
// remove last " " from the result (we added " " at the end of every value)
|
|
95
|
+
return result.trimEnd();
|
|
64
96
|
}
|