Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class Entity {
/** @private Record update times for each key */
updateTimes = new Map<string, number>();

extracted: Set<string> = new Set();

get(keys: KeyType[]): ValueType | null {
return this.opGet(pathKey(keys));
}
Expand Down
11 changes: 10 additions & 1 deletion src/extractStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ export default function extractStyle(
| {
plain?: boolean;
types?: ExtractStyleType | ExtractStyleType[];
once?: boolean;
},
) {
const { plain = false, types = ['style', 'token', 'cssVar'] } =
const { plain = false, types = ['style', 'token', 'cssVar'], once = false } =
typeof options === 'boolean' ? { plain: options } : options || {};

const matchPrefixRegexp = new RegExp(
Expand All @@ -60,6 +61,10 @@ export default function extractStyle(

styleKeys
.map<[order: number, style: string, updateTime: number] | null>((key) => {
if (once && cache.extracted.has(key)) {
return null; // Skip if already extracted
}

const cachePath = key.replace(matchPrefixRegexp, '').replace(/%/g, '|');
const [prefix] = key.split('%');
const extractFn = ExtractStyleFns[prefix as keyof typeof ExtractStyleFns];
Expand All @@ -74,6 +79,10 @@ export default function extractStyle(
if (key.startsWith('style')) {
cachePathMap[cachePath] = styleId;
}

// record that this style has been extracted
cache.extracted.add(key);

return [order, styleStr, updateTime];
})
.filter(isNotNull)
Expand Down
45 changes: 45 additions & 0 deletions tests/server.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ describe('SSR', () => {
},
});

const genCardStyle = (token: DerivativeToken): CSSInterpolation => ({
'.card': {
backgroundColor: token.primaryColor,
},
});

const Box = ({ children }: { children?: React.ReactNode }) => {
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
cssVar: { key: 'css-var-test' },
Expand All @@ -79,6 +85,16 @@ describe('SSR', () => {
return <div className="box">{children}</div>;
};

const Card = ({ children }: { children?: React.ReactNode }) => {
const [token] = useCacheToken<DerivativeToken>(theme, [baseToken], {
cssVar: { key: 'css-var-test' },
});

useStyleRegister({ theme, token, path: ['.card'] }, () => [genCardStyle(token)]);

return <div className="card">{children}</div>;
};

const IdHolder = () => {
const id = React.useId();
return (
Expand Down Expand Up @@ -415,4 +431,33 @@ describe('SSR', () => {
// C B A
testOrder(<C />, <B />, <A />, ['c', 'b', 'a']);
});

it('should extract once when once option is true', () => {
const cache = createCache();

renderToString(
<StyleProvider cache={cache}>
<IdHolder />
<Box>
<IdHolder />
</Box>
<IdHolder />
</StyleProvider>,
);

const style = extractStyle(cache, {plain: true, once: true});

renderToString(
<StyleProvider cache={cache}>
<Card />
</StyleProvider>,
);
const style2 = extractStyle(cache, {plain: true, once: true});

expect(style).toContain('.box');
expect(style).not.toContain('.card');

expect(style2).toContain('.card');
expect(style2).not.toContain('.box');
})
});