Skip to content

Commit 06b1c2b

Browse files
committed
make cloneCandidate and cloneVariant faster
``` cloneCandidate - src/candidate.bench.ts > Candidate cloning 1.72x faster than cloneCandidate (spread) 74.03x faster than structuredClone ```
1 parent 6e5fc73 commit 06b1c2b

File tree

2 files changed

+103
-14
lines changed

2 files changed

+103
-14
lines changed

packages/tailwindcss/src/candidate.bench.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Scanner } from '@tailwindcss/oxide'
22
import { bench, describe } from 'vitest'
3-
import { cloneCandidate, parseCandidate } from './candidate'
3+
import { cloneCandidate, parseCandidate, type Candidate, type Variant } from './candidate'
44
import { buildDesignSystem } from './design-system'
55
import { Theme } from './theme'
66

@@ -37,4 +37,58 @@ describe('Candidate cloning', async () => {
3737
cloneCandidate(candidate)
3838
}
3939
})
40+
41+
bench('cloneCandidate (spread)', () => {
42+
for (let candidate of parsedCanddiates) {
43+
cloneCandidateSpread(candidate)
44+
}
45+
})
4046
})
47+
48+
function cloneCandidateSpread(candidate: Candidate): Candidate {
49+
switch (candidate.kind) {
50+
case 'arbitrary':
51+
return {
52+
...candidate,
53+
modifier: candidate.modifier ? { ...candidate.modifier } : null,
54+
variants: candidate.variants.map(cloneVariantSpread),
55+
}
56+
57+
case 'static':
58+
return { ...candidate, variants: candidate.variants.map(cloneVariantSpread) }
59+
60+
case 'functional':
61+
return {
62+
...candidate,
63+
value: candidate.value ? { ...candidate.value } : null,
64+
modifier: candidate.modifier ? { ...candidate.modifier } : null,
65+
variants: candidate.variants.map(cloneVariantSpread),
66+
}
67+
68+
default:
69+
candidate satisfies never
70+
throw new Error('Unknown candidate kind')
71+
}
72+
}
73+
74+
function cloneVariantSpread(variant: Variant): Variant {
75+
switch (variant.kind) {
76+
case 'arbitrary':
77+
case 'static':
78+
return { ...variant }
79+
80+
case 'functional':
81+
return { ...variant, modifier: variant.modifier ? { ...variant.modifier } : null }
82+
83+
case 'compound':
84+
return {
85+
...variant,
86+
variant: cloneVariantSpread(variant.variant),
87+
modifier: variant.modifier ? { ...variant.modifier } : null,
88+
}
89+
90+
default:
91+
variant satisfies never
92+
throw new Error('Unknown variant kind')
93+
}
94+
}

packages/tailwindcss/src/candidate.ts

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -219,20 +219,49 @@ export function cloneCandidate(candidate: Candidate): Candidate {
219219
switch (candidate.kind) {
220220
case 'arbitrary':
221221
return {
222-
...candidate,
222+
kind: candidate.kind,
223+
property: candidate.property,
224+
value: candidate.value,
225+
modifier: candidate.modifier
226+
? { kind: candidate.modifier.kind, value: candidate.modifier.value }
227+
: null,
223228
variants: candidate.variants.map(cloneVariant),
224-
modifier: candidate.modifier ? { ...candidate.modifier } : null,
229+
important: candidate.important,
230+
raw: candidate.raw,
225231
}
226232

227233
case 'static':
228-
return { ...candidate, variants: candidate.variants.map(cloneVariant) }
234+
return {
235+
kind: candidate.kind,
236+
root: candidate.root,
237+
variants: candidate.variants.map(cloneVariant),
238+
important: candidate.important,
239+
raw: candidate.raw,
240+
}
229241

230242
case 'functional':
231243
return {
232-
...candidate,
244+
kind: candidate.kind,
245+
root: candidate.root,
246+
value: candidate.value
247+
? candidate.value.kind === 'arbitrary'
248+
? {
249+
kind: candidate.value.kind,
250+
dataType: candidate.value.dataType,
251+
value: candidate.value.value,
252+
}
253+
: {
254+
kind: candidate.value.kind,
255+
value: candidate.value.value,
256+
fraction: candidate.value.fraction,
257+
}
258+
: null,
259+
modifier: candidate.modifier
260+
? { kind: candidate.modifier.kind, value: candidate.modifier.value }
261+
: null,
233262
variants: candidate.variants.map(cloneVariant),
234-
value: candidate.value ? { ...candidate.value } : null,
235-
modifier: candidate.modifier ? { ...candidate.modifier } : null,
263+
important: candidate.important,
264+
raw: candidate.raw,
236265
}
237266

238267
default:
@@ -244,23 +273,29 @@ export function cloneCandidate(candidate: Candidate): Candidate {
244273
export function cloneVariant(variant: Variant): Variant {
245274
switch (variant.kind) {
246275
case 'arbitrary':
247-
return { ...variant }
276+
return { kind: variant.kind, selector: variant.selector, relative: variant.relative }
248277

249278
case 'static':
250-
return { ...variant }
279+
return { kind: variant.kind, root: variant.root }
251280

252281
case 'functional':
253282
return {
254-
...variant,
255-
value: variant.value ? { ...variant.value } : null,
256-
modifier: variant.modifier ? { ...variant.modifier } : null,
283+
kind: variant.kind,
284+
root: variant.root,
285+
value: variant.value ? { kind: variant.value.kind, value: variant.value.value } : null,
286+
modifier: variant.modifier
287+
? { kind: variant.modifier.kind, value: variant.modifier.value }
288+
: null,
257289
}
258290

259291
case 'compound':
260292
return {
261-
...variant,
293+
kind: variant.kind,
294+
root: variant.root,
262295
variant: cloneVariant(variant.variant),
263-
modifier: variant.modifier ? { ...variant.modifier } : null,
296+
modifier: variant.modifier
297+
? { kind: variant.modifier.kind, value: variant.modifier.value }
298+
: null,
264299
}
265300

266301
default:

0 commit comments

Comments
 (0)