tailwindcss-patch 9.3.2 → 9.3.4
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.
- package/dist/cli.js +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/commands/cli-runtime.d.mts +1 -1
- package/dist/commands/cli-runtime.d.ts +1 -1
- package/dist/commands/cli-runtime.js +1 -1
- package/dist/commands/cli-runtime.mjs +1 -1
- package/dist/{index.bundle-CcVIAfDb.js → index.bundle-BIcthh-J.js} +25 -6
- package/dist/{index.bundle-DjS24i5r.mjs → index.bundle-BU7uUzRH.mjs} +25 -6
- package/dist/index.d.mts +2 -67
- package/dist/index.d.ts +2 -67
- package/dist/index.js +4 -2
- package/dist/index.mjs +3 -3
- package/dist/{validate-Bqych-z9.d.ts → validate-Bd5gnHL1.d.mts} +84 -7
- package/dist/{validate-Sq_yuh3r.mjs → validate-BoTQvn7s.mjs} +184 -7
- package/dist/{validate-CF0M_7KH.js → validate-DmELQoI2.js} +195 -6
- package/dist/{validate-C8-qsOWo.d.mts → validate-ZwyABmFW.d.ts} +85 -6
- package/package.json +1 -1
- package/src/extraction/candidate-extractor.ts +222 -1
- package/src/index.bundle.ts +5 -0
- package/src/index.ts +4 -0
- package/src/options/normalize.ts +14 -0
- package/src/options/types.ts +4 -0
- package/src/runtime/class-collector.ts +33 -13
- package/src/v4/index.ts +1 -0
- package/src/v4/source.ts +48 -6
- package/src/v4/types.ts +8 -0
|
@@ -55,6 +55,190 @@ export interface ExtractValidCandidatesOption {
|
|
|
55
55
|
bareArbitraryValues?: boolean | BareArbitraryValueOptions
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export interface ExtractSourceCandidate {
|
|
59
|
+
rawCandidate: string
|
|
60
|
+
start: number
|
|
61
|
+
end: number
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface ExtractSourceCandidateWithContext extends ExtractSourceCandidate {
|
|
65
|
+
content: string
|
|
66
|
+
extension: string
|
|
67
|
+
localStart: number
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const HTML_ATTRIBUTE_NAME_CANDIDATE_RE = /^(?:class|className|hover-class|hoverClass)$/
|
|
71
|
+
const CSS_DIRECTIVE_CANDIDATE_RE = /^@(?:apply|tailwind|source|config|plugin|theme|utility|custom-variant|variant)$/
|
|
72
|
+
const CSS_APPLY_IMPORTANT = '!important'
|
|
73
|
+
const JS_LIKE_SOURCE_EXTENSION_RE = /^(?:[cm]?[jt]sx?)$/
|
|
74
|
+
const MIXED_TEMPLATE_SOURCE_EXTENSION_RE = /^(?:vue|uvue|nvue|svelte|mpx)$/
|
|
75
|
+
const CSS_LIKE_SOURCE_EXTENSION_RE = /^(?:css|wxss|acss|jxss|ttss|qss|tyss|scss|sass|less|styl|stylus)$/
|
|
76
|
+
const SFC_SCRIPT_BLOCK_RE = /<script\b[^>]*>([\s\S]*?)<\/script>/gi
|
|
77
|
+
|
|
78
|
+
function isWhitespace(value: string | undefined) {
|
|
79
|
+
return value === ' ' || value === '\n' || value === '\r' || value === '\t' || value === '\f'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isHtmlAttributeNameCandidate(content: string, candidate: ExtractSourceCandidate) {
|
|
83
|
+
if (!HTML_ATTRIBUTE_NAME_CANDIDATE_RE.test(candidate.rawCandidate)) {
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
let index = candidate.end
|
|
87
|
+
while (isWhitespace(content[index])) {
|
|
88
|
+
index++
|
|
89
|
+
}
|
|
90
|
+
return content[index] === '='
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isInsideHtmlTagText(content: string, candidate: ExtractSourceCandidate) {
|
|
94
|
+
const open = content.lastIndexOf('<', candidate.start)
|
|
95
|
+
const close = content.lastIndexOf('>', candidate.start)
|
|
96
|
+
if (open > close) {
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
const nextOpen = content.indexOf('<', candidate.end)
|
|
100
|
+
return nextOpen !== -1 && (nextOpen < content.indexOf('>', candidate.end) || content.indexOf('>', candidate.end) === -1)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isCssDirectiveCandidate(candidate: string) {
|
|
104
|
+
return candidate === CSS_APPLY_IMPORTANT || CSS_DIRECTIVE_CANDIDATE_RE.test(candidate)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isCandidateInCssApplyParams(content: string, candidate: ExtractSourceCandidate) {
|
|
108
|
+
const apply = content.lastIndexOf('@apply', candidate.start)
|
|
109
|
+
if (apply === -1) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
const boundary = content.slice(apply + '@apply'.length, candidate.start)
|
|
113
|
+
return !/[;{}]/.test(boundary)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isCandidateInsideJsStringStaticContent(content: string, start: number) {
|
|
117
|
+
let quote: '"' | '\'' | '`' | undefined
|
|
118
|
+
let templateExpressionDepth = 0
|
|
119
|
+
for (let index = 0; index < start; index++) {
|
|
120
|
+
const char = content[index]
|
|
121
|
+
const next = content[index + 1]
|
|
122
|
+
|
|
123
|
+
if (quote && char === '\\') {
|
|
124
|
+
index++
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (quote === '`' && templateExpressionDepth > 0) {
|
|
129
|
+
if (char === '"' || char === '\'') {
|
|
130
|
+
const nestedQuote = char
|
|
131
|
+
index++
|
|
132
|
+
while (index < start) {
|
|
133
|
+
const nestedChar = content[index]
|
|
134
|
+
if (nestedChar === '\\') {
|
|
135
|
+
index += 2
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
if (nestedChar === nestedQuote) {
|
|
139
|
+
break
|
|
140
|
+
}
|
|
141
|
+
index++
|
|
142
|
+
}
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
if (char === '`') {
|
|
146
|
+
index++
|
|
147
|
+
while (index < start) {
|
|
148
|
+
const nestedChar = content[index]
|
|
149
|
+
if (nestedChar === '\\') {
|
|
150
|
+
index += 2
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
if (nestedChar === '`') {
|
|
154
|
+
break
|
|
155
|
+
}
|
|
156
|
+
index++
|
|
157
|
+
}
|
|
158
|
+
continue
|
|
159
|
+
}
|
|
160
|
+
if (char === '{') {
|
|
161
|
+
templateExpressionDepth++
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
if (char === '}') {
|
|
165
|
+
templateExpressionDepth--
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (quote) {
|
|
172
|
+
if (quote === '`' && char === '$' && next === '{') {
|
|
173
|
+
templateExpressionDepth = 1
|
|
174
|
+
index++
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
if (char === quote) {
|
|
178
|
+
quote = undefined
|
|
179
|
+
}
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (char === '"' || char === '\'' || char === '`') {
|
|
184
|
+
quote = char
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return quote !== undefined && templateExpressionDepth === 0
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function shouldKeepSourceCandidate(content: string, extension: string, candidate: ExtractSourceCandidate) {
|
|
192
|
+
if (!candidate.rawCandidate || isCssDirectiveCandidate(candidate.rawCandidate)) {
|
|
193
|
+
return false
|
|
194
|
+
}
|
|
195
|
+
if (CSS_LIKE_SOURCE_EXTENSION_RE.test(extension) && !isCandidateInCssApplyParams(content, candidate)) {
|
|
196
|
+
return false
|
|
197
|
+
}
|
|
198
|
+
if (isHtmlAttributeNameCandidate(content, candidate)) {
|
|
199
|
+
return false
|
|
200
|
+
}
|
|
201
|
+
if (isInsideHtmlTagText(content, candidate)) {
|
|
202
|
+
return false
|
|
203
|
+
}
|
|
204
|
+
if (
|
|
205
|
+
JS_LIKE_SOURCE_EXTENSION_RE.test(extension)
|
|
206
|
+
&& !isCandidateInsideJsStringStaticContent(content, candidate.start)
|
|
207
|
+
) {
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
return true
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function createLocalCandidate(candidate: ExtractSourceCandidateWithContext): ExtractSourceCandidate {
|
|
214
|
+
return {
|
|
215
|
+
rawCandidate: candidate.rawCandidate,
|
|
216
|
+
start: candidate.localStart,
|
|
217
|
+
end: candidate.localStart + candidate.rawCandidate.length,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function extractMixedSourceScriptCandidates(content: string) {
|
|
222
|
+
const candidates: ExtractSourceCandidateWithContext[] = []
|
|
223
|
+
SFC_SCRIPT_BLOCK_RE.lastIndex = 0
|
|
224
|
+
let match = SFC_SCRIPT_BLOCK_RE.exec(content)
|
|
225
|
+
while (match !== null) {
|
|
226
|
+
const scriptContent = match[1] ?? ''
|
|
227
|
+
const scriptStart = match.index + match[0].indexOf(scriptContent)
|
|
228
|
+
const scriptCandidates = await extractRawCandidatesWithPositions(scriptContent, 'js')
|
|
229
|
+
candidates.push(...scriptCandidates.map(candidate => ({
|
|
230
|
+
content: scriptContent,
|
|
231
|
+
extension: 'js',
|
|
232
|
+
localStart: candidate.start,
|
|
233
|
+
rawCandidate: candidate.rawCandidate,
|
|
234
|
+
start: candidate.start + scriptStart,
|
|
235
|
+
end: candidate.end + scriptStart,
|
|
236
|
+
})))
|
|
237
|
+
match = SFC_SCRIPT_BLOCK_RE.exec(content)
|
|
238
|
+
}
|
|
239
|
+
return candidates
|
|
240
|
+
}
|
|
241
|
+
|
|
58
242
|
function createCandidateCacheKey(
|
|
59
243
|
designSystemKey: string,
|
|
60
244
|
options: Pick<ExtractValidCandidatesOption, 'bareArbitraryValues'>,
|
|
@@ -68,7 +252,7 @@ function createCandidateCacheKey(
|
|
|
68
252
|
export async function extractRawCandidatesWithPositions(
|
|
69
253
|
content: string,
|
|
70
254
|
extension: string = 'html',
|
|
71
|
-
): Promise<
|
|
255
|
+
): Promise<ExtractSourceCandidate[]> {
|
|
72
256
|
const { Scanner } = await getOxideModule()
|
|
73
257
|
const scanner = new Scanner({})
|
|
74
258
|
const result = scanner.getCandidatesWithPositions({ content, extension })
|
|
@@ -80,6 +264,43 @@ export async function extractRawCandidatesWithPositions(
|
|
|
80
264
|
}))
|
|
81
265
|
}
|
|
82
266
|
|
|
267
|
+
export async function extractSourceCandidatesWithPositions(
|
|
268
|
+
content: string,
|
|
269
|
+
extension: string = 'html',
|
|
270
|
+
): Promise<ExtractSourceCandidate[]> {
|
|
271
|
+
const normalizedExtension = extension.replace(/^\./, '')
|
|
272
|
+
const candidates: ExtractSourceCandidateWithContext[] = (await extractRawCandidatesWithPositions(content, normalizedExtension))
|
|
273
|
+
.map(candidate => ({
|
|
274
|
+
...candidate,
|
|
275
|
+
content,
|
|
276
|
+
extension: normalizedExtension,
|
|
277
|
+
localStart: candidate.start,
|
|
278
|
+
}))
|
|
279
|
+
if (MIXED_TEMPLATE_SOURCE_EXTENSION_RE.test(normalizedExtension)) {
|
|
280
|
+
candidates.push(...await extractMixedSourceScriptCandidates(content))
|
|
281
|
+
}
|
|
282
|
+
const seen = new Set<string>()
|
|
283
|
+
return candidates.filter((candidate) => {
|
|
284
|
+
if (!shouldKeepSourceCandidate(candidate.content, candidate.extension, createLocalCandidate(candidate))) {
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
const key = `${candidate.start}:${candidate.end}:${candidate.rawCandidate}`
|
|
288
|
+
if (seen.has(key)) {
|
|
289
|
+
return false
|
|
290
|
+
}
|
|
291
|
+
seen.add(key)
|
|
292
|
+
return true
|
|
293
|
+
}).map(({ rawCandidate, start, end }) => ({ rawCandidate, start, end }))
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function extractSourceCandidates(
|
|
297
|
+
content: string,
|
|
298
|
+
extension: string = 'html',
|
|
299
|
+
): Promise<string[]> {
|
|
300
|
+
const candidates = await extractSourceCandidatesWithPositions(content, extension)
|
|
301
|
+
return [...new Set(candidates.map(candidate => candidate.rawCandidate))]
|
|
302
|
+
}
|
|
303
|
+
|
|
83
304
|
export async function extractRawCandidates(
|
|
84
305
|
sources?: SourceEntry[],
|
|
85
306
|
): Promise<string[]> {
|
package/src/index.bundle.ts
CHANGED
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
extractProjectCandidatesWithPositions,
|
|
23
23
|
extractRawCandidates,
|
|
24
24
|
extractRawCandidatesWithPositions,
|
|
25
|
+
extractSourceCandidates,
|
|
26
|
+
extractSourceCandidatesWithPositions,
|
|
25
27
|
extractValidCandidates,
|
|
26
28
|
groupTokensByFile,
|
|
27
29
|
} from './extraction/candidate-extractor'
|
|
@@ -57,6 +59,8 @@ export {
|
|
|
57
59
|
extractProjectCandidatesWithPositions,
|
|
58
60
|
extractRawCandidates,
|
|
59
61
|
extractRawCandidatesWithPositions,
|
|
62
|
+
extractSourceCandidates,
|
|
63
|
+
extractSourceCandidatesWithPositions,
|
|
60
64
|
extractValidCandidates,
|
|
61
65
|
getPatchStatusReport,
|
|
62
66
|
groupTokensByFile,
|
|
@@ -105,6 +109,7 @@ export type { TailwindCssPatchOptions } from './config'
|
|
|
105
109
|
export * from './types'
|
|
106
110
|
export type {
|
|
107
111
|
TailwindV4CandidateSource,
|
|
112
|
+
TailwindV4CssSource,
|
|
108
113
|
TailwindV4DesignSystem,
|
|
109
114
|
TailwindV4Engine,
|
|
110
115
|
TailwindV4GenerateOptions,
|
package/src/index.ts
CHANGED
|
@@ -39,8 +39,11 @@ export {
|
|
|
39
39
|
extractProjectCandidatesWithPositions,
|
|
40
40
|
extractRawCandidates,
|
|
41
41
|
extractRawCandidatesWithPositions,
|
|
42
|
+
extractSourceCandidates,
|
|
43
|
+
extractSourceCandidatesWithPositions,
|
|
42
44
|
extractValidCandidates,
|
|
43
45
|
groupTokensByFile,
|
|
46
|
+
type ExtractSourceCandidate,
|
|
44
47
|
} from './extraction/candidate-extractor'
|
|
45
48
|
export {
|
|
46
49
|
collectClassesFromContexts,
|
|
@@ -60,6 +63,7 @@ export {
|
|
|
60
63
|
} from './v4'
|
|
61
64
|
export type {
|
|
62
65
|
TailwindV4CandidateSource,
|
|
66
|
+
TailwindV4CssSource,
|
|
63
67
|
TailwindV4DesignSystem,
|
|
64
68
|
TailwindV4Engine,
|
|
65
69
|
TailwindV4GenerateOptions,
|
package/src/options/normalize.ts
CHANGED
|
@@ -148,6 +148,19 @@ function normalizeTailwindV4Options(
|
|
|
148
148
|
): NormalizedTailwindV4Options {
|
|
149
149
|
const configuredBase = v4?.base ? path.resolve(v4.base) : undefined
|
|
150
150
|
const base = configuredBase ?? fallbackBase
|
|
151
|
+
const resolveV4Path = (value: string) => path.isAbsolute(value) ? path.resolve(value) : path.resolve(fallbackBase, value)
|
|
152
|
+
const cssSources = Array.isArray(v4?.cssSources)
|
|
153
|
+
? v4!.cssSources
|
|
154
|
+
.filter(source => typeof source?.css === 'string')
|
|
155
|
+
.map(source => ({
|
|
156
|
+
css: source.css,
|
|
157
|
+
...(source.base === undefined ? {} : { base: resolveV4Path(source.base) }),
|
|
158
|
+
...(source.file === undefined ? {} : { file: resolveV4Path(source.file) }),
|
|
159
|
+
...(source.dependencies === undefined
|
|
160
|
+
? {}
|
|
161
|
+
: { dependencies: source.dependencies.filter(Boolean).map(resolveV4Path) }),
|
|
162
|
+
}))
|
|
163
|
+
: []
|
|
151
164
|
const cssEntries = Array.isArray(v4?.cssEntries)
|
|
152
165
|
? v4!.cssEntries.filter((entry): entry is string => Boolean(entry)).map(entry => path.resolve(entry))
|
|
153
166
|
: []
|
|
@@ -167,6 +180,7 @@ function normalizeTailwindV4Options(
|
|
|
167
180
|
base,
|
|
168
181
|
...(configuredBase === undefined ? {} : { configuredBase }),
|
|
169
182
|
...(v4?.css === undefined ? {} : { css: v4.css }),
|
|
183
|
+
cssSources,
|
|
170
184
|
cssEntries,
|
|
171
185
|
sources,
|
|
172
186
|
hasUserDefinedSources,
|
package/src/options/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SourceEntry } from '@tailwindcss/oxide'
|
|
2
2
|
import type { PackageResolvingOptions } from 'local-pkg'
|
|
3
3
|
import type { ILengthUnitsPatchOptions } from '../types'
|
|
4
|
+
import type { TailwindV4CssSource } from '../v4/types'
|
|
4
5
|
|
|
5
6
|
export type CacheStrategy = 'merge' | 'overwrite'
|
|
6
7
|
export type CacheDriver = 'file' | 'memory' | 'noop'
|
|
@@ -97,6 +98,8 @@ export interface TailwindV4Options {
|
|
|
97
98
|
base?: string
|
|
98
99
|
/** Raw CSS passed directly to the v4 design system. */
|
|
99
100
|
css?: string
|
|
101
|
+
/** 构建器在 CSS 落盘前捕获的内存 CSS 入口。 */
|
|
102
|
+
cssSources?: TailwindV4CssSource[]
|
|
100
103
|
/** Set of CSS entry files that should be scanned for `@config` directives. */
|
|
101
104
|
cssEntries?: string[]
|
|
102
105
|
/** Overrides the content sources scanned by the oxide scanner. */
|
|
@@ -187,6 +190,7 @@ export interface NormalizedTailwindV4Options {
|
|
|
187
190
|
base: string
|
|
188
191
|
configuredBase?: string
|
|
189
192
|
css?: string
|
|
193
|
+
cssSources: TailwindV4CssSource[]
|
|
190
194
|
cssEntries: string[]
|
|
191
195
|
sources: SourceEntry[]
|
|
192
196
|
hasUserDefinedSources: boolean
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ExtractValidCandidatesOption } from '../extraction/candidate-extractor'
|
|
1
2
|
import type { NormalizedTailwindCssPatchOptions } from '../options/types'
|
|
2
3
|
import type { TailwindcssRuntimeContext } from '../types'
|
|
3
4
|
import process from 'node:process'
|
|
@@ -63,6 +64,35 @@ export async function collectClassesFromTailwindV4(
|
|
|
63
64
|
negated: source.negated,
|
|
64
65
|
}))
|
|
65
66
|
}
|
|
67
|
+
const addCandidates = async (extractOptions: ExtractValidCandidatesOption) => {
|
|
68
|
+
const candidates = await extractValidCandidates(extractOptions)
|
|
69
|
+
for (const candidate of candidates) {
|
|
70
|
+
if (options.filter(candidate)) {
|
|
71
|
+
set.add(candidate)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (v4Options.cssSources.length > 0) {
|
|
77
|
+
for (const source of v4Options.cssSources) {
|
|
78
|
+
const sourceFile = toAbsolute(source.file)
|
|
79
|
+
const sourceBase = toAbsolute(source.base)
|
|
80
|
+
?? (sourceFile ? path.dirname(sourceFile) : resolvedDefaultBase)
|
|
81
|
+
const designSystemBases = resolvedConfiguredBase && resolvedConfiguredBase !== sourceBase
|
|
82
|
+
? [sourceBase, resolvedConfiguredBase]
|
|
83
|
+
: [sourceBase]
|
|
84
|
+
const sources = resolveSources(sourceBase)
|
|
85
|
+
const firstBase = designSystemBases[0] ?? sourceBase
|
|
86
|
+
await addCandidates({
|
|
87
|
+
cwd: options.projectRoot,
|
|
88
|
+
base: firstBase,
|
|
89
|
+
baseFallbacks: designSystemBases.slice(1),
|
|
90
|
+
css: source.css,
|
|
91
|
+
...(v4Options.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues }),
|
|
92
|
+
...(sources === undefined ? {} : { sources }),
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
66
96
|
|
|
67
97
|
if (v4Options.cssEntries.length > 0) {
|
|
68
98
|
for (const entry of v4Options.cssEntries) {
|
|
@@ -86,15 +116,10 @@ export async function collectClassesFromTailwindV4(
|
|
|
86
116
|
...(v4Options.bareArbitraryValues === undefined ? {} : { bareArbitraryValues: v4Options.bareArbitraryValues }),
|
|
87
117
|
...(sources === undefined ? {} : { sources }),
|
|
88
118
|
}
|
|
89
|
-
|
|
90
|
-
for (const candidate of candidates) {
|
|
91
|
-
if (options.filter(candidate)) {
|
|
92
|
-
set.add(candidate)
|
|
93
|
-
}
|
|
94
|
-
}
|
|
119
|
+
await addCandidates(extractOptions)
|
|
95
120
|
}
|
|
96
121
|
}
|
|
97
|
-
else {
|
|
122
|
+
else if (v4Options.cssSources.length === 0) {
|
|
98
123
|
const baseForCss = resolvedConfiguredBase ?? resolvedDefaultBase
|
|
99
124
|
const sources = resolveSources(baseForCss)
|
|
100
125
|
const extractOptions = {
|
|
@@ -104,12 +129,7 @@ export async function collectClassesFromTailwindV4(
|
|
|
104
129
|
...(v4Options.css === undefined ? {} : { css: v4Options.css }),
|
|
105
130
|
...(sources === undefined ? {} : { sources }),
|
|
106
131
|
}
|
|
107
|
-
|
|
108
|
-
for (const candidate of candidates) {
|
|
109
|
-
if (options.filter(candidate)) {
|
|
110
|
-
set.add(candidate)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
132
|
+
await addCandidates(extractOptions)
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
return set
|
package/src/v4/index.ts
CHANGED
package/src/v4/source.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NormalizedTailwindCssPatchOptions, TailwindCssPatchOptions } from '../config'
|
|
2
|
-
import type { TailwindV4ResolvedSource, TailwindV4SourceOptions } from './types'
|
|
2
|
+
import type { TailwindV4CssSource, TailwindV4ResolvedSource, TailwindV4SourceOptions } from './types'
|
|
3
3
|
import { promises as fs } from 'node:fs'
|
|
4
4
|
import process from 'node:process'
|
|
5
5
|
import path from 'pathe'
|
|
@@ -87,6 +87,35 @@ async function resolveCssEntries(entries: string[], projectRoot: string, base: s
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
function resolveCssSources(sources: TailwindV4CssSource[], projectRoot: string, base: string | undefined) {
|
|
91
|
+
const resolvedSources = sources.map(source => ({
|
|
92
|
+
...source,
|
|
93
|
+
base: source.base === undefined ? undefined : resolveBase(source.base, projectRoot),
|
|
94
|
+
file: source.file === undefined
|
|
95
|
+
? undefined
|
|
96
|
+
: path.isAbsolute(source.file)
|
|
97
|
+
? path.resolve(source.file)
|
|
98
|
+
: path.resolve(projectRoot, source.file),
|
|
99
|
+
dependencies: source.dependencies?.map(dependency =>
|
|
100
|
+
path.isAbsolute(dependency) ? path.resolve(dependency) : path.resolve(projectRoot, dependency),
|
|
101
|
+
) ?? [],
|
|
102
|
+
}))
|
|
103
|
+
const firstSource = resolvedSources[0]
|
|
104
|
+
const resolvedBase = base
|
|
105
|
+
?? firstSource?.base
|
|
106
|
+
?? (firstSource?.file ? path.dirname(firstSource.file) : projectRoot)
|
|
107
|
+
const dependencies = resolvedSources.flatMap(source => [
|
|
108
|
+
source.file,
|
|
109
|
+
...source.dependencies,
|
|
110
|
+
]).filter((dependency): dependency is string => Boolean(dependency))
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
base: resolvedBase,
|
|
114
|
+
css: resolvedSources.map(source => source.css).join('\n'),
|
|
115
|
+
dependencies,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
90
119
|
function normalizeResolvedSource(
|
|
91
120
|
source: {
|
|
92
121
|
projectRoot: string
|
|
@@ -129,15 +158,27 @@ export async function resolveTailwindV4Source(options: TailwindV4SourceOptions =
|
|
|
129
158
|
})
|
|
130
159
|
}
|
|
131
160
|
|
|
132
|
-
if (options.cssEntries?.length) {
|
|
133
|
-
const entries =
|
|
161
|
+
if (options.cssEntries?.length || options.cssSources?.length) {
|
|
162
|
+
const entries = options.cssEntries?.length
|
|
163
|
+
? await resolveCssEntries(options.cssEntries, projectRoot, configuredBase)
|
|
164
|
+
: undefined
|
|
165
|
+
const sources = options.cssSources?.length
|
|
166
|
+
? resolveCssSources(options.cssSources, projectRoot, configuredBase)
|
|
167
|
+
: undefined
|
|
168
|
+
const css = [
|
|
169
|
+
entries?.css,
|
|
170
|
+
sources?.css,
|
|
171
|
+
].filter(Boolean).join('\n')
|
|
134
172
|
return normalizeResolvedSource({
|
|
135
173
|
projectRoot,
|
|
136
174
|
cwd,
|
|
137
|
-
base: entries
|
|
175
|
+
base: configuredBase ?? entries?.base ?? sources?.base ?? cwd,
|
|
138
176
|
baseFallbacks,
|
|
139
|
-
css
|
|
140
|
-
dependencies:
|
|
177
|
+
css,
|
|
178
|
+
dependencies: [
|
|
179
|
+
...(entries?.dependencies ?? []),
|
|
180
|
+
...(sources?.dependencies ?? []),
|
|
181
|
+
],
|
|
141
182
|
})
|
|
142
183
|
}
|
|
143
184
|
|
|
@@ -177,6 +218,7 @@ function createSourceOptionsFromNormalizedPatchOptions(
|
|
|
177
218
|
...(v4?.configuredBase === undefined ? {} : { base: v4.configuredBase }),
|
|
178
219
|
baseFallbacks,
|
|
179
220
|
...(v4?.css === undefined ? {} : { css: v4.css }),
|
|
221
|
+
...(v4?.cssSources === undefined ? {} : { cssSources: v4.cssSources }),
|
|
180
222
|
...(v4?.cssEntries === undefined ? {} : { cssEntries: v4.cssEntries }),
|
|
181
223
|
packageName: options.tailwind.packageName,
|
|
182
224
|
}
|
package/src/v4/types.ts
CHANGED
|
@@ -4,10 +4,18 @@ export interface TailwindV4SourceOptions {
|
|
|
4
4
|
base?: string
|
|
5
5
|
baseFallbacks?: string[]
|
|
6
6
|
css?: string
|
|
7
|
+
cssSources?: TailwindV4CssSource[]
|
|
7
8
|
cssEntries?: string[]
|
|
8
9
|
packageName?: string
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
export interface TailwindV4CssSource {
|
|
13
|
+
css: string
|
|
14
|
+
base?: string
|
|
15
|
+
file?: string
|
|
16
|
+
dependencies?: string[]
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export interface TailwindV4ResolvedSource {
|
|
12
20
|
projectRoot: string
|
|
13
21
|
base: string
|