@cwcss/crosswind 0.1.5 → 0.2.0
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/LICENSE.md +21 -0
- package/README.md +52 -0
- package/dist/bin/cli.js +14615 -0
- package/dist/build.d.ts +24 -0
- package/dist/config.d.ts +5 -0
- package/dist/generator.d.ts +31 -0
- package/dist/index.d.ts +10 -0
- package/dist/parser.d.ts +42 -0
- package/dist/plugin.d.ts +22 -0
- package/dist/preflight-forms.d.ts +5 -0
- package/dist/preflight.d.ts +2 -0
- package/dist/rules-advanced.d.ts +27 -0
- package/dist/rules-effects.d.ts +25 -0
- package/dist/rules-forms.d.ts +7 -0
- package/dist/rules-grid.d.ts +13 -0
- package/dist/rules-interactivity.d.ts +41 -0
- package/dist/rules-layout.d.ts +26 -0
- package/dist/rules-transforms.d.ts +33 -0
- package/dist/rules-typography.d.ts +41 -0
- package/dist/rules.d.ts +39 -0
- package/dist/scanner.d.ts +18 -0
- package/dist/src/index.js +12848 -0
- package/dist/transformer-compile-class.d.ts +37 -0
- package/{src/types.ts → dist/types.d.ts} +17 -86
- package/package.json +2 -16
- package/PLUGIN.md +0 -235
- package/benchmark/framework-comparison.bench.ts +0 -850
- package/bin/cli.ts +0 -365
- package/bin/crosswind +0 -0
- package/bin/headwind +0 -0
- package/build.ts +0 -8
- package/crosswind.config.ts +0 -9
- package/example/comprehensive.html +0 -70
- package/example/index.html +0 -21
- package/example/output.css +0 -236
- package/examples/plugin/README.md +0 -112
- package/examples/plugin/build.ts +0 -32
- package/examples/plugin/src/index.html +0 -34
- package/examples/plugin/src/index.ts +0 -7
- package/headwind +0 -2
- package/src/build.ts +0 -101
- package/src/config.ts +0 -529
- package/src/generator.ts +0 -2173
- package/src/index.ts +0 -10
- package/src/parser.ts +0 -1471
- package/src/plugin.ts +0 -118
- package/src/preflight-forms.ts +0 -229
- package/src/preflight.ts +0 -388
- package/src/rules-advanced.ts +0 -477
- package/src/rules-effects.ts +0 -461
- package/src/rules-forms.ts +0 -103
- package/src/rules-grid.ts +0 -241
- package/src/rules-interactivity.ts +0 -525
- package/src/rules-layout.ts +0 -385
- package/src/rules-transforms.ts +0 -412
- package/src/rules-typography.ts +0 -486
- package/src/rules.ts +0 -809
- package/src/scanner.ts +0 -84
- package/src/transformer-compile-class.ts +0 -275
- package/test/advanced-features.test.ts +0 -911
- package/test/arbitrary.test.ts +0 -396
- package/test/attributify.test.ts +0 -592
- package/test/bracket-syntax.test.ts +0 -1133
- package/test/build.test.ts +0 -99
- package/test/colors.test.ts +0 -934
- package/test/flexbox.test.ts +0 -669
- package/test/generator.test.ts +0 -597
- package/test/grid.test.ts +0 -584
- package/test/layout.test.ts +0 -404
- package/test/modifiers.test.ts +0 -417
- package/test/parser.test.ts +0 -564
- package/test/performance-regression.test.ts +0 -376
- package/test/performance.test.ts +0 -568
- package/test/plugin.test.ts +0 -160
- package/test/scanner.test.ts +0 -94
- package/test/sizing.test.ts +0 -481
- package/test/spacing.test.ts +0 -394
- package/test/transformer-compile-class.test.ts +0 -287
- package/test/transforms.test.ts +0 -448
- package/test/typography.test.ts +0 -632
- package/test/variants-form-states.test.ts +0 -225
- package/test/variants-group-peer.test.ts +0 -66
- package/test/variants-media.test.ts +0 -213
- package/test/variants-positional.test.ts +0 -58
- package/test/variants-pseudo-elements.test.ts +0 -47
- package/test/variants-state.test.ts +0 -62
- package/tsconfig.json +0 -18
package/src/scanner.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { CompileClassTransformer } from './transformer-compile-class'
|
|
2
|
-
import type { ExtractClassesOptions } from './parser'
|
|
3
|
-
import { Glob } from 'bun'
|
|
4
|
-
import { extractClasses } from './parser'
|
|
5
|
-
|
|
6
|
-
export interface ScanResult {
|
|
7
|
-
classes: Set<string>
|
|
8
|
-
transformedFiles: Map<string, string>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Scans files for utility classes using Bun's fast Glob API
|
|
13
|
-
*/
|
|
14
|
-
export class Scanner {
|
|
15
|
-
constructor(
|
|
16
|
-
private patterns: string[],
|
|
17
|
-
private transformer: CompileClassTransformer | null | undefined = undefined,
|
|
18
|
-
private extractOptions: ExtractClassesOptions | undefined = undefined,
|
|
19
|
-
) {}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Scan all files matching the patterns and extract utility classes
|
|
23
|
-
*/
|
|
24
|
-
async scan(): Promise<ScanResult> {
|
|
25
|
-
const allClasses = new Set<string>()
|
|
26
|
-
const transformedFiles = new Map<string, string>()
|
|
27
|
-
|
|
28
|
-
// Use Promise.all to scan all patterns concurrently for better performance
|
|
29
|
-
await Promise.all(
|
|
30
|
-
this.patterns.map(async (pattern) => {
|
|
31
|
-
const glob = new Glob(pattern)
|
|
32
|
-
|
|
33
|
-
// Bun's glob.scan() returns an async iterable
|
|
34
|
-
for await (const file of glob.scan('.')) {
|
|
35
|
-
try {
|
|
36
|
-
let content = await Bun.file(file).text()
|
|
37
|
-
|
|
38
|
-
// Apply transformer if enabled
|
|
39
|
-
if (this.transformer) {
|
|
40
|
-
const result = this.transformer.processFile(content)
|
|
41
|
-
if (result.hasChanges) {
|
|
42
|
-
transformedFiles.set(file, result.content)
|
|
43
|
-
content = result.content
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const classes = extractClasses(content, this.extractOptions)
|
|
48
|
-
|
|
49
|
-
for (const cls of classes) {
|
|
50
|
-
allClasses.add(cls)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Silently skip files that can't be read
|
|
55
|
-
// (e.g., binary files, permission issues)
|
|
56
|
-
continue
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}),
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
return { classes: allClasses, transformedFiles }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Scan a single file for utility classes
|
|
67
|
-
*/
|
|
68
|
-
async scanFile(filePath: string): Promise<Set<string>> {
|
|
69
|
-
try {
|
|
70
|
-
const content = await Bun.file(filePath).text()
|
|
71
|
-
return extractClasses(content, this.extractOptions)
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return new Set<string>()
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Scan content string for utility classes
|
|
80
|
-
*/
|
|
81
|
-
scanContent(content: string): Set<string> {
|
|
82
|
-
return extractClasses(content, this.extractOptions)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import type { CrosswindConfig } from './types'
|
|
2
|
-
|
|
3
|
-
export interface CompileClassOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Trigger string to mark classes for compilation
|
|
6
|
-
* @default ':hw:'
|
|
7
|
-
*/
|
|
8
|
-
trigger?: string
|
|
9
|
-
/**
|
|
10
|
-
* Prefix for generated class names
|
|
11
|
-
* @default 'hw-'
|
|
12
|
-
*/
|
|
13
|
-
classPrefix?: string
|
|
14
|
-
/**
|
|
15
|
-
* Hash function to generate class names
|
|
16
|
-
*/
|
|
17
|
-
hashFn?: (content: string) => string
|
|
18
|
-
/**
|
|
19
|
-
* Layer name for compiled classes
|
|
20
|
-
* @default 'shortcuts'
|
|
21
|
-
*/
|
|
22
|
-
layer?: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Simple hash function for generating class names
|
|
27
|
-
*/
|
|
28
|
-
function simpleHash(str: string): string {
|
|
29
|
-
let hash = 0
|
|
30
|
-
for (let i = 0; i < str.length; i++) {
|
|
31
|
-
const char = str.charCodeAt(i)
|
|
32
|
-
hash = (hash << 5) - hash + char
|
|
33
|
-
hash = hash & hash // Convert to 32-bit integer
|
|
34
|
-
}
|
|
35
|
-
return Math.abs(hash).toString(36)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Extract compile class markers from content
|
|
40
|
-
*/
|
|
41
|
-
export function extractCompileClasses(
|
|
42
|
-
content: string,
|
|
43
|
-
options: CompileClassOptions = {},
|
|
44
|
-
): Map<string, string[]> {
|
|
45
|
-
const trigger = options.trigger || ':hw:'
|
|
46
|
-
const compiledClasses = new Map<string, string[]>()
|
|
47
|
-
|
|
48
|
-
// Match class attributes with the trigger
|
|
49
|
-
// Supports: class=":uno: ..." and className=":uno: ..."
|
|
50
|
-
const classRegex = /(?:class|className)=["']([^"']*)["']/g
|
|
51
|
-
let match: RegExpExecArray | null
|
|
52
|
-
|
|
53
|
-
// eslint-disable-next-line no-cond-assign
|
|
54
|
-
while ((match = classRegex.exec(content)) !== null) {
|
|
55
|
-
const fullClass = match[1]
|
|
56
|
-
|
|
57
|
-
// Check if it starts with the trigger
|
|
58
|
-
if (fullClass.trim().startsWith(trigger)) {
|
|
59
|
-
// Remove the trigger and get the classes
|
|
60
|
-
const classes = fullClass
|
|
61
|
-
.replace(trigger, '')
|
|
62
|
-
.trim()
|
|
63
|
-
.split(/\s+/)
|
|
64
|
-
.filter(Boolean)
|
|
65
|
-
|
|
66
|
-
if (classes.length > 0) {
|
|
67
|
-
// Generate a unique identifier for this group of classes
|
|
68
|
-
const classKey = classes.sort().join(' ')
|
|
69
|
-
if (!compiledClasses.has(classKey)) {
|
|
70
|
-
compiledClasses.set(classKey, classes)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return compiledClasses
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Transform content by replacing compile markers with generated class names
|
|
81
|
-
*/
|
|
82
|
-
export function transformContent(
|
|
83
|
-
content: string,
|
|
84
|
-
compiledClassMap: Map<string, string>,
|
|
85
|
-
options: CompileClassOptions = {},
|
|
86
|
-
): string {
|
|
87
|
-
const trigger = options.trigger || ':hw:'
|
|
88
|
-
let transformed = content
|
|
89
|
-
|
|
90
|
-
const classRegex = /(?:class|className)=["']([^"']*)["']/g
|
|
91
|
-
let match: RegExpExecArray | null
|
|
92
|
-
|
|
93
|
-
// We need to replace in reverse order to maintain string positions
|
|
94
|
-
const replacements: Array<{ start: number, end: number, replacement: string }> = []
|
|
95
|
-
|
|
96
|
-
// eslint-disable-next-line no-cond-assign
|
|
97
|
-
while ((match = classRegex.exec(content)) !== null) {
|
|
98
|
-
const fullClass = match[1]
|
|
99
|
-
|
|
100
|
-
if (fullClass.trim().startsWith(trigger)) {
|
|
101
|
-
const classes = fullClass
|
|
102
|
-
.replace(trigger, '')
|
|
103
|
-
.trim()
|
|
104
|
-
.split(/\s+/)
|
|
105
|
-
.filter(Boolean)
|
|
106
|
-
|
|
107
|
-
const classKey = classes.sort().join(' ')
|
|
108
|
-
const generatedClass = compiledClassMap.get(classKey)
|
|
109
|
-
|
|
110
|
-
if (generatedClass) {
|
|
111
|
-
const attrName = match[0].startsWith('class=') ? 'class' : 'className'
|
|
112
|
-
const quote = match[0].includes('"') ? '"' : '\''
|
|
113
|
-
const replacement = `${attrName}=${quote}${generatedClass}${quote}`
|
|
114
|
-
|
|
115
|
-
replacements.push({
|
|
116
|
-
start: match.index,
|
|
117
|
-
end: match.index + match[0].length,
|
|
118
|
-
replacement,
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Apply replacements in reverse order
|
|
125
|
-
for (let i = replacements.length - 1; i >= 0; i--) {
|
|
126
|
-
const { start, end, replacement } = replacements[i]
|
|
127
|
-
transformed = transformed.substring(0, start) + replacement + transformed.substring(end)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return transformed
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Generate class names for compiled classes
|
|
135
|
-
*/
|
|
136
|
-
export function generateCompiledClassNames(
|
|
137
|
-
compiledClasses: Map<string, string[]>,
|
|
138
|
-
options: CompileClassOptions = {},
|
|
139
|
-
): Map<string, string> {
|
|
140
|
-
const classPrefix = options.classPrefix || 'hw-'
|
|
141
|
-
const hashFn = options.hashFn || simpleHash
|
|
142
|
-
|
|
143
|
-
const classMap = new Map<string, string>()
|
|
144
|
-
|
|
145
|
-
for (const [classKey] of compiledClasses) {
|
|
146
|
-
const hash = hashFn(classKey)
|
|
147
|
-
const generatedClassName = `${classPrefix}${hash}`
|
|
148
|
-
classMap.set(classKey, generatedClassName)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return classMap
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Main transformer class
|
|
156
|
-
*/
|
|
157
|
-
export class CompileClassTransformer {
|
|
158
|
-
private compiledClasses = new Map<string, string[]>()
|
|
159
|
-
private classNameMap = new Map<string, string>()
|
|
160
|
-
private options: CompileClassOptions
|
|
161
|
-
|
|
162
|
-
constructor(options: CompileClassOptions = {}) {
|
|
163
|
-
this.options = {
|
|
164
|
-
trigger: ':hw:',
|
|
165
|
-
classPrefix: 'hw-',
|
|
166
|
-
layer: 'shortcuts',
|
|
167
|
-
...options,
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Process a file and extract compile classes
|
|
173
|
-
*/
|
|
174
|
-
processFile(content: string): { content: string, hasChanges: boolean } {
|
|
175
|
-
const extracted = extractCompileClasses(content, this.options)
|
|
176
|
-
|
|
177
|
-
if (extracted.size === 0) {
|
|
178
|
-
return { content, hasChanges: false }
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Merge with existing compiled classes
|
|
182
|
-
let hasNewClasses = false
|
|
183
|
-
for (const [key, classes] of extracted) {
|
|
184
|
-
if (!this.compiledClasses.has(key)) {
|
|
185
|
-
this.compiledClasses.set(key, classes)
|
|
186
|
-
hasNewClasses = true
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Generate class names if we have new classes
|
|
191
|
-
if (hasNewClasses || this.classNameMap.size !== this.compiledClasses.size) {
|
|
192
|
-
this.classNameMap = generateCompiledClassNames(this.compiledClasses, this.options)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Transform the content
|
|
196
|
-
const transformed = transformContent(content, this.classNameMap, this.options)
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
content: transformed,
|
|
200
|
-
hasChanges: transformed !== content,
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Get all compiled classes and their generated names
|
|
206
|
-
*/
|
|
207
|
-
getCompiledClasses(): Map<string, { className: string, utilities: string[] }> {
|
|
208
|
-
const result = new Map<string, { className: string, utilities: string[] }>()
|
|
209
|
-
|
|
210
|
-
for (const [key, utilities] of this.compiledClasses) {
|
|
211
|
-
const className = this.classNameMap.get(key)
|
|
212
|
-
if (className) {
|
|
213
|
-
result.set(key, { className, utilities })
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return result
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Generate CSS for compiled classes
|
|
222
|
-
*/
|
|
223
|
-
generateCSS(config: CrosswindConfig, generator: any): string {
|
|
224
|
-
const compiledClasses = this.getCompiledClasses()
|
|
225
|
-
let css = ''
|
|
226
|
-
|
|
227
|
-
for (const [, { className, utilities }] of compiledClasses) {
|
|
228
|
-
// Generate CSS for each utility in the group
|
|
229
|
-
for (const utility of utilities) {
|
|
230
|
-
generator.generate(utility)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Get the generated CSS and wrap it with the compiled class name
|
|
234
|
-
const _generatedCSS = generator.toCSS(false)
|
|
235
|
-
|
|
236
|
-
// We need to extract just the CSS for these utilities
|
|
237
|
-
// This is a simplified approach - in production you'd want to track
|
|
238
|
-
// which rules were generated for which utilities
|
|
239
|
-
css += `\n/* Compiled class: ${className} */\n`
|
|
240
|
-
css += `/* Original utilities: ${utilities.join(' ')} */\n`
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return css
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Reset the transformer state
|
|
248
|
-
*/
|
|
249
|
-
reset(): void {
|
|
250
|
-
this.compiledClasses.clear()
|
|
251
|
-
this.classNameMap.clear()
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Get statistics about compiled classes
|
|
256
|
-
*/
|
|
257
|
-
getStats(): {
|
|
258
|
-
totalGroups: number
|
|
259
|
-
totalUtilities: number
|
|
260
|
-
averageUtilitiesPerGroup: number
|
|
261
|
-
} {
|
|
262
|
-
let totalUtilities = 0
|
|
263
|
-
for (const [, utilities] of this.compiledClasses) {
|
|
264
|
-
totalUtilities += utilities.length
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return {
|
|
268
|
-
totalGroups: this.compiledClasses.size,
|
|
269
|
-
totalUtilities,
|
|
270
|
-
averageUtilitiesPerGroup: this.compiledClasses.size > 0
|
|
271
|
-
? totalUtilities / this.compiledClasses.size
|
|
272
|
-
: 0,
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|