Skip to content

Commit 8df14cb

Browse files
tassoevanggazzo
andauthored
perf: Generated classNames from props (#230)
Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com>
1 parent e7faeba commit 8df14cb

15 files changed

+155
-98
lines changed

packages/css-in-js/.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = {
99
'newlines-between': 'always',
1010
groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']]
1111
}],
12+
'no-extra-parens': 'off'
1213
},
1314
'env': {
1415
'jest': true,

packages/css-in-js/README.md

+55-31
Original file line numberDiff line numberDiff line change
@@ -27,40 +27,33 @@ yarn test
2727

2828
#### Table of Contents
2929

30-
- [css](#css)
30+
- [createSelector](#createselector)
3131
- [Parameters](#parameters)
32-
- [keyframes](#keyframes)
33-
- [Parameters](#parameters-1)
3432
- [attachRules](#attachrules)
35-
- [Parameters](#parameters-2)
33+
- [Parameters](#parameters-1)
3634
- [referenceRules](#referencerules)
35+
- [Parameters](#parameters-2)
36+
- [css](#css)
3737
- [Parameters](#parameters-3)
38-
- [transpile](#transpile)
38+
- [className](#classname)
3939
- [Parameters](#parameters-4)
40-
- [createSelector](#createselector)
40+
- [keyframes](#keyframes)
4141
- [Parameters](#parameters-5)
42+
- [toClassName](#toclassname)
43+
- [Parameters](#parameters-6)
44+
- [transpile](#transpile)
45+
- [Parameters](#parameters-7)
4246

43-
### css
44-
45-
Template string tag to declare CSS content chunks.
46-
47-
#### Parameters
48-
49-
- `slices` **TemplateStringsArray**
50-
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**
51-
52-
Returns **any** a callback to render the CSS content
53-
54-
### keyframes
47+
### createSelector
5548

56-
Template string tag to declare CSS `@keyframe` at-rules.
49+
Creates a pair of selector and escaped selector for a CSS Modules content.
5750

5851
#### Parameters
5952

60-
- `slices` **TemplateStringsArray**
61-
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**
53+
- `content` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
6254

63-
Returns **any** a callback to render the CSS at-rule content
55+
Returns **\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]** a pair of selectors in the format `rcx-@<content hash>`; the second element is escaped
56+
for use in CSS content
6457

6558
### attachRules
6659

@@ -87,27 +80,58 @@ style sheet.
8780

8881
Returns **any** a callback to unreference the rules
8982

90-
### transpile
83+
### css
9184

92-
Transpiles CSS Modules content to CSS rules.
85+
Template string tag to declare CSS content chunks.
9386

9487
#### Parameters
9588

96-
- `selector` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
97-
- `content` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
89+
- `slices` **TemplateStringsArray**
90+
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**
9891

99-
Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
92+
Returns **cssFn** a callback to render the CSS content
10093

101-
### createSelector
94+
### className
10295

103-
Creates a pair of selector and escaped selector for a CSS Modules content.
96+
Template string tag to declare CSS content within a className.
10497

10598
#### Parameters
10699

100+
- `className` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
101+
102+
Returns **any** a callback to render the CSS content
103+
104+
### keyframes
105+
106+
Template string tag to declare CSS `@keyframe` at-rules.
107+
108+
#### Parameters
109+
110+
- `slices` **TemplateStringsArray**
111+
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**
112+
113+
Returns **any** a callback to render the CSS at-rule content
114+
115+
### toClassName
116+
117+
Process a value as a className.
118+
119+
#### Parameters
120+
121+
- `value` **(cssFn | classNameFn | [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))**
122+
123+
Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** a string containing a className or undefined
124+
125+
### transpile
126+
127+
Transpiles CSS Modules content to CSS rules.
128+
129+
#### Parameters
130+
131+
- `selector` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
107132
- `content` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
108133

109-
Returns **\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]** a pair of selectors in the format `rcx-@<content hash>`; the second element is escaped
110-
for use in CSS content
134+
Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
111135

112136
## Author
113137

packages/css-in-js/src/index.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @flow
22

3-
export * from './tags';
3+
export * from './selectors';
44
export * from './sheet';
5+
export * from './tags';
6+
export * from './toClassName';
57
export * from './transpile';
6-
export * from './selectors';

packages/css-in-js/src/tags.js

+17-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ declare class TemplateStringsArray extends $ReadOnlyArray<string> {
66
+raw: string;
77
}
88

9+
export type cssFn = (rules: string[]) => string;
10+
11+
export type classNameFn = {
12+
(rules: string[]): string,
13+
className: string,
14+
};
15+
916
const createReplacementsMapping = (rules = []) => (value) => {
1017
if (value === 0) {
1118
return '0';
@@ -27,11 +34,20 @@ const createReplacementsMapping = (rules = []) => (value) => {
2734
*
2835
* @return a callback to render the CSS content
2936
*/
30-
export const css = (slices: TemplateStringsArray, ...values: string[]) => (rules: string[] = []) => {
37+
export const css = (slices: TemplateStringsArray, ...values: string[]): cssFn => (rules: string[] = []) => {
3138
const replacements = values.map(createReplacementsMapping(rules));
3239
return String.raw(slices, ...replacements);
3340
};
3441

42+
/**
43+
* Template string tag to declare CSS content within a className.
44+
*
45+
* @return a callback to render the CSS content
46+
*/
47+
export const className = (className: string) =>
48+
(slices: TemplateStringsArray, ...values: string[]): classNameFn =>
49+
Object.assign((css(slices, ...values): classNameFn), { className });
50+
3551
/**
3652
* Template string tag to declare CSS `@keyframe` at-rules.
3753
*

packages/css-in-js/src/toClassName.js

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// @flow
2+
3+
import { createSelector } from './selectors';
4+
import { referenceRules } from './sheet';
5+
import { transpile } from './transpile';
6+
import type { cssFn, classNameFn } from './tags';
7+
8+
/**
9+
* Process a value as a className.
10+
*
11+
* @return a string containing a className or undefined
12+
*/
13+
export const toClassName = (value: cssFn | classNameFn | string): ?string => {
14+
if (typeof value === 'function') {
15+
const rules = [];
16+
rules.push(value(rules));
17+
18+
const content = rules.filter(Boolean).join('') || undefined;
19+
20+
if (!content) {
21+
return undefined;
22+
}
23+
24+
const className = (value: classNameFn).className || createSelector(content)[0];
25+
const encodedClassName = className.replace(/@|#|:/g, (char) => `\\${ char }`);
26+
27+
const parsedRules = transpile(`.${ encodedClassName }`, content);
28+
referenceRules(parsedRules);
29+
30+
return className;
31+
}
32+
33+
if (typeof value === 'string' && value) {
34+
return value;
35+
}
36+
37+
return undefined;
38+
};

packages/fuselage/src/components/Box/mergeProps.js

+3-30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createSelector, transpile, referenceRules } from '@rocket.chat/css-in-js';
1+
import { toClassName } from '@rocket.chat/css-in-js';
22

33
import { mapClassNames } from './mapClassNames';
44
import { mapSpaceProps } from '../../styles/props/spaces';
@@ -38,38 +38,11 @@ export const mergeProps = (props, contextProps, ref) => {
3838
mapPositionProps,
3939
].reduce((props, transform) => transform(props), initialProps);
4040

41-
const rules = [];
42-
4341
mergedProps.className = Array.from(
4442
new Set(
45-
mergedProps.className.map((className) => {
46-
if (typeof className === 'function') {
47-
rules.push(className(rules));
48-
return undefined;
49-
}
50-
51-
if (typeof className === 'string') {
52-
return className;
53-
}
54-
55-
return undefined;
56-
}).filter(Boolean),
43+
mergedProps.className.map(toClassName).filter(Boolean),
5744
),
58-
);
59-
60-
mergedProps.className = mergedProps.className.join(' ');
61-
62-
const content = rules.filter(Boolean).join('') || undefined;
63-
64-
if (!content) {
65-
return mergedProps;
66-
}
67-
68-
const [className, encodedClassName] = createSelector(content);
69-
const parsedRules = transpile(`.rcx-box.${ encodedClassName }`, content);
70-
referenceRules(parsedRules);
71-
72-
mergedProps.className += ` ${ className }`;
45+
).join(' ');
7346

7447
return mergedProps;
7548
};

packages/fuselage/src/styles/helpers.js packages/fuselage/src/helpers/createLogicalProperties.js

+1-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { css } from '@rocket.chat/css-in-js';
22

3-
import { memoize } from '../helpers/memoize';
3+
import { memoize } from './memoize';
44

55
export const cssSupports = memoize((value) => typeof window !== 'undefined' && window.CSS && window.CSS.supports(value));
66

@@ -109,27 +109,3 @@ export const createLogicalProperties = ({
109109
inlineEndProperty,
110110
];
111111
};
112-
113-
export const createPropType = (getValue) => {
114-
const propType = (props, propName, componentName) => {
115-
const propValue = props[propName];
116-
117-
if (propValue === undefined || getValue(propValue) !== undefined) {
118-
return;
119-
}
120-
121-
return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
122-
};
123-
124-
propType.isRequired = (props, propName, componentName) => {
125-
const propValue = props[propName];
126-
127-
if (propValue !== undefined && getValue(propValue) !== undefined) {
128-
return;
129-
}
130-
131-
return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
132-
};
133-
134-
return propType;
135-
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export const createPropType = (getValue) => {
2+
const propType = (props, propName, componentName) => {
3+
const propValue = props[propName];
4+
5+
if (propValue === undefined || getValue(propValue) !== undefined) {
6+
return;
7+
}
8+
9+
return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
10+
};
11+
12+
propType.isRequired = (props, propName, componentName) => {
13+
const propValue = props[propName];
14+
15+
if (propValue !== undefined && getValue(propValue) !== undefined) {
16+
return;
17+
}
18+
19+
return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
20+
};
21+
22+
return propType;
23+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { memoize } from './memoize';
2+
3+
export const cssSupports = memoize((value) => typeof window !== 'undefined' && window.CSS && window.CSS.supports(value));

packages/fuselage/src/styles/props/borders.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { css } from '@rocket.chat/css-in-js';
22

3+
import { createLogicalProperties } from '../../helpers/createLogicalProperties';
4+
import { cssSupports } from '../../helpers/cssSupports';
35
import { memoize } from '../../helpers/memoize';
4-
import { createLogicalProperties, cssSupports } from '../helpers';
56
import { getColorValue } from './colors';
67

78
export const [

packages/fuselage/src/styles/props/colors.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { css } from '@rocket.chat/css-in-js';
22
import tokenColors from '@rocket.chat/fuselage-tokens/colors';
33
import invariant from 'invariant';
44

5+
import { createPropType } from '../../helpers/createPropType';
6+
import { cssSupports } from '../../helpers/cssSupports';
57
import { memoize } from '../../helpers/memoize';
6-
import { cssSupports, createPropType } from '../helpers';
7-
88

99
const mapTypeToPrefix = {
1010
neutral: 'n',

packages/fuselage/src/styles/props/layout.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { css } from '@rocket.chat/css-in-js';
22

3+
import { createPropType } from '../../helpers/createPropType';
34
import { memoize } from '../../helpers/memoize';
4-
import { createPropType } from '../helpers';
55

66
export const getSizeValue = memoize((propValue) => {
77
if (propValue === undefined || propValue === null) {

packages/fuselage/src/styles/props/position.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { css } from '@rocket.chat/css-in-js';
22

3+
import { createLogicalProperties } from '../../helpers/createLogicalProperties';
4+
import { createPropType } from '../../helpers/createPropType';
5+
import { cssSupports } from '../../helpers/cssSupports';
36
import { memoize } from '../../helpers/memoize';
4-
import { cssSupports, createLogicalProperties, createPropType } from '../helpers';
5-
67

78
export const getInsetValue = memoize((propValue) => {
89
if (propValue === undefined || propValue === null) {

packages/fuselage/src/styles/props/spaces.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { css } from '@rocket.chat/css-in-js';
22

3+
import { createLogicalProperties } from '../../helpers/createLogicalProperties';
4+
import { createPropType } from '../../helpers/createPropType';
35
import { memoize } from '../../helpers/memoize';
4-
import { createPropType, createLogicalProperties } from '../helpers';
5-
66

77
export const getMarginValue = memoize((propValue) => {
88
if (propValue === undefined || propValue === null) {

packages/fuselage/src/styles/props/typography.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { css } from '@rocket.chat/css-in-js';
22
import tokenTypography from '@rocket.chat/fuselage-tokens/typography';
33
import PropTypes from 'prop-types';
44

5-
import { cssSupports } from '../helpers';
5+
import { cssSupports } from '../../helpers/cssSupports';
66
import { getSizeValue } from './layout';
77

88
export const getFontFamilyValue = (propValue) => {

0 commit comments

Comments
 (0)