cataract 0.1.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.
- checksums.yaml +7 -0
- data/.clang-tidy +30 -0
- data/.github/workflows/ci-macos.yml +12 -0
- data/.github/workflows/ci.yml +77 -0
- data/.github/workflows/test.yml +76 -0
- data/.gitignore +45 -0
- data/.overcommit.yml +38 -0
- data/.rubocop.yml +83 -0
- data/BENCHMARKS.md +201 -0
- data/CHANGELOG.md +1 -0
- data/Gemfile +27 -0
- data/LICENSE +21 -0
- data/RAGEL_MIGRATION.md +60 -0
- data/README.md +292 -0
- data/Rakefile +209 -0
- data/benchmarks/benchmark_harness.rb +193 -0
- data/benchmarks/benchmark_merging.rb +121 -0
- data/benchmarks/benchmark_optimization_comparison.rb +168 -0
- data/benchmarks/benchmark_parsing.rb +153 -0
- data/benchmarks/benchmark_ragel_removal.rb +56 -0
- data/benchmarks/benchmark_runner.rb +70 -0
- data/benchmarks/benchmark_serialization.rb +180 -0
- data/benchmarks/benchmark_shorthand.rb +109 -0
- data/benchmarks/benchmark_shorthand_expansion.rb +176 -0
- data/benchmarks/benchmark_specificity.rb +124 -0
- data/benchmarks/benchmark_string_allocation.rb +151 -0
- data/benchmarks/benchmark_stylesheet_to_s.rb +62 -0
- data/benchmarks/benchmark_to_s_cached.rb +55 -0
- data/benchmarks/benchmark_value_splitter.rb +54 -0
- data/benchmarks/benchmark_yjit.rb +158 -0
- data/benchmarks/benchmark_yjit_workers.rb +61 -0
- data/benchmarks/profile_to_s.rb +23 -0
- data/benchmarks/speedup_calculator.rb +83 -0
- data/benchmarks/system_metadata.rb +81 -0
- data/benchmarks/templates/benchmarks.md.erb +221 -0
- data/benchmarks/yjit_tests.rb +141 -0
- data/cataract.gemspec +34 -0
- data/cliff.toml +92 -0
- data/examples/color_conversion_visual_test/color_conversion_test.html +3603 -0
- data/examples/color_conversion_visual_test/generate.rb +202 -0
- data/examples/color_conversion_visual_test/template.html.erb +259 -0
- data/examples/css_analyzer/analyzer.rb +164 -0
- data/examples/css_analyzer/analyzers/base.rb +33 -0
- data/examples/css_analyzer/analyzers/colors.rb +133 -0
- data/examples/css_analyzer/analyzers/important.rb +88 -0
- data/examples/css_analyzer/analyzers/properties.rb +61 -0
- data/examples/css_analyzer/analyzers/specificity.rb +68 -0
- data/examples/css_analyzer/templates/report.html.erb +575 -0
- data/examples/css_analyzer.rb +69 -0
- data/examples/github_analysis.html +5343 -0
- data/ext/cataract/cataract.c +1086 -0
- data/ext/cataract/cataract.h +174 -0
- data/ext/cataract/css_parser.c +1435 -0
- data/ext/cataract/extconf.rb +48 -0
- data/ext/cataract/import_scanner.c +174 -0
- data/ext/cataract/merge.c +973 -0
- data/ext/cataract/shorthand_expander.c +902 -0
- data/ext/cataract/specificity.c +213 -0
- data/ext/cataract/value_splitter.c +116 -0
- data/ext/cataract_color/cataract_color.c +16 -0
- data/ext/cataract_color/color_conversion.c +1687 -0
- data/ext/cataract_color/color_conversion.h +136 -0
- data/ext/cataract_color/color_conversion_lab.c +571 -0
- data/ext/cataract_color/color_conversion_named.c +259 -0
- data/ext/cataract_color/color_conversion_oklab.c +547 -0
- data/ext/cataract_color/extconf.rb +23 -0
- data/ext/cataract_old/cataract.c +393 -0
- data/ext/cataract_old/cataract.h +250 -0
- data/ext/cataract_old/css_parser.c +933 -0
- data/ext/cataract_old/extconf.rb +67 -0
- data/ext/cataract_old/import_scanner.c +174 -0
- data/ext/cataract_old/merge.c +776 -0
- data/ext/cataract_old/shorthand_expander.c +902 -0
- data/ext/cataract_old/specificity.c +213 -0
- data/ext/cataract_old/stylesheet.c +290 -0
- data/ext/cataract_old/value_splitter.c +116 -0
- data/lib/cataract/at_rule.rb +97 -0
- data/lib/cataract/color_conversion.rb +18 -0
- data/lib/cataract/declarations.rb +332 -0
- data/lib/cataract/import_resolver.rb +210 -0
- data/lib/cataract/rule.rb +131 -0
- data/lib/cataract/stylesheet.rb +716 -0
- data/lib/cataract/stylesheet_scope.rb +257 -0
- data/lib/cataract/version.rb +5 -0
- data/lib/cataract.rb +107 -0
- data/lib/tasks/gem.rake +158 -0
- data/scripts/fuzzer/run.rb +828 -0
- data/scripts/fuzzer/worker.rb +99 -0
- data/scripts/generate_benchmarks_md.rb +155 -0
- metadata +135 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cataract
|
|
4
|
+
# Represents a CSS rule with a selector and declarations.
|
|
5
|
+
#
|
|
6
|
+
# Rule is a C struct defined as: `Struct.new(:id, :selector, :declarations, :specificity)`
|
|
7
|
+
#
|
|
8
|
+
# Rules are created by the parser and stored in Stylesheet objects. Each rule
|
|
9
|
+
# contains:
|
|
10
|
+
# - An ID (position in the stylesheet)
|
|
11
|
+
# - A CSS selector string
|
|
12
|
+
# - An array of Declaration structs
|
|
13
|
+
# - A specificity value (calculated lazily)
|
|
14
|
+
#
|
|
15
|
+
# Media query information is stored separately in Stylesheet's media_index.
|
|
16
|
+
#
|
|
17
|
+
# @example Access rule properties
|
|
18
|
+
# sheet = Cataract.parse_css("body { color: red; font-size: 14px; }")
|
|
19
|
+
# rule = sheet.rules.first
|
|
20
|
+
# rule.selector #=> "body"
|
|
21
|
+
# rule.specificity #=> 1
|
|
22
|
+
# rule.declarations.length #=> 2
|
|
23
|
+
#
|
|
24
|
+
# @attr [Integer] id The rule's position in the stylesheet (0-indexed)
|
|
25
|
+
# @attr [String] selector The CSS selector (e.g., "body", ".class", "#id")
|
|
26
|
+
# @attr [Array<Declaration>] declarations Array of CSS property declarations
|
|
27
|
+
# @attr [Integer, nil] specificity CSS specificity value (calculated lazily)
|
|
28
|
+
class Rule
|
|
29
|
+
# Silence warning about method redefinition. We redefine below to lazily calculate
|
|
30
|
+
# specificity
|
|
31
|
+
undef_method :specificity if method_defined?(:specificity)
|
|
32
|
+
|
|
33
|
+
# Get the CSS specificity value for this rule's selector.
|
|
34
|
+
#
|
|
35
|
+
# Specificity is calculated lazily on first access and then cached.
|
|
36
|
+
# The calculation follows the CSS specification:
|
|
37
|
+
# - Inline styles: not applicable to parsed stylesheets
|
|
38
|
+
# - IDs: count of #id selectors
|
|
39
|
+
# - Classes/attributes/pseudo-classes: count of .class, [attr], :pseudo
|
|
40
|
+
# - Elements/pseudo-elements: count of element, ::pseudo
|
|
41
|
+
#
|
|
42
|
+
# @return [Integer] CSS specificity value
|
|
43
|
+
#
|
|
44
|
+
# @example Get specificity
|
|
45
|
+
# rule = Cataract.parse_css("#header .nav a").rules.first
|
|
46
|
+
# rule.specificity #=> 111 (1 ID + 1 class + 1 element)
|
|
47
|
+
def specificity
|
|
48
|
+
return self[:specificity] unless self[:specificity].nil?
|
|
49
|
+
|
|
50
|
+
# Calculate and cache
|
|
51
|
+
calculated = Cataract.calculate_specificity(selector)
|
|
52
|
+
self[:specificity] = calculated
|
|
53
|
+
calculated
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Check if this is a selector-based rule (vs an at-rule like @keyframes).
|
|
57
|
+
#
|
|
58
|
+
# @return [Boolean] Always returns true for Rule objects
|
|
59
|
+
def selector?
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Check if this is an at-rule.
|
|
64
|
+
#
|
|
65
|
+
# @return [Boolean] Always returns false for Rule objects
|
|
66
|
+
def at_rule?
|
|
67
|
+
false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Check if this is a specific at-rule type.
|
|
71
|
+
#
|
|
72
|
+
# @param _type [Symbol] At-rule type (e.g., :keyframes, :font_face)
|
|
73
|
+
# @return [Boolean] Always returns false for Rule objects
|
|
74
|
+
def at_rule_type?(_type)
|
|
75
|
+
false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Check if this rule has a declaration with the specified property and optional value.
|
|
79
|
+
#
|
|
80
|
+
# @param property [String] CSS property name to match
|
|
81
|
+
# @param value [String, nil] Optional value to match
|
|
82
|
+
# @return [Boolean] true if rule has matching declaration
|
|
83
|
+
#
|
|
84
|
+
# @example Check for color property
|
|
85
|
+
# rule.has_property?('color') #=> true
|
|
86
|
+
#
|
|
87
|
+
# @example Check for specific property value
|
|
88
|
+
# rule.has_property?('color', 'red') #=> true
|
|
89
|
+
def has_property?(property, value = nil)
|
|
90
|
+
declarations.any? do |decl|
|
|
91
|
+
property_matches = decl.property == property
|
|
92
|
+
value_matches = value.nil? || decl.value == value
|
|
93
|
+
property_matches && value_matches
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Check if this rule has any !important declarations, optionally for a specific property.
|
|
98
|
+
#
|
|
99
|
+
# @param property [String, nil] Optional property name to match
|
|
100
|
+
# @return [Boolean] true if rule has matching !important declaration
|
|
101
|
+
#
|
|
102
|
+
# @example Check for any !important
|
|
103
|
+
# rule.has_important? #=> true
|
|
104
|
+
#
|
|
105
|
+
# @example Check for color !important
|
|
106
|
+
# rule.has_important?('color') #=> true
|
|
107
|
+
def has_important?(property = nil)
|
|
108
|
+
if property
|
|
109
|
+
declarations.any? { |d| d.property == property && d.important }
|
|
110
|
+
else
|
|
111
|
+
declarations.any?(&:important)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Compare rules by their attributes rather than object identity.
|
|
116
|
+
#
|
|
117
|
+
# Two rules are equal if they have the same id, selector, declarations, and specificity.
|
|
118
|
+
#
|
|
119
|
+
# @param other [Object] Object to compare with
|
|
120
|
+
# @return [Boolean] true if rules have same attributes
|
|
121
|
+
def ==(other)
|
|
122
|
+
return false unless other.is_a?(Rule)
|
|
123
|
+
|
|
124
|
+
id == other.id &&
|
|
125
|
+
selector == other.selector &&
|
|
126
|
+
declarations == other.declarations &&
|
|
127
|
+
specificity == other.specificity
|
|
128
|
+
end
|
|
129
|
+
alias eql? ==
|
|
130
|
+
end
|
|
131
|
+
end
|