Skip to content

Commit

Permalink
refactor: add back local in-memory cache to reduce loops
Browse files Browse the repository at this point in the history
  • Loading branch information
eels committed Oct 26, 2021
1 parent 4613fff commit bf6ff7c
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 38 deletions.
81 changes: 44 additions & 37 deletions src/lib/construct.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cc from 'classcat';
import circularStringify from '@src/utils/circularStringify';
import convertCamelToKebabCase from '@src/utils/convertCamelToKebabCase';
import generateDisplayName from '@src/utils/generateDisplayName';
import hash from '@src/utils/hash';
Expand All @@ -21,71 +22,77 @@ export default function construct<Props = ChicProps>(options: ConstructOptions<P
const classNamesHash = hash(classNamesArray.join(''));

function wrapper() {
const cache: ExtendableObject<Class[]> = {};
const name = generateDisplayName(<any>target);

function styled(props: Props, ref: Ref<Element>) {
const constructedProps = <ChicProps>Object.assign({}, attrs, props);
const constructedPropsHash = hash(circularStringify(constructedProps)).toString(36);
const constructedPropsEntries = Object.entries(constructedProps);
const as = constructedProps.as || target;
const hasValidAs = isType(as, ['function', 'object', 'string']);
const element = hasValidAs && !isTargetObject ? as : target;
const componentClassNames = <Class[]>[];
const dynamicStyleClassNames = <string[]>[];
const dynamicClassNames = <string[]>[];
const componentClassNames = constructedProps.className;
const modifiers: ExtendableObject<Props[Extract<keyof Props, string>]> = {};

if (constructedPropsEntries.length === 0) {
constructedPropsEntries.push([`__chic-${Date.now().toString(36)}`, false]);
}

for (const [prop, value] of constructedPropsEntries) {
for (const className of classNamesArray) {
const stylesLookup = styles[className];
if (!cache[constructedPropsHash]) {
cache[constructedPropsHash] = <Class[]>[];

if (!stylesLookup) {
throw new Error(`Target class "${className}" does not exist in provided module`);
}
for (const [prop, value] of constructedPropsEntries) {
for (const className of classNamesArray) {
const stylesLookup = styles[className];

if (!stylesLookup) {
throw new Error(`Target class "${className}" does not exist in provided module`);
}

componentClassNames.push(stylesLookup);
cache[constructedPropsHash].push(stylesLookup);

if (prefixes.some((prefix) => !!prop.match(`^${prefix}`))) {
const prefix = prop.match(prefixesRegex)?.[0];
const modifier = convertCamelToKebabCase(prop.replace(prefixesRegex, ''));
const baseClassName = `${className}--${modifier.toLowerCase()}`;
const modifierValueExtention = prefix === 'with' ? `-${value}` : '';
const constructedClassName = `${baseClassName}${modifierValueExtention}`;
const constructedStylesLookup = styles[constructedClassName];
if (prefixes.some((prefix) => !!prop.match(`^${prefix}`))) {
const prefix = prop.match(prefixesRegex)?.[0];
const modifier = convertCamelToKebabCase(prop.replace(prefixesRegex, ''));
const baseClassName = `${className}--${modifier.toLowerCase()}`;
const modifierValueExtention = prefix === 'with' ? `-${value}` : '';
const constructedClassName = `${baseClassName}${modifierValueExtention}`;
const constructedStylesLookup = styles[constructedClassName];

if (constructedStylesLookup) {
modifiers[constructedStylesLookup] = value;
if (constructedStylesLookup) {
modifiers[constructedStylesLookup] = value;
}
}
}
}

if (!isTargetObject && !isValidProp(prop)) {
delete constructedProps[prop];
}
if (!isTargetObject && !isValidProp(prop)) {
delete constructedProps[prop];
}

if (!isTargetObject && prop === 'style' && isType(value, 'object')) {
const styleHash = hash(JSON.stringify(value));
const constructedStyleHash = hash(`${classNamesHash}${styleHash}`).toString(36);
const styleEntries = Object.entries(value);
const styleClassName = `cm${constructedStyleHash}`;
const dynamicStyles = <string[]>[];
if (!isTargetObject && prop === 'style' && isType(value, 'object')) {
const styleHash = hash(JSON.stringify(value)).toString(36);
const constructedStyleHash = hash(`${classNamesHash}${styleHash}`).toString(36);
const styleEntries = Object.entries(value);
const styleClassName = `cm${constructedStyleHash}`;
const dynamicStyles = <string[]>[];

delete constructedProps.style;
dynamicStyleClassNames.push(styleClassName);
delete constructedProps.style;
dynamicClassNames.push(styleClassName);

for (const [property, value] of styleEntries) {
dynamicStyles.push(prefixCSSDeclaration(convertCamelToKebabCase(property), value));
}
for (const [property, value] of styleEntries) {
dynamicStyles.push(prefixCSSDeclaration(convertCamelToKebabCase(property), value));
}

insertDynamicCSSRule(styleClassName, dynamicStyles);
insertDynamicCSSRule(styleClassName, dynamicStyles);
}
}
}

componentClassNames.push(modifiers, dynamicStyleClassNames, constructedProps.className);
cache[constructedPropsHash].push(modifiers, dynamicClassNames, componentClassNames);
}

constructedProps.className = cc(componentClassNames);
constructedProps.className = cc(cache[constructedPropsHash]);

const propsRefObject = ref ? { ref } : {};
const propsToForward = Object.assign({}, constructedProps, propsRefObject);
Expand Down
2 changes: 1 addition & 1 deletion test/lib/construct.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe('lib/construct', () => {
};

render(createElement(construct(parameters), { style: { color: 'red' } }));
expect(screen.getByRole('heading')).toHaveClass('cmq487y5');
expect(screen.getByRole('heading')).toHaveClass('cmghm6z');
expect(screen.getByRole('heading')).not.toHaveAttribute('style');
});

Expand Down

0 comments on commit bf6ff7c

Please sign in to comment.