Skip to content

Commit 739a847

Browse files
committed
feat: extractStyle support once (#228)
1 parent 35f041c commit 739a847

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

src/Cache.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class Entity {
1717
/** @private Internal cache map. Do not access this directly */
1818
cache = new Map<string, ValueType>();
1919

20+
extracted: Set<string> = new Set();
21+
2022
get(keys: KeyType[]): ValueType | null {
2123
return this.opGet(pathKey(keys));
2224
}

src/extractStyle.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ export default function extractStyle(
3636
| {
3737
plain?: boolean;
3838
types?: ExtractStyleType | ExtractStyleType[];
39+
once?: boolean;
3940
},
4041
) {
41-
const { plain = false, types = ['style', 'token', 'cssVar'] } =
42+
const { plain = false, types = ['style', 'token', 'cssVar'], once = false } =
4243
typeof options === 'boolean' ? { plain: options } : options || {};
4344

4445
const matchPrefixRegexp = new RegExp(
@@ -60,6 +61,9 @@ export default function extractStyle(
6061

6162
styleKeys
6263
.map<[number, string] | null>((key) => {
64+
if (once && cache.extracted.has(key)) {
65+
return null; // Skip if already extracted
66+
}
6367
const cachePath = key.replace(matchPrefixRegexp, '').replace(/%/g, '|');
6468
const [prefix] = key.split('%');
6569
const extractFn = ExtractStyleFns[prefix as keyof typeof ExtractStyleFns];
@@ -73,6 +77,10 @@ export default function extractStyle(
7377
if (key.startsWith('style')) {
7478
cachePathMap[cachePath] = styleId;
7579
}
80+
81+
// record that this style has been extracted
82+
cache.extracted.add(key);
83+
7684
return [order, styleStr];
7785
})
7886
.filter(isNotNull)

tests/server.spec.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ describe('SSR', () => {
6969
},
7070
});
7171

72+
const genCardStyle = (token: DerivativeToken): CSSInterpolation => ({
73+
'.card': {
74+
backgroundColor: token.primaryColor,
75+
},
76+
});
77+
7278
const Box = ({ children }: { children?: React.ReactNode }) => {
7379
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken]);
7480

@@ -79,6 +85,16 @@ describe('SSR', () => {
7985
return wrapSSR(<div className="box">{children}</div>);
8086
};
8187

88+
const Card = ({ children }: { children?: React.ReactNode }) => {
89+
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
90+
cssVar: { key: 'css-var-test' },
91+
});
92+
93+
useStyleRegister({ theme, token, path: ['.card'] }, () => [genCardStyle(token)]);
94+
95+
return <div className="card">{children}</div>;
96+
};
97+
8298
const IdHolder = () => {
8399
const id = React.useId();
84100
return (
@@ -441,4 +457,33 @@ describe('SSR', () => {
441457
].join(''),
442458
);
443459
});
460+
461+
it('should extract once when once option is true', () => {
462+
const cache = createCache();
463+
464+
renderToString(
465+
<StyleProvider cache={cache}>
466+
<IdHolder />
467+
<Box>
468+
<IdHolder />
469+
</Box>
470+
<IdHolder />
471+
</StyleProvider>,
472+
);
473+
474+
const style = extractStyle(cache, {plain: true, once: true});
475+
476+
renderToString(
477+
<StyleProvider cache={cache}>
478+
<Card />
479+
</StyleProvider>,
480+
);
481+
const style2 = extractStyle(cache, {plain: true, once: true});
482+
483+
expect(style).toContain('.box');
484+
expect(style).not.toContain('.card');
485+
486+
expect(style2).toContain('.card');
487+
expect(style2).not.toContain('.box');
488+
})
444489
});

0 commit comments

Comments
 (0)