Skip to content

Commit

Permalink
feat: add "classNameHashSalt" for AOT
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter committed May 16, 2024
1 parent 3cac9f0 commit 8889486
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { makeStyles } from '@griffel/react';

export const useStyles = makeStyles({
root: { color: 'red', paddingLeft: '4px' },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { __styles } from '@griffel/react';
export const useStyles = __styles(
{
root: {
sj55zd: 'feohi3x',
uwmqm3: ['f1rwgqon', 'f1tyzn0d'],
},
},
{
d: ['.feohi3x{color:red;}', '.f1rwgqon{padding-left:4px;}', '.f1tyzn0d{padding-right:4px;}'],
},
);
3 changes: 3 additions & 0 deletions packages/babel-preset/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export const configSchema: JSONSchema7 = {

type: 'object',
properties: {
classNameHashSalt: {
type: 'string',
},
generateMetadata: {
type: 'boolean',
},
Expand Down
8 changes: 8 additions & 0 deletions packages/babel-preset/src/transformPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ pluginTester({
// Configs
//
//
{
title: 'config: classNameHashSalt',
fixture: path.resolve(fixturesDir, 'config-classname-hash-salt', 'code.ts'),
outputFixture: path.resolve(fixturesDir, 'config-classname-hash-salt', 'output.ts'),
pluginOptions: {
classNameHashSalt: 'prefix',
},
},
{
title: 'config: babelOptions',
fixture: path.resolve(fixturesDir, 'config-babel-options', 'code.ts'),
Expand Down
3 changes: 3 additions & 0 deletions packages/babel-preset/src/transformPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export const transformPlugin = declare<Partial<BabelPluginOptions>, PluginObj<Ba

const pluginOptions: Required<BabelPluginOptions> = {
babelOptions: {},
classNameHashSalt: '',
generateMetadata: false,
modules: [
{ moduleSource: '@griffel/react', importName: 'makeStyles' },
Expand Down Expand Up @@ -296,6 +297,7 @@ export const transformPlugin = declare<Partial<BabelPluginOptions>, PluginObj<Ba
state.filename as string,
stylesBySlots,
),
options.classNameHashSalt,
);
const uniqueCSSRules = dedupeCSSRules(cssRulesByBucket);

Expand Down Expand Up @@ -328,6 +330,7 @@ export const transformPlugin = declare<Partial<BabelPluginOptions>, PluginObj<Ba
state.filename as string,
styles,
),
options.classNameHashSalt,
);

if (pluginOptions.generateMetadata) {
Expand Down
2 changes: 2 additions & 0 deletions packages/babel-preset/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { TransformOptions } from '@babel/core';
import type { EvalRule } from '@linaria/babel-preset';

export type BabelPluginOptions = {
classNameHashSalt?: string;

/**
* Returns the evaluated CSS rules in the file result metadata
* @default false
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/resolveStyleRulesForSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@ import type { CSSClassesMapBySlot, CSSRulesByBucket, StyleBucketName, StylesBySl
* Calls resolveStyleRules() for each slot, is also used by build time transform.
*
* @param stylesBySlots - An object with makeStyles rules where a key is a slot name
* @param classNameHashSalt - A salt for classes hash
*
* @return - A tuple with an object classnames mapping where a key is a slot name and an array with CSS rules
*/
export function resolveStyleRulesForSlots<Slots extends string | number>(
stylesBySlots: StylesBySlots<Slots>,
classNameHashSalt: string = '',
): [CSSClassesMapBySlot<Slots>, CSSRulesByBucket] {
const classesMapBySlot = {} as CSSClassesMapBySlot<Slots>;
const cssRules: CSSRulesByBucket = {};

// eslint-disable-next-line guard-for-in
for (const slotName in stylesBySlots) {
const slotStyles: GriffelStyle = stylesBySlots[slotName];
const [cssClassMap, cssRulesByBucket] = resolveStyleRules(slotStyles);
const [cssClassMap, cssRulesByBucket] = resolveStyleRules(slotStyles, classNameHashSalt);

classesMapBySlot[slotName] = cssClassMap;

Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/runtime/resolveResetStyleRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,11 @@ function createStringFromStyles(styles: GriffelResetStyle) {
*/
export function resolveResetStyleRules(
styles: GriffelResetStyle,
classNameHashSalt: string = '',
): [string, string | null, CSSRulesByBucket | string[]] {
const [ltrRule, rtlRule] = createStringFromStyles(styles);

const ltrClassName = RESET_HASH_PREFIX + hashString(ltrRule);
const ltrClassName = RESET_HASH_PREFIX + hashString(classNameHashSalt + ltrRule);
const [ltrCSS, ltrCSSAtRules] = compileResetCSSRules(`.${ltrClassName}{${ltrRule}}`);

const hasAtRules = ltrCSSAtRules.length > 0;
Expand All @@ -150,7 +151,7 @@ export function resolveResetStyleRules(
return [ltrClassName, null, hasAtRules ? { r: ltrCSS, s: ltrCSSAtRules } : ltrCSS];
}

const rtlClassName = RESET_HASH_PREFIX + hashString(rtlRule);
const rtlClassName = RESET_HASH_PREFIX + hashString(classNameHashSalt + rtlRule);
const [rtlCSS, rtlCSSAtRules] = compileResetCSSRules(`.${rtlClassName}{${rtlRule}}`);

return [
Expand Down
16 changes: 13 additions & 3 deletions packages/core/src/runtime/resolveStyleRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ function pushToCSSRules(
*/
export function resolveStyleRules(
styles: GriffelStyle,
classNameHashSalt: string = '',
selectors: string[] = [],
atRules: AtRules = {
container: '',
Expand Down Expand Up @@ -129,14 +130,15 @@ export function resolveStyleRules(
const shorthandProperties = shorthand[1];
const shorthandResetStyles = Object.fromEntries(shorthandProperties.map(property => [property, RESET]));

resolveStyleRules(shorthandResetStyles, selectors, atRules, cssClassesMap, cssRulesByBucket);
resolveStyleRules(shorthandResetStyles, classNameHashSalt, selectors, atRules, cssClassesMap, cssRulesByBucket);
}

// uniq key based on a hash of property & selector, used for merging later
const key = hashPropertyKey(selector, property, atRules);
const className = hashClassName(
{
value: value.toString(),
salt: classNameHashSalt,
selector,
property,
},
Expand All @@ -151,6 +153,7 @@ export function resolveStyleRules(
{
value: rtlDefinition.value.toString(),
property: rtlDefinition.key,
salt: classNameHashSalt,
selector,
},
atRules,
Expand Down Expand Up @@ -228,6 +231,7 @@ export function resolveStyleRules(

resolveStyleRules(
{ animationName: animationNames.join(', ') },
classNameHashSalt,
selectors,
atRules,
cssClassesMap,
Expand All @@ -252,21 +256,21 @@ export function resolveStyleRules(
const shorthandProperties = shorthand[1];
const shorthandResetStyles = Object.fromEntries(shorthandProperties.map(property => [property, RESET]));

resolveStyleRules(shorthandResetStyles, selectors, atRules, cssClassesMap, cssRulesByBucket);
resolveStyleRules(shorthandResetStyles, classNameHashSalt, selectors, atRules, cssClassesMap, cssRulesByBucket);
}

const key = hashPropertyKey(selector, property, atRules);
const className = hashClassName(
{
value: value.map(v => (v ?? '').toString()).join(';'),
salt: classNameHashSalt,
selector,
property,
},
atRules,
);

const rtlDefinitions = value.map(v => convertProperty(property, v!));

const rtlPropertyConsistent = !rtlDefinitions.some(v => v.key !== rtlDefinitions[0].key);

if (!rtlPropertyConsistent) {
Expand All @@ -284,6 +288,7 @@ export function resolveStyleRules(
? hashClassName(
{
value: rtlDefinitions.map(v => (v?.value ?? '').toString()).join(';'),
salt: classNameHashSalt,
property: rtlDefinitions[0].key,
selector,
},
Expand Down Expand Up @@ -324,6 +329,7 @@ export function resolveStyleRules(
if (isNestedSelector(property)) {
resolveStyleRules(
value as GriffelStyle,
classNameHashSalt,
selectors.concat(normalizeNestedProperty(property)),
atRules,
cssClassesMap,
Expand All @@ -334,6 +340,7 @@ export function resolveStyleRules(

resolveStyleRules(
value as GriffelStyle,
classNameHashSalt,
selectors,
{ ...atRules, media: combinedMediaQuery },
cssClassesMap,
Expand All @@ -344,6 +351,7 @@ export function resolveStyleRules(

resolveStyleRules(
value as GriffelStyle,
classNameHashSalt,
selectors,
{ ...atRules, layer: combinedLayerQuery },
cssClassesMap,
Expand All @@ -354,6 +362,7 @@ export function resolveStyleRules(

resolveStyleRules(
value as GriffelStyle,
classNameHashSalt,
selectors,
{ ...atRules, supports: combinedSupportQuery },
cssClassesMap,
Expand All @@ -367,6 +376,7 @@ export function resolveStyleRules(

resolveStyleRules(
value as GriffelStyle,
classNameHashSalt,
selectors,
{ ...atRules, container: containerQuery },
cssClassesMap,
Expand Down
31 changes: 31 additions & 0 deletions packages/core/src/runtime/utils/hashClassName.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { hashClassName } from './hashClassName';

const defaultOptions = {
property: 'color',
selector: '',
value: 'red',

salt: '',
};
const defaultAtRules = {
container: '',
media: '',
layer: '',
supports: '',
};

describe('hashClassName', () => {
it('should hash the className', () => {
expect(hashClassName(defaultOptions, defaultAtRules)).toMatchInlineSnapshot(`"fe3e8s9"`);
});

it('should use salt for hash', () => {
const withoutSalt = hashClassName(defaultOptions, defaultAtRules);
const withSalt = hashClassName({ ...defaultOptions, salt: 'HASH_SALT' }, defaultAtRules);

expect(withoutSalt).not.toEqual(withSalt);

expect(withoutSalt).toMatchInlineSnapshot(`"fe3e8s9"`);
expect(withSalt).toMatchInlineSnapshot(`"f3mwu0g"`);
});
});
6 changes: 4 additions & 2 deletions packages/core/src/runtime/utils/hashClassName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import type { AtRules } from './types';
interface HashedClassNameParts {
property: string;
value: string;
salt: string;
selector: string;
}

export function hashClassName({ property, selector, value }: HashedClassNameParts, atRules: AtRules): string {
export function hashClassName({ property, selector, salt, value }: HashedClassNameParts, atRules: AtRules): string {
return (
HASH_PREFIX +
hashString(
selector +
salt +
selector +
atRules.container +
atRules.media +
atRules.layer +
Expand Down
1 change: 1 addition & 0 deletions packages/vite-plugin/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"test": {
"executor": "nx:run-commands",
"dependsOn": [{ "target": "build", "dependencies": true }],
"options": {
"cwd": "packages/vite-plugin",
"commands": [{ "command": "node --test **/*.test.mjs" }],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { makeStyles } from '@griffel/react';

export const styles = makeStyles({
root: { color: 'red', paddingLeft: '10px' },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { __styles } from '@griffel/react';
export const styles = __styles(
{
root: {
sj55zd: 'f3mwu0g',
uwmqm3: ['f1rfztu6', 'f1h66kgv'],
},
},
{
d: ['.f3mwu0g{color:red;}', '.f1rfztu6{padding-left:10px;}', '.f1h66kgv{padding-right:10px;}'],
},
);
6 changes: 6 additions & 0 deletions packages/webpack-loader/src/webpackLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ describe('webpackLoader', () => {
testFixture('empty');

// Integration fixtures for config functionality
testFixture('config-classname-hash-salt', {
loaderOptions: {
classNameHashSalt: 'HASH_SALT',
},
});

testFixture('config-modules', {
loaderOptions: {
modules: [{ moduleSource: 'react-make-styles', importName: 'makeStyles' }],
Expand Down

0 comments on commit 8889486

Please sign in to comment.