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,46 @@
1
+ import type { PackageInfo } from 'local-pkg'
2
+ import type { NormalizedTailwindCssPatchOptions } from '../options/types'
3
+ import { applyExposeContextPatch } from './operations/export-context'
4
+ import { applyExtendLengthUnitsPatchV3, applyExtendLengthUnitsPatchV4 } from './operations/extend-length-units'
5
+
6
+ export interface PatchRunnerContext {
7
+ packageInfo: PackageInfo
8
+ options: NormalizedTailwindCssPatchOptions
9
+ majorVersion: 2 | 3 | 4
10
+ }
11
+
12
+ export interface PatchRunnerResult {
13
+ exposeContext?: ReturnType<typeof applyExposeContextPatch>
14
+ extendLengthUnits?: ReturnType<typeof applyExtendLengthUnitsPatchV3> | ReturnType<typeof applyExtendLengthUnitsPatchV4>
15
+ }
16
+
17
+ export function applyTailwindPatches(context: PatchRunnerContext): PatchRunnerResult {
18
+ const { packageInfo, options, majorVersion } = context
19
+ const results: PatchRunnerResult = {}
20
+
21
+ if (options.features.exposeContext.enabled && (majorVersion === 2 || majorVersion === 3)) {
22
+ results.exposeContext = applyExposeContextPatch({
23
+ rootDir: packageInfo.rootPath,
24
+ refProperty: options.features.exposeContext.refProperty,
25
+ overwrite: options.overwrite,
26
+ majorVersion,
27
+ })
28
+ }
29
+
30
+ if (options.features.extendLengthUnits?.enabled) {
31
+ if (majorVersion === 3) {
32
+ results.extendLengthUnits = applyExtendLengthUnitsPatchV3(
33
+ packageInfo.rootPath,
34
+ options.features.extendLengthUnits,
35
+ )
36
+ }
37
+ else if (majorVersion === 4) {
38
+ results.extendLengthUnits = applyExtendLengthUnitsPatchV4(
39
+ packageInfo.rootPath,
40
+ options.features.extendLengthUnits,
41
+ )
42
+ }
43
+ }
44
+
45
+ return results
46
+ }
@@ -0,0 +1,262 @@
1
+ import type { PackageInfo } from 'local-pkg'
2
+ import type {
3
+ NormalizedExtendLengthUnitsOptions,
4
+ NormalizedTailwindCssPatchOptions,
5
+ } from '../options/types'
6
+ import type { PatchStatusEntry, PatchStatusReport } from '../types'
7
+ import * as t from '@babel/types'
8
+ import fs from 'fs-extra'
9
+ import path from 'pathe'
10
+
11
+ import { parse, traverse } from '../babel'
12
+ import { transformPostcssPluginV2, transformProcessTailwindFeaturesReturnContextV2 } from './operations/export-context/postcss-v2'
13
+ import { transformPostcssPlugin, transformProcessTailwindFeaturesReturnContext } from './operations/export-context/postcss-v3'
14
+ import { applyExtendLengthUnitsPatchV4 } from './operations/extend-length-units'
15
+
16
+ interface PatchStatusContext {
17
+ packageInfo: PackageInfo
18
+ options: NormalizedTailwindCssPatchOptions
19
+ majorVersion: 2 | 3 | 4
20
+ }
21
+
22
+ function inspectLengthUnitsArray(
23
+ content: string,
24
+ variableName: string,
25
+ units: string[],
26
+ ) {
27
+ const ast = parse(content)
28
+ let found = false
29
+ let missingUnits: string[] = []
30
+
31
+ traverse(ast, {
32
+ Identifier(path) {
33
+ if (
34
+ path.node.name === variableName
35
+ && t.isVariableDeclarator(path.parent)
36
+ && t.isArrayExpression(path.parent.init)
37
+ ) {
38
+ found = true
39
+ const existing = new Set(
40
+ path.parent.init.elements
41
+ .map(element => (t.isStringLiteral(element) ? element.value : undefined))
42
+ .filter(Boolean) as string[],
43
+ )
44
+ missingUnits = units.filter(unit => !existing.has(unit))
45
+ path.stop()
46
+ }
47
+ },
48
+ })
49
+
50
+ return {
51
+ found,
52
+ missingUnits,
53
+ }
54
+ }
55
+
56
+ function checkExposeContextPatch(context: PatchStatusContext): PatchStatusEntry {
57
+ const { packageInfo, options, majorVersion } = context
58
+ const refProperty = options.features.exposeContext.refProperty
59
+
60
+ if (!options.features.exposeContext.enabled) {
61
+ return {
62
+ name: 'exposeContext',
63
+ status: 'skipped',
64
+ reason: 'exposeContext feature disabled',
65
+ files: [],
66
+ }
67
+ }
68
+
69
+ if (majorVersion === 4) {
70
+ return {
71
+ name: 'exposeContext',
72
+ status: 'unsupported',
73
+ reason: 'Context export patch is only required for Tailwind v2/v3',
74
+ files: [],
75
+ }
76
+ }
77
+
78
+ const checks: Array<{ relative: string, exists: boolean, patched: boolean }> = []
79
+
80
+ function inspectFile(
81
+ relative: string,
82
+ transform: (content: string) => { hasPatched: boolean },
83
+ ) {
84
+ const filePath = path.resolve(packageInfo.rootPath, relative)
85
+ if (!fs.existsSync(filePath)) {
86
+ checks.push({ relative, exists: false, patched: false })
87
+ return
88
+ }
89
+ const content = fs.readFileSync(filePath, 'utf8')
90
+ const { hasPatched } = transform(content)
91
+ checks.push({
92
+ relative,
93
+ exists: true,
94
+ patched: hasPatched,
95
+ })
96
+ }
97
+
98
+ if (majorVersion === 3) {
99
+ inspectFile('lib/processTailwindFeatures.js', transformProcessTailwindFeaturesReturnContext)
100
+ const pluginCandidates = ['lib/plugin.js', 'lib/index.js']
101
+ const pluginRelative = pluginCandidates.find(candidate => fs.existsSync(path.resolve(packageInfo.rootPath, candidate)))
102
+ if (pluginRelative) {
103
+ inspectFile(pluginRelative, content => transformPostcssPlugin(content, { refProperty }))
104
+ }
105
+ else {
106
+ checks.push({ relative: 'lib/plugin.js', exists: false, patched: false })
107
+ }
108
+ }
109
+ else {
110
+ inspectFile('lib/jit/processTailwindFeatures.js', transformProcessTailwindFeaturesReturnContextV2)
111
+ inspectFile('lib/jit/index.js', content => transformPostcssPluginV2(content, { refProperty }))
112
+ }
113
+
114
+ const files = checks.filter(check => check.exists).map(check => check.relative)
115
+ const missingFiles = checks.filter(check => !check.exists)
116
+ const unpatchedFiles = checks.filter(check => check.exists && !check.patched)
117
+
118
+ const reasons: string[] = []
119
+ if (missingFiles.length) {
120
+ reasons.push(`missing files: ${missingFiles.map(item => item.relative).join(', ')}`)
121
+ }
122
+ if (unpatchedFiles.length) {
123
+ reasons.push(`unpatched files: ${unpatchedFiles.map(item => item.relative).join(', ')}`)
124
+ }
125
+
126
+ return {
127
+ name: 'exposeContext',
128
+ status: reasons.length ? 'not-applied' : 'applied',
129
+ ...(reasons.length ? { reason: reasons.join('; ') } : {}),
130
+ files,
131
+ }
132
+ }
133
+
134
+ function checkExtendLengthUnitsV3(rootDir: string, options: NormalizedExtendLengthUnitsOptions): PatchStatusEntry {
135
+ const lengthUnitsFilePath = options.lengthUnitsFilePath ?? 'lib/util/dataTypes.js'
136
+ const variableName = options.variableName ?? 'lengthUnits'
137
+ const target = path.resolve(rootDir, lengthUnitsFilePath)
138
+ const files = fs.existsSync(target) ? [path.relative(rootDir, target)] : []
139
+
140
+ if (!fs.existsSync(target)) {
141
+ return {
142
+ name: 'extendLengthUnits',
143
+ status: 'not-applied',
144
+ reason: `missing ${lengthUnitsFilePath}`,
145
+ files,
146
+ }
147
+ }
148
+
149
+ const content = fs.readFileSync(target, 'utf8')
150
+ const { found, missingUnits } = inspectLengthUnitsArray(content, variableName, options.units)
151
+
152
+ if (!found) {
153
+ return {
154
+ name: 'extendLengthUnits',
155
+ status: 'not-applied',
156
+ reason: `could not locate ${variableName} array in ${lengthUnitsFilePath}`,
157
+ files,
158
+ }
159
+ }
160
+
161
+ if (missingUnits.length) {
162
+ return {
163
+ name: 'extendLengthUnits',
164
+ status: 'not-applied',
165
+ reason: `missing units: ${missingUnits.join(', ')}`,
166
+ files,
167
+ }
168
+ }
169
+
170
+ return {
171
+ name: 'extendLengthUnits',
172
+ status: 'applied',
173
+ files,
174
+ }
175
+ }
176
+
177
+ function checkExtendLengthUnitsV4(rootDir: string, options: NormalizedExtendLengthUnitsOptions): PatchStatusEntry {
178
+ const distDir = path.resolve(rootDir, 'dist')
179
+ if (!fs.existsSync(distDir)) {
180
+ return {
181
+ name: 'extendLengthUnits',
182
+ status: 'not-applied',
183
+ reason: 'dist directory not found for Tailwind v4 package',
184
+ files: [],
185
+ }
186
+ }
187
+
188
+ const result = applyExtendLengthUnitsPatchV4(rootDir, {
189
+ ...options,
190
+ enabled: true,
191
+ overwrite: false,
192
+ })
193
+
194
+ if (result.files.length === 0) {
195
+ return {
196
+ name: 'extendLengthUnits',
197
+ status: 'not-applied',
198
+ reason: 'no bundle chunks matched the length unit pattern',
199
+ files: [],
200
+ }
201
+ }
202
+
203
+ const files = result.files.map(file => path.relative(rootDir, file.file))
204
+ const pending = result.files.filter(file => !file.hasPatched)
205
+ if (pending.length) {
206
+ return {
207
+ name: 'extendLengthUnits',
208
+ status: 'not-applied',
209
+ reason: `missing units in ${pending.length} bundle${pending.length > 1 ? 's' : ''}`,
210
+ files: pending.map(file => path.relative(rootDir, file.file)),
211
+ }
212
+ }
213
+
214
+ return {
215
+ name: 'extendLengthUnits',
216
+ status: 'applied',
217
+ files,
218
+ }
219
+ }
220
+
221
+ function checkExtendLengthUnitsPatch(context: PatchStatusContext): PatchStatusEntry {
222
+ const { packageInfo, options, majorVersion } = context
223
+
224
+ if (!options.features.extendLengthUnits) {
225
+ return {
226
+ name: 'extendLengthUnits',
227
+ status: 'skipped',
228
+ reason: 'extendLengthUnits feature disabled',
229
+ files: [],
230
+ }
231
+ }
232
+
233
+ if (majorVersion === 2) {
234
+ return {
235
+ name: 'extendLengthUnits',
236
+ status: 'unsupported',
237
+ reason: 'length unit extension is only applied for Tailwind v3/v4',
238
+ files: [],
239
+ }
240
+ }
241
+
242
+ if (majorVersion === 3) {
243
+ return checkExtendLengthUnitsV3(packageInfo.rootPath, options.features.extendLengthUnits)
244
+ }
245
+
246
+ return checkExtendLengthUnitsV4(packageInfo.rootPath, options.features.extendLengthUnits)
247
+ }
248
+
249
+ export function getPatchStatusReport(context: PatchStatusContext): PatchStatusReport {
250
+ return {
251
+ package: {
252
+ name: context.packageInfo.name ?? context.packageInfo.packageJson?.name,
253
+ version: context.packageInfo.version,
254
+ root: context.packageInfo.rootPath,
255
+ },
256
+ majorVersion: context.majorVersion,
257
+ entries: [
258
+ checkExposeContextPatch(context),
259
+ checkExtendLengthUnitsPatch(context),
260
+ ],
261
+ }
262
+ }
@@ -0,0 +1,105 @@
1
+ import type { NormalizedTailwindCssPatchOptions } from '../options/types'
2
+ import type { TailwindcssRuntimeContext } from '../types'
3
+ import process from 'node:process'
4
+ import fs from 'fs-extra'
5
+ import path from 'pathe'
6
+ import { extractValidCandidates } from '../extraction/candidate-extractor'
7
+ import { isObject } from '../utils'
8
+
9
+ export function collectClassesFromContexts(
10
+ contexts: TailwindcssRuntimeContext[],
11
+ filter: (className: string) => boolean,
12
+ ) {
13
+ const set = new Set<string>()
14
+ for (const context of contexts) {
15
+ if (!isObject(context) || !context.classCache) {
16
+ continue
17
+ }
18
+
19
+ for (const key of context.classCache.keys()) {
20
+ const className = key.toString()
21
+ if (filter(className)) {
22
+ set.add(className)
23
+ }
24
+ }
25
+ }
26
+ return set
27
+ }
28
+
29
+ export async function collectClassesFromTailwindV4(
30
+ options: NormalizedTailwindCssPatchOptions,
31
+ ) {
32
+ const set = new Set<string>()
33
+ const v4Options = options.tailwind.v4
34
+ if (!v4Options) {
35
+ return set
36
+ }
37
+
38
+ const toAbsolute = (value: string | undefined) => {
39
+ if (!value) {
40
+ return undefined
41
+ }
42
+ return path.isAbsolute(value) ? value : path.resolve(options.projectRoot, value)
43
+ }
44
+ const resolvedConfiguredBase = toAbsolute(v4Options.configuredBase)
45
+ const resolvedDefaultBase = toAbsolute(v4Options.base) ?? process.cwd()
46
+ const resolveSources = (base: string) => {
47
+ if (!v4Options.sources?.length) {
48
+ return undefined
49
+ }
50
+
51
+ return v4Options.sources.map(source => ({
52
+ base: source.base ?? base,
53
+ pattern: source.pattern,
54
+ negated: source.negated,
55
+ }))
56
+ }
57
+
58
+ if (v4Options.cssEntries.length > 0) {
59
+ for (const entry of v4Options.cssEntries) {
60
+ const filePath = path.isAbsolute(entry) ? entry : path.resolve(options.projectRoot, entry)
61
+ if (!(await fs.pathExists(filePath))) {
62
+ continue
63
+ }
64
+ const css = await fs.readFile(filePath, 'utf8')
65
+ const entryDir = path.dirname(filePath)
66
+ const designSystemBases = resolvedConfiguredBase && resolvedConfiguredBase !== entryDir
67
+ ? [entryDir, resolvedConfiguredBase]
68
+ : [entryDir]
69
+ const sourcesBase = resolvedConfiguredBase ?? entryDir
70
+ const sources = resolveSources(sourcesBase)
71
+ const firstBase = designSystemBases[0] ?? entryDir
72
+ const extractOptions = {
73
+ cwd: options.projectRoot,
74
+ base: firstBase,
75
+ baseFallbacks: designSystemBases.slice(1),
76
+ css,
77
+ ...(sources === undefined ? {} : { sources }),
78
+ }
79
+ const candidates = await extractValidCandidates(extractOptions)
80
+ for (const candidate of candidates) {
81
+ if (options.filter(candidate)) {
82
+ set.add(candidate)
83
+ }
84
+ }
85
+ }
86
+ }
87
+ else {
88
+ const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase
89
+ const sources = resolveSources(baseForCss)
90
+ const extractOptions = {
91
+ cwd: options.projectRoot,
92
+ base: baseForCss,
93
+ ...(v4Options.css === undefined ? {} : { css: v4Options.css }),
94
+ ...(sources === undefined ? {} : { sources }),
95
+ }
96
+ const candidates = await extractValidCandidates(extractOptions)
97
+ for (const candidate of candidates) {
98
+ if (options.filter(candidate)) {
99
+ set.add(candidate)
100
+ }
101
+ }
102
+ }
103
+
104
+ return set
105
+ }
@@ -0,0 +1,148 @@
1
+ import type { PackageInfo } from 'local-pkg'
2
+ import type { NormalizedTailwindCssPatchOptions } from '../config'
3
+ import type { PatchStatusReport, TailwindcssRuntimeContext } from '../types'
4
+ import type { applyTailwindPatches } from '../install/patch-runner'
5
+ import { collectClassesFromContexts, collectClassesFromTailwindV4 } from '../install/class-collector'
6
+ import { loadRuntimeContexts } from '../install/context-registry'
7
+ import { applyTailwindPatches as runPatch } from '../install/patch-runner'
8
+ import { runTailwindBuild } from '../install/process-tailwindcss'
9
+ import { getPatchStatusReport } from '../install/status'
10
+
11
+ export type TailwindMajorVersion = 2 | 3 | 4
12
+ export type PatchResult = ReturnType<typeof applyTailwindPatches>
13
+
14
+ export interface TailwindCollector {
15
+ patch(): Promise<PatchResult>
16
+ getPatchStatus(): Promise<PatchStatusReport>
17
+ collectClassSet(): Promise<Set<string>>
18
+ getContexts(): TailwindcssRuntimeContext[]
19
+ getPatchSnapshot(): string
20
+ runTailwindBuildIfNeeded?(): Promise<void>
21
+ }
22
+
23
+ function resolveTailwindExecutionOptions(
24
+ normalized: NormalizedTailwindCssPatchOptions,
25
+ majorVersion: 2 | 3,
26
+ ) {
27
+ const base = normalized.tailwind
28
+ if (majorVersion === 2 && base.v2) {
29
+ return {
30
+ cwd: base.v2.cwd ?? base.cwd ?? normalized.projectRoot,
31
+ config: base.v2.config ?? base.config,
32
+ postcssPlugin: base.v2.postcssPlugin ?? base.postcssPlugin,
33
+ }
34
+ }
35
+
36
+ if (majorVersion === 3 && base.v3) {
37
+ return {
38
+ cwd: base.v3.cwd ?? base.cwd ?? normalized.projectRoot,
39
+ config: base.v3.config ?? base.config,
40
+ postcssPlugin: base.v3.postcssPlugin ?? base.postcssPlugin,
41
+ }
42
+ }
43
+
44
+ return {
45
+ cwd: base.cwd ?? normalized.projectRoot,
46
+ config: base.config,
47
+ postcssPlugin: base.postcssPlugin,
48
+ }
49
+ }
50
+
51
+ abstract class BaseCollector implements TailwindCollector {
52
+ constructor(
53
+ protected readonly packageInfo: PackageInfo,
54
+ protected readonly options: NormalizedTailwindCssPatchOptions,
55
+ protected readonly majorVersion: TailwindMajorVersion,
56
+ ) {}
57
+
58
+ async patch() {
59
+ return runPatch({
60
+ packageInfo: this.packageInfo,
61
+ options: this.options,
62
+ majorVersion: this.majorVersion,
63
+ })
64
+ }
65
+
66
+ async getPatchStatus() {
67
+ return getPatchStatusReport({
68
+ packageInfo: this.packageInfo,
69
+ options: this.options,
70
+ majorVersion: this.majorVersion,
71
+ })
72
+ }
73
+
74
+ getContexts() {
75
+ return loadRuntimeContexts(
76
+ this.packageInfo,
77
+ this.majorVersion,
78
+ this.options.features.exposeContext.refProperty,
79
+ )
80
+ }
81
+
82
+ abstract collectClassSet(): Promise<Set<string>>
83
+ abstract getPatchSnapshot(): string
84
+ }
85
+
86
+ export class RuntimeCollector extends BaseCollector {
87
+ private inFlightBuild: Promise<void> | undefined
88
+
89
+ constructor(
90
+ packageInfo: PackageInfo,
91
+ options: NormalizedTailwindCssPatchOptions,
92
+ majorVersion: 2 | 3,
93
+ private readonly snapshotFactory: () => string,
94
+ ) {
95
+ super(packageInfo, options, majorVersion)
96
+ }
97
+
98
+ async collectClassSet() {
99
+ const contexts = this.getContexts()
100
+ return collectClassesFromContexts(contexts, this.options.filter)
101
+ }
102
+
103
+ getPatchSnapshot() {
104
+ return this.snapshotFactory()
105
+ }
106
+
107
+ async runTailwindBuildIfNeeded() {
108
+ if (this.inFlightBuild) {
109
+ return this.inFlightBuild
110
+ }
111
+
112
+ const executionOptions = resolveTailwindExecutionOptions(this.options, this.majorVersion as 2 | 3)
113
+ const buildOptions = {
114
+ cwd: executionOptions.cwd,
115
+ majorVersion: this.majorVersion as 2 | 3,
116
+ ...(executionOptions.config === undefined ? {} : { config: executionOptions.config }),
117
+ ...(executionOptions.postcssPlugin === undefined ? {} : { postcssPlugin: executionOptions.postcssPlugin }),
118
+ }
119
+ this.inFlightBuild = runTailwindBuild(buildOptions).then(() => undefined)
120
+ try {
121
+ await this.inFlightBuild
122
+ }
123
+ finally {
124
+ this.inFlightBuild = undefined
125
+ }
126
+ }
127
+ }
128
+
129
+ export class TailwindV4Collector extends BaseCollector {
130
+ constructor(
131
+ packageInfo: PackageInfo,
132
+ options: NormalizedTailwindCssPatchOptions,
133
+ snapshotFactory: () => string,
134
+ ) {
135
+ super(packageInfo, options, 4)
136
+ this.snapshotFactory = snapshotFactory
137
+ }
138
+
139
+ private readonly snapshotFactory: () => string
140
+
141
+ async collectClassSet() {
142
+ return collectClassesFromTailwindV4(this.options)
143
+ }
144
+
145
+ getPatchSnapshot() {
146
+ return this.snapshotFactory()
147
+ }
148
+ }
@@ -0,0 +1,65 @@
1
+ import type { PackageInfo } from 'local-pkg'
2
+ import type { TailwindcssRuntimeContext } from '../types'
3
+ import { createRequire } from 'node:module'
4
+ import fs from 'fs-extra'
5
+ import path from 'pathe'
6
+
7
+ const require = createRequire(import.meta.url)
8
+
9
+ function resolveRuntimeEntry(packageInfo: PackageInfo, majorVersion: 2 | 3): string | undefined {
10
+ const root = packageInfo.rootPath
11
+
12
+ if (majorVersion === 2) {
13
+ const jitIndex = path.join(root, 'lib/jit/index.js')
14
+ if (fs.existsSync(jitIndex)) {
15
+ return jitIndex
16
+ }
17
+ }
18
+ else if (majorVersion === 3) {
19
+ const plugin = path.join(root, 'lib/plugin.js')
20
+ const index = path.join(root, 'lib/index.js')
21
+ if (fs.existsSync(plugin)) {
22
+ return plugin
23
+ }
24
+ if (fs.existsSync(index)) {
25
+ return index
26
+ }
27
+ }
28
+
29
+ return undefined
30
+ }
31
+
32
+ export function loadRuntimeContexts(
33
+ packageInfo: PackageInfo,
34
+ majorVersion: 2 | 3 | 4,
35
+ refProperty: string,
36
+ ): TailwindcssRuntimeContext[] {
37
+ if (majorVersion === 4) {
38
+ return []
39
+ }
40
+
41
+ const entry = resolveRuntimeEntry(packageInfo, majorVersion)
42
+ if (!entry) {
43
+ return []
44
+ }
45
+
46
+ const moduleExports = require(entry) as Record<string, any>
47
+ if (!moduleExports) {
48
+ return []
49
+ }
50
+
51
+ const ref = moduleExports[refProperty]
52
+ if (!ref) {
53
+ return []
54
+ }
55
+
56
+ if (Array.isArray(ref)) {
57
+ return ref as TailwindcssRuntimeContext[]
58
+ }
59
+
60
+ if (typeof ref === 'object' && Array.isArray(ref.value)) {
61
+ return ref.value as TailwindcssRuntimeContext[]
62
+ }
63
+
64
+ return []
65
+ }