tailwindcss-patch 9.0.0-alpha.1 → 9.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +20 -0
  2. package/dist/{chunk-Z6OMJZTU.js → chunk-TOAZIPHJ.js} +29 -11
  3. package/dist/{chunk-SWLOK2S6.mjs → chunk-VDWTCQ74.mjs} +29 -11
  4. package/dist/cli.js +4 -4
  5. package/dist/cli.mjs +1 -1
  6. package/dist/index.d.mts +43 -35
  7. package/dist/index.d.ts +43 -35
  8. package/dist/index.js +2 -2
  9. package/dist/index.mjs +1 -1
  10. package/package.json +8 -3
  11. package/src/api/tailwindcss-patcher.ts +424 -0
  12. package/src/babel/index.ts +12 -0
  13. package/src/cache/context.ts +212 -0
  14. package/src/cache/store.ts +1440 -0
  15. package/src/cache/types.ts +71 -0
  16. package/src/cli.ts +20 -0
  17. package/src/commands/basic-handlers.ts +145 -0
  18. package/src/commands/cli.ts +56 -0
  19. package/src/commands/command-context.ts +77 -0
  20. package/src/commands/command-definitions.ts +102 -0
  21. package/src/commands/command-metadata.ts +68 -0
  22. package/src/commands/command-registrar.ts +39 -0
  23. package/src/commands/command-runtime.ts +33 -0
  24. package/src/commands/default-handler-map.ts +25 -0
  25. package/src/commands/migrate-config.ts +104 -0
  26. package/src/commands/migrate-handler.ts +67 -0
  27. package/src/commands/migration-aggregation.ts +100 -0
  28. package/src/commands/migration-args.ts +85 -0
  29. package/src/commands/migration-file-executor.ts +189 -0
  30. package/src/commands/migration-output.ts +115 -0
  31. package/src/commands/migration-report-loader.ts +26 -0
  32. package/src/commands/migration-report.ts +21 -0
  33. package/src/commands/migration-source.ts +318 -0
  34. package/src/commands/migration-target-files.ts +161 -0
  35. package/src/commands/migration-target-resolver.ts +34 -0
  36. package/src/commands/migration-types.ts +65 -0
  37. package/src/commands/restore-handler.ts +24 -0
  38. package/src/commands/status-handler.ts +17 -0
  39. package/src/commands/status-output.ts +60 -0
  40. package/src/commands/token-output.ts +30 -0
  41. package/src/commands/types.ts +137 -0
  42. package/src/commands/validate-handler.ts +42 -0
  43. package/src/commands/validate.ts +83 -0
  44. package/src/config/index.ts +25 -0
  45. package/src/config/workspace.ts +87 -0
  46. package/src/constants.ts +4 -0
  47. package/src/extraction/candidate-extractor.ts +354 -0
  48. package/src/index.ts +57 -0
  49. package/src/install/class-collector.ts +1 -0
  50. package/src/install/context-registry.ts +1 -0
  51. package/src/install/index.ts +5 -0
  52. package/src/install/patch-runner.ts +1 -0
  53. package/src/install/process-tailwindcss.ts +1 -0
  54. package/src/install/status.ts +1 -0
  55. package/src/logger.ts +5 -0
  56. package/src/options/legacy.ts +93 -0
  57. package/src/options/normalize.ts +262 -0
  58. package/src/options/types.ts +217 -0
  59. package/src/patching/operations/export-context/index.ts +110 -0
  60. package/src/patching/operations/export-context/postcss-v2.ts +235 -0
  61. package/src/patching/operations/export-context/postcss-v3.ts +249 -0
  62. package/src/patching/operations/extend-length-units.ts +197 -0
  63. package/src/patching/patch-runner.ts +46 -0
  64. package/src/patching/status.ts +262 -0
  65. package/src/runtime/class-collector.ts +105 -0
  66. package/src/runtime/collector.ts +148 -0
  67. package/src/runtime/context-registry.ts +65 -0
  68. package/src/runtime/process-tailwindcss.ts +115 -0
  69. package/src/types.ts +159 -0
  70. package/src/utils.ts +52 -0
