@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.
Files changed (87) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +52 -0
  3. package/dist/bin/cli.js +14615 -0
  4. package/dist/build.d.ts +24 -0
  5. package/dist/config.d.ts +5 -0
  6. package/dist/generator.d.ts +31 -0
  7. package/dist/index.d.ts +10 -0
  8. package/dist/parser.d.ts +42 -0
  9. package/dist/plugin.d.ts +22 -0
  10. package/dist/preflight-forms.d.ts +5 -0
  11. package/dist/preflight.d.ts +2 -0
  12. package/dist/rules-advanced.d.ts +27 -0
  13. package/dist/rules-effects.d.ts +25 -0
  14. package/dist/rules-forms.d.ts +7 -0
  15. package/dist/rules-grid.d.ts +13 -0
  16. package/dist/rules-interactivity.d.ts +41 -0
  17. package/dist/rules-layout.d.ts +26 -0
  18. package/dist/rules-transforms.d.ts +33 -0
  19. package/dist/rules-typography.d.ts +41 -0
  20. package/dist/rules.d.ts +39 -0
  21. package/dist/scanner.d.ts +18 -0
  22. package/dist/src/index.js +12848 -0
  23. package/dist/transformer-compile-class.d.ts +37 -0
  24. package/{src/types.ts → dist/types.d.ts} +17 -86
  25. package/package.json +2 -16
  26. package/PLUGIN.md +0 -235
  27. package/benchmark/framework-comparison.bench.ts +0 -850
  28. package/bin/cli.ts +0 -365
  29. package/bin/crosswind +0 -0
  30. package/bin/headwind +0 -0
  31. package/build.ts +0 -8
  32. package/crosswind.config.ts +0 -9
  33. package/example/comprehensive.html +0 -70
  34. package/example/index.html +0 -21
  35. package/example/output.css +0 -236
  36. package/examples/plugin/README.md +0 -112
  37. package/examples/plugin/build.ts +0 -32
  38. package/examples/plugin/src/index.html +0 -34
  39. package/examples/plugin/src/index.ts +0 -7
  40. package/headwind +0 -2
  41. package/src/build.ts +0 -101
  42. package/src/config.ts +0 -529
  43. package/src/generator.ts +0 -2173
  44. package/src/index.ts +0 -10
  45. package/src/parser.ts +0 -1471
  46. package/src/plugin.ts +0 -118
  47. package/src/preflight-forms.ts +0 -229
  48. package/src/preflight.ts +0 -388
  49. package/src/rules-advanced.ts +0 -477
  50. package/src/rules-effects.ts +0 -461
  51. package/src/rules-forms.ts +0 -103
  52. package/src/rules-grid.ts +0 -241
  53. package/src/rules-interactivity.ts +0 -525
  54. package/src/rules-layout.ts +0 -385
  55. package/src/rules-transforms.ts +0 -412
  56. package/src/rules-typography.ts +0 -486
  57. package/src/rules.ts +0 -809
  58. package/src/scanner.ts +0 -84
  59. package/src/transformer-compile-class.ts +0 -275
  60. package/test/advanced-features.test.ts +0 -911
  61. package/test/arbitrary.test.ts +0 -396
  62. package/test/attributify.test.ts +0 -592
  63. package/test/bracket-syntax.test.ts +0 -1133
  64. package/test/build.test.ts +0 -99
  65. package/test/colors.test.ts +0 -934
  66. package/test/flexbox.test.ts +0 -669
  67. package/test/generator.test.ts +0 -597
  68. package/test/grid.test.ts +0 -584
  69. package/test/layout.test.ts +0 -404
  70. package/test/modifiers.test.ts +0 -417
  71. package/test/parser.test.ts +0 -564
  72. package/test/performance-regression.test.ts +0 -376
  73. package/test/performance.test.ts +0 -568
  74. package/test/plugin.test.ts +0 -160
  75. package/test/scanner.test.ts +0 -94
  76. package/test/sizing.test.ts +0 -481
  77. package/test/spacing.test.ts +0 -394
  78. package/test/transformer-compile-class.test.ts +0 -287
  79. package/test/transforms.test.ts +0 -448
  80. package/test/typography.test.ts +0 -632
  81. package/test/variants-form-states.test.ts +0 -225
  82. package/test/variants-group-peer.test.ts +0 -66
  83. package/test/variants-media.test.ts +0 -213
  84. package/test/variants-positional.test.ts +0 -58
  85. package/test/variants-pseudo-elements.test.ts +0 -47
  86. package/test/variants-state.test.ts +0 -62
  87. 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
- }