@cwcss/crosswind 0.1.5 → 0.1.6

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