cataract 0.2.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/.rubocop.yml +2 -0
- data/BENCHMARKS.md +41 -38
- data/CHANGELOG.md +13 -0
- data/README.md +9 -3
- data/ext/cataract/cataract.c +273 -92
- data/ext/cataract/cataract.h +4 -3
- data/ext/cataract/css_parser.c +125 -11
- data/ext/cataract/flatten.c +271 -16
- data/lib/cataract/declaration.rb +19 -0
- data/lib/cataract/pure/flatten.rb +103 -8
- data/lib/cataract/pure/parser.rb +203 -139
- data/lib/cataract/pure/serializer.rb +217 -115
- data/lib/cataract/pure.rb +4 -2
- data/lib/cataract/rule.rb +39 -3
- data/lib/cataract/stylesheet.rb +137 -14
- data/lib/cataract/stylesheet_scope.rb +11 -4
- data/lib/cataract/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3e4b300f6a6341cae02280d452f06ee15685f516a3c2e38fe2ebb80ea321f5c9
|
|
4
|
+
data.tar.gz: 427b9ed2a6b89f775943ac6e4d912734676cd21481b2b5b070cae0a3e0624eaa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce2ff83a256eed934014f9452d52a768a984eb89beaa801de17ceaabf603340c6ebe6daefedd47733febd025ce79d7c979739450867e9b9cc75f0ccf266f4c7c
|
|
7
|
+
data.tar.gz: 98f270d1a6293d35b343ec84f11d1c625987dfa3ebee2f3b6155c8909dc29c3a4f30b485828bc0c1a35f51bbffa8ebae9db40cf324b3f6c9d037a0dd3ab08028
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -32,7 +32,7 @@ jobs:
|
|
|
32
32
|
uses: actions/cache@v4
|
|
33
33
|
with:
|
|
34
34
|
path: .lint-passed
|
|
35
|
-
key: lint-${{ hashFiles('ext/**/*.c', 'ext/**/*.h', '.clang-tidy', 'Gemfile') }}
|
|
35
|
+
key: lint-${{ hashFiles('ext/**/*.c', 'ext/**/*.h', '.clang-tidy', 'Gemfile', '.github/workflows/*.yml', 'cataract.gemspec', 'Rakefile') }}
|
|
36
36
|
|
|
37
37
|
- name: Install clang-tidy
|
|
38
38
|
if: steps.lint-cache.outputs.cache-hit != 'true'
|
data/.rubocop.yml
CHANGED
|
@@ -171,5 +171,7 @@ Cataract/BanAssertIncludes:
|
|
|
171
171
|
Include:
|
|
172
172
|
- 'test/**/*'
|
|
173
173
|
Exclude:
|
|
174
|
+
- 'test/color/**/*.rb'
|
|
174
175
|
- 'test/test_benchmark_doc_generator.rb'
|
|
176
|
+
- 'test/test_speedup_calculator.rb'
|
|
175
177
|
- 'test/support/**/*' # Support files define assert_contains which uses assert_includes internally
|
data/BENCHMARKS.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Performance Benchmarks
|
|
6
6
|
|
|
7
|
-
Performance comparison between Cataract's C extension and pure Ruby implementations
|
|
7
|
+
Performance comparison between Cataract's C extension and pure Ruby implementations.
|
|
8
8
|
|
|
9
9
|
## Test Environment
|
|
10
10
|
|
|
@@ -18,18 +18,19 @@ Performance comparison between Cataract's C extension and pure Ruby implementati
|
|
|
18
18
|
|
|
19
19
|
Time to parse CSS into internal data structures
|
|
20
20
|
|
|
21
|
-
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
22
|
-
|
|
23
|
-
| Small CSS (64 lines, 1.0KB) |
|
|
24
|
-
| Medium CSS with @media (139 lines, 1.6KB) |
|
|
21
|
+
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
22
|
+
|-----------|--------|----------------|-------------|
|
|
23
|
+
| Small CSS (64 lines, 1.0KB) | 36.46K i/s | 3.46K i/s | 14.1K i/s |
|
|
24
|
+
| Medium CSS with @media (139 lines, 1.6KB) | 37.11K i/s | 2.1K i/s | 9.26K i/s |
|
|
25
|
+
| Selector lists (3500 lines, 62.5KB, 500 lists) | 447.6 i/s | 57.9 i/s | 212.5 i/s |
|
|
25
26
|
|
|
26
27
|
### Speedups
|
|
27
28
|
|
|
28
29
|
| Comparison | Speedup |
|
|
29
30
|
|------------|---------|
|
|
30
|
-
| Native vs Pure (no YJIT) |
|
|
31
|
-
| Native vs Pure (YJIT) |
|
|
32
|
-
| YJIT impact on Pure Ruby | 4.
|
|
31
|
+
| Native vs Pure (no YJIT) | 12.14x faster (avg) |
|
|
32
|
+
| Native vs Pure (YJIT) | 2.89x faster (avg) |
|
|
33
|
+
| YJIT impact on Pure Ruby | 4.14x faster (avg) |
|
|
33
34
|
|
|
34
35
|
---
|
|
35
36
|
|
|
@@ -37,18 +38,21 @@ Time to parse CSS into internal data structures
|
|
|
37
38
|
|
|
38
39
|
Time to convert parsed CSS back to string format
|
|
39
40
|
|
|
40
|
-
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
|
41
|
+
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
42
|
+
|-----------|--------|----------------|-------------|
|
|
43
|
+
| to_s (Bootstrap - 191KB) | 1.16K i/s | 34.9 i/s | 49.7 i/s |
|
|
44
|
+
| to_s (Compact utilities - 2.9KB) | 1.16K i/s | 34.9 i/s | 49.7 i/s |
|
|
45
|
+
| to_formatted_s (Nested CSS - 1.3KB) | 158.75K i/s | 63.62K i/s | 114.79K i/s |
|
|
46
|
+
| to_s with selector_lists (3.4KB) | 17.95K i/s | 1.43K i/s | 1.99K i/s |
|
|
47
|
+
| Media filtering (Bootstrap print only) | 235.05K i/s | 98.95K i/s | 149.3K i/s |
|
|
44
48
|
|
|
45
49
|
### Speedups
|
|
46
50
|
|
|
47
51
|
| Comparison | Speedup |
|
|
48
52
|
|------------|---------|
|
|
49
|
-
| Native vs Pure (no YJIT) |
|
|
50
|
-
| Native vs Pure (YJIT) |
|
|
51
|
-
| YJIT impact on Pure Ruby | 1.
|
|
53
|
+
| Native vs Pure (no YJIT) | 10.97x faster (avg) |
|
|
54
|
+
| Native vs Pure (YJIT) | 7.56x faster (avg) |
|
|
55
|
+
| YJIT impact on Pure Ruby | 1.57x faster (avg) |
|
|
52
56
|
|
|
53
57
|
---
|
|
54
58
|
|
|
@@ -56,22 +60,22 @@ Time to convert parsed CSS back to string format
|
|
|
56
60
|
|
|
57
61
|
Time to calculate CSS selector specificity values
|
|
58
62
|
|
|
59
|
-
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
60
|
-
|
|
61
|
-
| Simple Selectors |
|
|
62
|
-
| Compound Selectors | 6.
|
|
63
|
-
| Combinators | 5.
|
|
64
|
-
| Pseudo-classes & Pseudo-elements | 5.
|
|
65
|
-
| :not() Pseudo-class (CSS3) | 3.
|
|
66
|
-
| Complex Real-world Selectors | 4.
|
|
63
|
+
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
64
|
+
|-----------|--------|----------------|-------------|
|
|
65
|
+
| Simple Selectors | 8.42M i/s | 508.29K i/s | 2.6M i/s |
|
|
66
|
+
| Compound Selectors | 6.6M i/s | 186.16K i/s | 400.7K i/s |
|
|
67
|
+
| Combinators | 5.21M i/s | 147.76K i/s | 255.26K i/s |
|
|
68
|
+
| Pseudo-classes & Pseudo-elements | 5.39M i/s | 117.25K i/s | 196.91K i/s |
|
|
69
|
+
| :not() Pseudo-class (CSS3) | 3.48M i/s | 103.11K i/s | 164.66K i/s |
|
|
70
|
+
| Complex Real-world Selectors | 4.05M i/s | 52.17K i/s | 78.28K i/s |
|
|
67
71
|
|
|
68
72
|
### Speedups
|
|
69
73
|
|
|
70
74
|
| Comparison | Speedup |
|
|
71
75
|
|------------|---------|
|
|
72
|
-
| Native vs Pure (no YJIT) |
|
|
73
|
-
| Native vs Pure (YJIT) |
|
|
74
|
-
| YJIT impact on Pure Ruby |
|
|
76
|
+
| Native vs Pure (no YJIT) | 40.75x faster (avg) |
|
|
77
|
+
| Native vs Pure (YJIT) | 23.38x faster (avg) |
|
|
78
|
+
| YJIT impact on Pure Ruby | 2.29x faster (avg) |
|
|
75
79
|
|
|
76
80
|
---
|
|
77
81
|
|
|
@@ -79,22 +83,22 @@ Time to calculate CSS selector specificity values
|
|
|
79
83
|
|
|
80
84
|
Time to flatten multiple CSS rule sets with same selector
|
|
81
85
|
|
|
82
|
-
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
83
|
-
|
|
84
|
-
| No shorthand properties (large) |
|
|
85
|
-
| Simple properties |
|
|
86
|
-
| Cascade with specificity |
|
|
87
|
-
| Important declarations |
|
|
88
|
-
| Shorthand expansion |
|
|
89
|
-
| Complex
|
|
86
|
+
| Test Case | Native | Pure (no YJIT) | Pure (YJIT) |
|
|
87
|
+
|-----------|--------|----------------|-------------|
|
|
88
|
+
| No shorthand properties (large) | 20.74K i/s | 3.15K i/s | 5.73K i/s |
|
|
89
|
+
| Simple properties | 155.01K i/s | 73.1K i/s | 99.95K i/s |
|
|
90
|
+
| Cascade with specificity | 194.63K i/s | 74.22K i/s | 104.8K i/s |
|
|
91
|
+
| Important declarations | 195.45K i/s | 72.82K i/s | 105.65K i/s |
|
|
92
|
+
| Shorthand expansion | 20.74K i/s | 3.15K i/s | 5.73K i/s |
|
|
93
|
+
| Complex flattening | 31.19K i/s | 15.81K i/s | 21.52K i/s |
|
|
90
94
|
|
|
91
95
|
### Speedups
|
|
92
96
|
|
|
93
97
|
| Comparison | Speedup |
|
|
94
98
|
|------------|---------|
|
|
95
|
-
| Native vs Pure (no YJIT) | 3.
|
|
96
|
-
| Native vs Pure (YJIT) | 1.
|
|
97
|
-
| YJIT impact on Pure Ruby | 1.
|
|
99
|
+
| Native vs Pure (no YJIT) | 3.01x faster (avg) |
|
|
100
|
+
| Native vs Pure (YJIT) | 1.97x faster (avg) |
|
|
101
|
+
| YJIT impact on Pure Ruby | 1.47x faster (avg) |
|
|
98
102
|
|
|
99
103
|
---
|
|
100
104
|
|
|
@@ -118,5 +122,4 @@ rake benchmark:generate_docs
|
|
|
118
122
|
|
|
119
123
|
- Benchmarks use benchmark-ips with 1-2s warmup and 2-5s measurement periods
|
|
120
124
|
- Measurements show median iterations per second (i/s)
|
|
121
|
-
- css_parser gem is included for reference comparison
|
|
122
125
|
- YJIT is enabled/disabled per subprocess for accurate comparison
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## [ Unreleased ]
|
|
2
|
+
|
|
3
|
+
## [0.2.2 - 2025-11-18]
|
|
4
|
+
|
|
5
|
+
- Feature: Selector list tracking - parser preserves comma-separated selector groupings (e.g., `h1, h2, h3`) through parse/flatten/serialize cycle
|
|
6
|
+
- Feature: Intelligent selector list serialization - automatically detects divergence during cascade and groups only matching rules
|
|
7
|
+
- Feature: Formatted CSS output with configurable line wrapping (`to_s(formatted: true, max_line_length: 80)`)
|
|
8
|
+
- Feature: Custom property (CSS variable) support - `Stylesheet#custom_properties` returns custom properties organized by media context
|
|
9
|
+
- Fix: Custom properties now preserve case-sensitivity per CSS spec (`--Color` vs `--color` are distinct)
|
|
10
|
+
- Fix: Custom properties support UTF-8 encoding for Unicode characters
|
|
11
|
+
- Fix: Property matching now supports prefix matching for vendor-prefixed properties
|
|
12
|
+
- Performance: Flatten operation optimized with manual iteration for selector list grouping
|
|
13
|
+
|
|
1
14
|
## [0.2.1] - 2025-11-14
|
|
2
15
|
|
|
3
16
|
- Fix serializer bug related to media queries
|
data/README.md
CHANGED
|
@@ -134,6 +134,12 @@ sheet.with_specificity(100..).each do |rule|
|
|
|
134
134
|
puts "High specificity: #{rule.selector} (#{rule.specificity})"
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# Filter by property (returns chainable scope)
|
|
138
|
+
sheet.with_property('margin') # Exact match: finds only 'margin' property
|
|
139
|
+
sheet.with_property('margin', prefix_match: true) # Prefix match: finds margin, margin-top, margin-left, etc.
|
|
140
|
+
sheet.with_property('background', prefix_match: true) # Finds ALL background-* properties
|
|
141
|
+
sheet.with_property('margin', '10px') # Filter by value too
|
|
142
|
+
|
|
137
143
|
# Chain filters together
|
|
138
144
|
sheet.with_media(:screen)
|
|
139
145
|
.with_specificity(50..200)
|
|
@@ -141,9 +147,9 @@ sheet.with_media(:screen)
|
|
|
141
147
|
.map(&:selector)
|
|
142
148
|
# => ["#header .nav", ".sidebar > ul li"]
|
|
143
149
|
|
|
144
|
-
# Find all rules with
|
|
145
|
-
sheet.
|
|
146
|
-
|
|
150
|
+
# Find all rules with any margin-related property
|
|
151
|
+
sheet.with_property('margin', prefix_match: true).each do |rule|
|
|
152
|
+
puts "#{rule.selector} uses margin"
|
|
147
153
|
end
|
|
148
154
|
|
|
149
155
|
# Find high-specificity selectors (potential refactoring targets)
|