@@ -0,0 +1,354 @@
1
+ import type { SourceEntry } from '@tailwindcss/oxide'
2
+ import type {
3
+ TailwindTokenByFileMap,
4
+ TailwindTokenFileKey,
5
+ TailwindTokenLocation,
6
+ TailwindTokenReport,
7
+ } from '../types'
8
+ import { promises as fs } from 'node:fs'
9
+ import process from 'node:process'
10
+ import path from 'pathe'
11
+
12
+ let nodeImportPromise: ReturnType<typeof importNode> | undefined
13
+ let oxideImportPromise: ReturnType<typeof importOxide> | undefined
14
+ const designSystemPromiseCache = new Map<string, Promise<any>>()
15
+ const designSystemCandidateCache = new Map<string, Map<string, boolean>>()
16
+
17
+ async function importNode() {
18
+ return import('@tailwindcss/node')
19
+ }
20
+
21
+ async function importOxide() {
22
+ return import('@tailwindcss/oxide')
23
+ }
24
+
25
+ function getNodeModule() {
26
+ nodeImportPromise ??= importNode()
27
+ return nodeImportPromise
28
+ }
29
+
30
+ function getOxideModule() {
31
+ oxideImportPromise ??= importOxide()
32
+ return oxideImportPromise
33
+ }
34
+
35
+ function createDesignSystemCacheKey(css: string, bases: string[]) {
36
+ return JSON.stringify({
37
+ css,
38
+ bases: Array.from(new Set(bases.filter(Boolean))),
39
+ })
40
+ }
41
+
42
+ async function loadDesignSystem(css: string, bases: string[]) {
43
+ const uniqueBases = Array.from(new Set(bases.filter(Boolean)))
44
+ if (uniqueBases.length === 0) {
45
+ throw new Error('No base directories provided for Tailwind CSS design system.')
46
+ }
47
+
48
+ const cacheKey = createDesignSystemCacheKey(css, uniqueBases)
49
+ const cached = designSystemPromiseCache.get(cacheKey)
50
+ if (cached) {
51
+ return cached
52
+ }
53
+
54
+ const promise = (async () => {
55
+ const { __unstable__loadDesignSystem } = await getNodeModule()
56
+ let lastError: unknown
57
+
58
+ for (const base of uniqueBases) {
59
+ try {
60
+ return await __unstable__loadDesignSystem(css, { base })
61
+ }
62
+ catch (error) {
63
+ lastError = error
64
+ }
65
+ }
66
+
67
+ if (lastError instanceof Error) {
68
+ throw lastError
69
+ }
70
+ throw new Error('Failed to load Tailwind CSS design system.')
71
+ })()
72
+
73
+ designSystemPromiseCache.set(cacheKey, promise)
74
+ promise.catch(() => {
75
+ if (designSystemPromiseCache.get(cacheKey) === promise) {
76
+ designSystemPromiseCache.delete(cacheKey)
77
+ designSystemCandidateCache.delete(cacheKey)
78
+ }
79
+ })
80
+ return promise
81
+ }
82
+
83
+ export interface ExtractValidCandidatesOption {
84
+ sources?: SourceEntry[]
85
+ base?: string
86
+ baseFallbacks?: string[]
87
+ css?: string
88
+ cwd?: string
89
+ }
90
+
91
+ export async function extractRawCandidatesWithPositions(
92
+ content: string,
93
+ extension: string = 'html',
94
+ ): Promise<{ rawCandidate: string, start: number, end: number }[]> {
95
+ const { Scanner } = await getOxideModule()
96
+ const scanner = new Scanner({})
97
+ const result = scanner.getCandidatesWithPositions({ content, extension })
98
+
99
+ return result.map(({ candidate, position }) => ({
100
+ rawCandidate: candidate,
101
+ start: position,
102
+ end: position + candidate.length,
103
+ }))
104
+ }
105
+
106
+ export async function extractRawCandidates(
107
+ sources?: SourceEntry[],
108
+ ): Promise<string[]> {
109
+ const { Scanner } = await getOxideModule()
110
+ const scanner = new Scanner(sources === undefined ? {} : { sources })
111
+
112
+ return scanner.scan()
113
+ }
114
+
115
+ export async function extractValidCandidates(options?: ExtractValidCandidatesOption) {
116
+ const providedOptions = options ?? {}
117
+ const defaultCwd = providedOptions.cwd ?? process.cwd()
118
+
119
+ const base = providedOptions.base ?? defaultCwd
120
+ const baseFallbacks = providedOptions.baseFallbacks ?? []
121
+ const css = providedOptions.css ?? '@import "tailwindcss";'
122
+ const sources = (providedOptions.sources ?? [
123
+ {
124
+ base: defaultCwd,
125
+ pattern: '**/*',
126
+ negated: false,
127
+ },
128
+ ]).map(source => ({
129
+ base: source.base ?? defaultCwd,
130
+ pattern: source.pattern,
131
+ negated: source.negated,
132
+ }))
133
+
134
+ const designSystemKey = createDesignSystemCacheKey(css, [base, ...baseFallbacks])
135
+ const designSystem = await loadDesignSystem(css, [base, ...baseFallbacks])
136
+ const candidateCache = designSystemCandidateCache.get(designSystemKey) ?? new Map<string, boolean>()
137
+ designSystemCandidateCache.set(designSystemKey, candidateCache)
138
+
139
+ const candidates = await extractRawCandidates(sources)
140
+ const validCandidates: string[] = []
141
+ const uncachedCandidates: string[] = []
142
+
143
+ for (const rawCandidate of candidates) {
144
+ const cached = candidateCache.get(rawCandidate)
145
+ if (cached === true) {
146
+ validCandidates.push(rawCandidate)
147
+ continue
148
+ }
149
+
150
+ if (cached === false) {
151
+ continue
152
+ }
153
+
154
+ if (designSystem.parseCandidate(rawCandidate).length > 0) {
155
+ uncachedCandidates.push(rawCandidate)
156
+ continue
157
+ }
158
+
159
+ candidateCache.set(rawCandidate, false)
160
+ }
161
+
162
+ if (uncachedCandidates.length === 0) {
163
+ return validCandidates
164
+ }
165
+
166
+ const cssByCandidate = designSystem.candidatesToCss(uncachedCandidates)
167
+
168
+ for (let index = 0; index < uncachedCandidates.length; index++) {
169
+ const candidate = uncachedCandidates[index]
170
+ if (candidate === undefined) {
171
+ continue
172
+ }
173
+ const candidateCss = cssByCandidate[index]
174
+ const isValid = typeof candidateCss === 'string' && candidateCss.trim().length > 0
175
+ candidateCache.set(candidate, isValid)
176
+ if (!isValid) {
177
+ continue
178
+ }
179
+ validCandidates.push(candidate)
180
+ }
181
+
182
+ return validCandidates
183
+ }
184
+
185
+ function normalizeSources(sources: SourceEntry[] | undefined, cwd: string) {
186
+ const baseSources = sources?.length
187
+ ? sources
188
+ : [
189
+ {
190
+ base: cwd,
191
+ pattern: '**/*',
192
+ negated: false,
193
+ },
194
+ ]
195
+
196
+ return baseSources.map(source => ({
197
+ base: source.base ?? cwd,
198
+ pattern: source.pattern,
199
+ negated: source.negated,
200
+ }))
201
+ }
202
+
203
+ function buildLineOffsets(content: string) {
204
+ const offsets: number[] = [0]
205
+ for (let i = 0; i < content.length; i++) {
206
+ if (content[i] === '\n') {
207
+ offsets.push(i + 1)
208
+ }
209
+ }
210
+ // Push a sentinel to simplify bounds checks during binary search.
211
+ if (offsets[offsets.length - 1] !== content.length) {
212
+ offsets.push(content.length)
213
+ }
214
+ return offsets
215
+ }
216
+
217
+ function resolveLineMeta(content: string, offsets: number[], index: number) {
218
+ let low = 0
219
+ let high = offsets.length - 1
220
+ while (low <= high) {
221
+ const mid = Math.floor((low + high) / 2)
222
+ const start = offsets[mid]
223
+ if (start === undefined) {
224
+ break
225
+ }
226
+ const nextStart = offsets[mid + 1] ?? content.length
227
+
228
+ if (index < start) {
229
+ high = mid - 1
230
+ continue
231
+ }
232
+
233
+ if (index >= nextStart) {
234
+ low = mid + 1
235
+ continue
236
+ }
237
+
238
+ const line = mid + 1
239
+ const column = index - start + 1
240
+ const lineEnd = content.indexOf('\n', start)
241
+ const lineText = content.slice(start, lineEnd === -1 ? content.length : lineEnd)
242
+
243
+ return { line, column, lineText }
244
+ }
245
+
246
+ const lastStart = offsets[offsets.length - 2] ?? 0
247
+ return {
248
+ line: offsets.length - 1,
249
+ column: index - lastStart + 1,
250
+ lineText: content.slice(lastStart),
251
+ }
252
+ }
253
+
254
+ function toExtension(filename: string) {
255
+ const ext = path.extname(filename).replace(/^\./, '')
256
+ return ext || 'txt'
257
+ }
258
+
259
+ function toRelativeFile(cwd: string, filename: string) {
260
+ const relative = path.relative(cwd, filename)
261
+ return relative === '' ? path.basename(filename) : relative
262
+ }
263
+
264
+ export interface ExtractProjectCandidatesOptions {
265
+ cwd?: string
266
+ sources?: SourceEntry[]
267
+ }
268
+
269
+ export async function extractProjectCandidatesWithPositions(
270
+ options?: ExtractProjectCandidatesOptions,
271
+ ): Promise<TailwindTokenReport> {
272
+ const cwd = options?.cwd ? path.resolve(options.cwd) : process.cwd()
273
+ const normalizedSources = normalizeSources(options?.sources, cwd)
274
+ const { Scanner } = await getOxideModule()
275
+ const scanner = new Scanner({
276
+ sources: normalizedSources,
277
+ })
278
+
279
+ const files = scanner.files ?? []
280
+ const entries: TailwindTokenLocation[] = []
281
+ const skipped: TailwindTokenReport['skippedFiles'] = []
282
+
283
+ for (const file of files) {
284
+ let content: string
285
+ try {
286
+ content = await fs.readFile(file, 'utf8')
287
+ }
288
+ catch (error) {
289
+ skipped.push({
290
+ file,
291
+ reason: error instanceof Error ? error.message : 'Unknown error',
292
+ })
293
+ continue
294
+ }
295
+
296
+ const extension = toExtension(file)
297
+ const matches = scanner.getCandidatesWithPositions({
298
+ file,
299
+ content,
300
+ extension,
301
+ })
302
+
303
+ if (!matches.length) {
304
+ continue
305
+ }
306
+
307
+ const offsets = buildLineOffsets(content)
308
+ const relativeFile = toRelativeFile(cwd, file)
309
+
310
+ for (const match of matches) {
311
+ const info = resolveLineMeta(content, offsets, match.position)
312
+ entries.push({
313
+ rawCandidate: match.candidate,
314
+ file,
315
+ relativeFile,
316
+ extension,
317
+ start: match.position,
318
+ end: match.position + match.candidate.length,
319
+ length: match.candidate.length,
320
+ line: info.line,
321
+ column: info.column,
322
+ lineText: info.lineText,
323
+ })
324
+ }
325
+ }
326
+
327
+ return {
328
+ entries,
329
+ filesScanned: files.length,
330
+ skippedFiles: skipped,
331
+ sources: normalizedSources,
332
+ }
333
+ }
334
+
335
+ export function groupTokensByFile(
336
+ report: TailwindTokenReport,
337
+ options?: { key?: TailwindTokenFileKey, stripAbsolutePaths?: boolean },
338
+ ): TailwindTokenByFileMap {
339
+ const key = options?.key ?? 'relative'
340
+ const stripAbsolute = options?.stripAbsolutePaths ?? key !== 'absolute'
341
+
342
+ return report.entries.reduce<TailwindTokenByFileMap>((acc, entry) => {
343
+ const bucketKey = key === 'absolute' ? entry.file : entry.relativeFile
344
+ const bucket = acc[bucketKey] ?? (acc[bucketKey] = [])
345
+ const value = stripAbsolute
346
+ ? {
347
+ ...entry,
348
+ file: entry.relativeFile,
349
+ }
350
+ : entry
351
+ bucket.push(value)
352
+ return acc
353
+ }, {})
354
+ }
package/src/index.ts ADDED
@@ -0,0 +1,57 @@
1
+ import type { TailwindcssMangleConfig } from '@tailwindcss-mangle/config'
2
+
3
+ export { TailwindcssPatcher } from './api/tailwindcss-patcher'
4
+ export { CacheStore } from './cache/store'
5
+ export {
6
+ createTailwindcssPatchCli,
7
+ mountTailwindcssPatchCommands,
8
+ type TailwindcssPatchCliMountOptions,
9
+ type TailwindcssPatchCliOptions,
10
+ type TailwindcssPatchCommand,
11
+ type TailwindcssPatchCommandContext,
12
+ type TailwindcssPatchCommandHandler,
13
+ type TailwindcssPatchCommandHandlerMap,
14
+ type TailwindcssPatchCommandOptionDefinition,
15
+ type TailwindcssPatchCommandOptions,
16
+ tailwindcssPatchCommands,
17
+ VALIDATE_EXIT_CODES,
18
+ VALIDATE_FAILURE_REASONS,
19
+ ValidateCommandError,
20
+ type ValidateFailureReason,
21
+ type ValidateFailureSummary,
22
+ type ValidateJsonFailurePayload,
23
+ type ValidateJsonSuccessPayload,
24
+ } from './commands/cli'
25
+ export {
26
+ type ConfigFileMigrationEntry,
27
+ type ConfigFileMigrationReport,
28
+ migrateConfigFiles,
29
+ type MigrateConfigFilesOptions,
30
+ MIGRATION_REPORT_KIND,
31
+ MIGRATION_REPORT_SCHEMA_VERSION,
32
+ restoreConfigFiles,
33
+ type RestoreConfigFilesOptions,
34
+ type RestoreConfigFilesResult,
35
+ } from './commands/migrate-config'
36
+ export { normalizeOptions } from './config'
37
+ export type { TailwindCssPatchOptions } from './config'
38
+ export {
39
+ extractProjectCandidatesWithPositions,
40
+ extractRawCandidates,
41
+ extractRawCandidatesWithPositions,
42
+ extractValidCandidates,
43
+ groupTokensByFile,
44
+ } from './extraction/candidate-extractor'
45
+ export {
46
+ collectClassesFromContexts,
47
+ collectClassesFromTailwindV4,
48
+ getPatchStatusReport,
49
+ loadRuntimeContexts,
50
+ runTailwindBuild,
51
+ } from './install'
52
+ export { default as logger } from './logger'
53
+ export * from './types'
54
+
55
+ export function defineConfig<T extends TailwindcssMangleConfig>(config: T): T {
56
+ return config
57
+ }
@@ -0,0 +1 @@
1
+ export { collectClassesFromContexts, collectClassesFromTailwindV4 } from '../runtime/class-collector'
@@ -0,0 +1 @@
1
+ export { loadRuntimeContexts } from '../runtime/context-registry'
@@ -0,0 +1,5 @@
1
+ export { collectClassesFromContexts, collectClassesFromTailwindV4 } from './class-collector'
2
+ export { loadRuntimeContexts } from './context-registry'
3
+ export { applyTailwindPatches } from './patch-runner'
4
+ export { runTailwindBuild } from './process-tailwindcss'
5
+ export { getPatchStatusReport } from './status'
@@ -0,0 +1 @@
1
+ export { applyTailwindPatches } from '../patching/patch-runner'
@@ -0,0 +1 @@
1
+ export { runTailwindBuild } from '../runtime/process-tailwindcss'
@@ -0,0 +1 @@
1
+ export { getPatchStatusReport } from '../patching/status'
package/src/logger.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { createConsola } from 'consola'
2
+
3
+ const logger = createConsola()
4
+
5
+ export default logger
@@ -0,0 +1,93 @@
1
+ import type { RegistryOptions } from '@tailwindcss-mangle/config'
2
+ import type { TailwindCssPatchOptions } from './types'
3
+
4
+ const deprecatedRegistryMapping = {
5
+ output: 'extract',
6
+ tailwind: 'tailwindcss',
7
+ } as const
8
+
9
+ type DeprecatedRegistryKey = keyof typeof deprecatedRegistryMapping
10
+
11
+ const deprecatedTailwindMapping = {
12
+ package: 'packageName',
13
+ legacy: 'v2',
14
+ classic: 'v3',
15
+ next: 'v4',
16
+ } as const
17
+
18
+ type DeprecatedTailwindKey = keyof typeof deprecatedTailwindMapping
19
+
20
+ function assertNoDeprecatedRegistryOptions(registry: RegistryOptions) {
21
+ const usedRegistryKeys = (Object.keys(deprecatedRegistryMapping) as DeprecatedRegistryKey[])
22
+ .filter(key => Object.prototype.hasOwnProperty.call(registry, key))
23
+
24
+ if (usedRegistryKeys.length > 0) {
25
+ const mapping = usedRegistryKeys.map(key => `${key} -> ${deprecatedRegistryMapping[key]}`).join(', ')
26
+ throw new Error(
27
+ `Legacy registry fields are no longer supported: ${usedRegistryKeys.join(', ')}. Use the modern fields instead: ${mapping}.`,
28
+ )
29
+ }
30
+
31
+ const tailwind = registry.tailwindcss
32
+ if (!tailwind) {
33
+ return
34
+ }
35
+
36
+ const usedTailwindKeys = (Object.keys(deprecatedTailwindMapping) as DeprecatedTailwindKey[])
37
+ .filter(key => Object.prototype.hasOwnProperty.call(tailwind, key))
38
+
39
+ if (usedTailwindKeys.length > 0) {
40
+ const mapping = usedTailwindKeys.map(key => `${key} -> tailwindcss.${deprecatedTailwindMapping[key]}`).join(', ')
41
+ throw new Error(
42
+ `Legacy "registry.tailwindcss" fields are no longer supported: ${usedTailwindKeys.join(', ')}. Use the modern fields instead: ${mapping}.`,
43
+ )
44
+ }
45
+ }
46
+
47
+ export function fromUnifiedConfig(registry?: RegistryOptions): TailwindCssPatchOptions {
48
+ if (!registry) {
49
+ return {}
50
+ }
51
+
52
+ assertNoDeprecatedRegistryOptions(registry)
53
+
54
+ const extract = registry.extract
55
+ ? {
56
+ ...(registry.extract.write === undefined ? {} : { write: registry.extract.write }),
57
+ ...(registry.extract.file === undefined ? {} : { file: registry.extract.file }),
58
+ ...(registry.extract.format === undefined ? {} : { format: registry.extract.format }),
59
+ ...(registry.extract.pretty === undefined ? {} : { pretty: registry.extract.pretty }),
60
+ ...(registry.extract.removeUniversalSelector === undefined ? {} : { removeUniversalSelector: registry.extract.removeUniversalSelector }),
61
+ }
62
+ : undefined
63
+
64
+ const tailwindcss = registry.tailwindcss
65
+ ? {
66
+ ...(registry.tailwindcss.version === undefined ? {} : { version: registry.tailwindcss.version }),
67
+ ...(registry.tailwindcss.packageName === undefined ? {} : { packageName: registry.tailwindcss.packageName }),
68
+ ...(registry.tailwindcss.resolve === undefined ? {} : { resolve: registry.tailwindcss.resolve }),
69
+ ...(registry.tailwindcss.config === undefined ? {} : { config: registry.tailwindcss.config }),
70
+ ...(registry.tailwindcss.cwd === undefined ? {} : { cwd: registry.tailwindcss.cwd }),
71
+ ...(registry.tailwindcss.v2 === undefined ? {} : { v2: registry.tailwindcss.v2 }),
72
+ ...(registry.tailwindcss.v3 === undefined ? {} : { v3: registry.tailwindcss.v3 }),
73
+ ...(registry.tailwindcss.v4 === undefined ? {} : { v4: registry.tailwindcss.v4 }),
74
+ }
75
+ : undefined
76
+
77
+ const apply = registry.apply
78
+ ? {
79
+ ...(registry.apply.overwrite === undefined ? {} : { overwrite: registry.apply.overwrite }),
80
+ ...(registry.apply.exposeContext === undefined ? {} : { exposeContext: registry.apply.exposeContext }),
81
+ ...(registry.apply.extendLengthUnits === undefined ? {} : { extendLengthUnits: registry.apply.extendLengthUnits }),
82
+ }
83
+ : undefined
84
+
85
+ return {
86
+ ...(registry.projectRoot === undefined ? {} : { projectRoot: registry.projectRoot }),
87
+ ...(apply === undefined ? {} : { apply }),
88
+ ...(registry.cache === undefined ? {} : { cache: registry.cache }),
89
+ ...(registry.filter === undefined ? {} : { filter: registry.filter }),
90
+ ...(extract === undefined ? {} : { extract }),
91
+ ...(tailwindcss === undefined ? {} : { tailwindcss }),
92
+ }
93
+ }