@@ -18,10 +18,14 @@ type ExtractRawCandidateResult = Awaited<ReturnType<typeof extractRawCandidatesW
1818type ExtractRawCandidatesFn = ( content : string , extension ?: string ) => Promise < ExtractRawCandidateResult >
1919
2020export interface BundleRuntimeClassSetManager {
21- sync : ( patcher : TailwindcssPatcherLike , snapshot : BundleSnapshot ) => Promise < Set < string > >
21+ sync : ( patcher : TailwindcssPatcherLike , snapshot : BundleSnapshot , options ?: BundleRuntimeClassSetSyncOptions ) => Promise < Set < string > >
2222 reset : ( ) => Promise < void >
2323}
2424
25+ export interface BundleRuntimeClassSetSyncOptions {
26+ baseClassSet ?: Set < string > | undefined
27+ }
28+
2529interface CreateBundleRuntimeClassSetManagerOptions {
2630 extractCandidates ?: ExtractValidCandidatesFn
2731 extractRawCandidates ?: ExtractRawCandidatesFn
@@ -71,7 +75,6 @@ function createCandidateValidationSource(candidates: Iterable<string>) {
7175
7276function removeCandidateSet (
7377 candidateCountByClass : Map < string , number > ,
74- runtimeSet : Set < string > ,
7578 candidates : Set < string > ,
7679) {
7780 for ( const className of candidates ) {
@@ -81,7 +84,6 @@ function removeCandidateSet(
8184 }
8285 if ( count <= 1 ) {
8386 candidateCountByClass . delete ( className )
84- runtimeSet . delete ( className )
8587 continue
8688 }
8789 candidateCountByClass . set ( className , count - 1 )
@@ -90,23 +92,42 @@ function removeCandidateSet(
9092
9193function addCandidateSet (
9294 candidateCountByClass : Map < string , number > ,
93- runtimeSet : Set < string > ,
9495 candidates : Set < string > ,
9596) {
9697 for ( const className of candidates ) {
9798 const nextCount = ( candidateCountByClass . get ( className ) ?? 0 ) + 1
9899 candidateCountByClass . set ( className , nextCount )
99- runtimeSet . add ( className )
100100 }
101101}
102102
103+ function createRuntimeClassSet (
104+ baseClassSet : Set < string > ,
105+ candidateCountByClass : Map < string , number > ,
106+ ) {
107+ return new Set ( [
108+ ...baseClassSet ,
109+ ...candidateCountByClass . keys ( ) ,
110+ ] )
111+ }
112+
113+ function createNonSourceBaseClassSet (
114+ baseClassSet : Set < string > ,
115+ candidateCountByClass : Map < string , number > ,
116+ ) {
117+ const nextBaseClassSet = new Set ( baseClassSet )
118+ for ( const candidate of candidateCountByClass . keys ( ) ) {
119+ nextBaseClassSet . delete ( candidate )
120+ }
121+ return nextBaseClassSet
122+ }
123+
103124export function createBundleRuntimeClassSetManager (
104125 options : CreateBundleRuntimeClassSetManagerOptions = { } ,
105126) : BundleRuntimeClassSetManager {
106127 const customExtractCandidates = options . extractCandidates
107128 const extractCandidates = customExtractCandidates ?? extractValidCandidates
108129 const extractRawCandidates = options . extractRawCandidates ?? extractRawCandidatesWithPositions
109- const runtimeSet = new Set < string > ( )
130+ let baseClassSet = new Set < string > ( )
110131 const candidateCountByClass = new Map < string , number > ( )
111132 const candidatesByFile = new Map < string , Set < string > > ( )
112133 const candidateValidityCache = new Map < string , boolean > ( )
@@ -115,7 +136,7 @@ export function createBundleRuntimeClassSetManager(
115136 let designSystemPromise : Promise < TailwindV4DesignSystem > | undefined
116137
117138 async function reset ( ) {
118- runtimeSet . clear ( )
139+ baseClassSet = new Set < string > ( )
119140 candidateCountByClass . clear ( )
120141 candidatesByFile . clear ( )
121142 candidateValidityCache . clear ( )
@@ -169,6 +190,13 @@ export function createBundleRuntimeClassSetManager(
169190 return
170191 }
171192
193+ if ( patcher . majorVersion === 3 && ! customExtractCandidates ) {
194+ for ( const candidate of unknownCandidates ) {
195+ candidateValidityCache . set ( candidate , true )
196+ }
197+ return
198+ }
199+
172200 const context = await resolveValidationContextCached ( patcher )
173201 if ( ! customExtractCandidates ) {
174202 try {
@@ -206,6 +234,7 @@ export function createBundleRuntimeClassSetManager(
206234 async function sync (
207235 patcher : TailwindcssPatcherLike ,
208236 snapshot : BundleSnapshot ,
237+ options : BundleRuntimeClassSetSyncOptions = { } ,
209238 ) {
210239 const nextSignature = getRuntimeClassSetSignature ( patcher ) ?? 'runtime:missing'
211240 const runtimeEntries = createRuntimeEntries ( snapshot )
@@ -219,12 +248,13 @@ export function createBundleRuntimeClassSetManager(
219248 }
220249
221250 runtimeSignature = nextSignature
251+ const nextBaseClassSet = options . baseClassSet
222252
223253 for ( const [ file , previousCandidates ] of candidatesByFile ) {
224254 if ( currentRuntimeFiles . has ( file ) ) {
225255 continue
226256 }
227- removeCandidateSet ( candidateCountByClass , runtimeSet , previousCandidates )
257+ removeCandidateSet ( candidateCountByClass , previousCandidates )
228258 candidatesByFile . delete ( file )
229259 }
230260
@@ -233,7 +263,10 @@ export function createBundleRuntimeClassSetManager(
233263 : [ ...collectChangedRuntimeFiles ( snapshot ) ]
234264
235265 if ( changedRuntimeFiles . length === 0 ) {
236- return new Set ( runtimeSet )
266+ if ( nextBaseClassSet ) {
267+ baseClassSet = createNonSourceBaseClassSet ( nextBaseClassSet , candidateCountByClass )
268+ }
269+ return createRuntimeClassSet ( baseClassSet , candidateCountByClass )
237270 }
238271
239272 const rawCandidatesByFile = new Map < string , Set < string > > ( )
@@ -261,7 +294,7 @@ export function createBundleRuntimeClassSetManager(
261294 const nextRawCandidates = rawCandidatesByFile . get ( file )
262295 const previousCandidates = candidatesByFile . get ( file )
263296 if ( previousCandidates ) {
264- removeCandidateSet ( candidateCountByClass , runtimeSet , previousCandidates )
297+ removeCandidateSet ( candidateCountByClass , previousCandidates )
265298 }
266299
267300 if ( ! nextRawCandidates || nextRawCandidates . size === 0 ) {
@@ -279,10 +312,15 @@ export function createBundleRuntimeClassSetManager(
279312 continue
280313 }
281314
282- addCandidateSet ( candidateCountByClass , runtimeSet , nextCandidates )
315+ addCandidateSet ( candidateCountByClass , nextCandidates )
283316 candidatesByFile . set ( file , nextCandidates )
284317 }
285318
319+ if ( nextBaseClassSet ) {
320+ baseClassSet = createNonSourceBaseClassSet ( nextBaseClassSet , candidateCountByClass )
321+ }
322+ const runtimeSet = createRuntimeClassSet ( baseClassSet , candidateCountByClass )
323+
286324 debug (
287325 'incremental runtime set synced, changedFiles=%d rawCandidates=%d validateMisses=%d runtimeSize=%d trackedFiles=%d' ,
288326 changedRuntimeFiles . length ,
0 commit comments