cataract 0.4.0 → 0.5.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 +4 -4
- data/.yardopts +9 -0
- data/CHANGELOG.md +8 -0
- data/ext/cataract/cataract.c +124 -11
- data/ext/cataract/cataract.h +13 -2
- data/ext/cataract/css_parser.c +298 -36
- data/ext/cataract/flatten.c +6 -2
- data/ext/cataract/specificity.c +13 -2
- data/lib/cataract/at_rule.rb +4 -2
- data/lib/cataract/conditional_group.rb +72 -0
- data/lib/cataract/native.rb +1 -0
- data/lib/cataract/pure/flatten.rb +7 -1
- data/lib/cataract/pure/parser.rb +266 -24
- data/lib/cataract/pure/serializer.rb +95 -8
- data/lib/cataract/pure/specificity.rb +10 -2
- data/lib/cataract/pure.rb +1 -0
- data/lib/cataract/rule.rb +8 -4
- data/lib/cataract/stylesheet.rb +67 -20
- data/lib/cataract/version.rb +1 -1
- metadata +3 -1
|
@@ -12,13 +12,15 @@ module Cataract
|
|
|
12
12
|
# compact-vs-formatted knobs all live on ivars instead of being threaded
|
|
13
13
|
# through every helper as parameters.
|
|
14
14
|
class Serializer
|
|
15
|
-
def initialize(rules, charset, has_nesting, selector_lists, media_queries, media_query_lists, formatted
|
|
15
|
+
def initialize(rules, charset, has_nesting, selector_lists, media_queries, media_query_lists, formatted:,
|
|
16
|
+
conditional_groups: [])
|
|
16
17
|
@rules = rules
|
|
17
18
|
@charset = charset
|
|
18
19
|
@has_nesting = has_nesting
|
|
19
20
|
@selector_lists = selector_lists || {}
|
|
20
21
|
@media_queries = media_queries || []
|
|
21
22
|
@media_query_lists = media_query_lists || {}
|
|
23
|
+
@conditional_groups = conditional_groups || []
|
|
22
24
|
@formatted = formatted
|
|
23
25
|
@result = +''
|
|
24
26
|
|
|
@@ -123,6 +125,64 @@ module Cataract
|
|
|
123
125
|
end
|
|
124
126
|
end
|
|
125
127
|
|
|
128
|
+
# A rule's conditional-group wrapping chain, outermost first, found
|
|
129
|
+
# by walking parent_id from its own conditional_group_id up to the
|
|
130
|
+
# root. Only @supports currently populates conditional_group_id (see
|
|
131
|
+
# the parser), but this walks whatever's there generically.
|
|
132
|
+
#
|
|
133
|
+
# @param conditional_group_id [Integer, nil]
|
|
134
|
+
# @return [Array<ConditionalGroup>] empty if not in any conditional group
|
|
135
|
+
def conditional_group_chain(conditional_group_id)
|
|
136
|
+
chain = []
|
|
137
|
+
id = conditional_group_id
|
|
138
|
+
while id
|
|
139
|
+
group = @conditional_groups[id]
|
|
140
|
+
break unless group
|
|
141
|
+
|
|
142
|
+
chain.unshift(group)
|
|
143
|
+
id = group.parent_id
|
|
144
|
+
end
|
|
145
|
+
chain
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Reconcile the currently-open conditional-group blocks with the
|
|
149
|
+
# ones a rule needs, like diffing two paths down a tree: close
|
|
150
|
+
# whatever's open past where the chains diverge (innermost first),
|
|
151
|
+
# then open whatever the target chain adds from that point on.
|
|
152
|
+
# Nested entirely inside whatever media block is currently open
|
|
153
|
+
# (conditional groups don't affect media context).
|
|
154
|
+
#
|
|
155
|
+
# @param current_chain [Array<ConditionalGroup>] currently-open chain, outermost first
|
|
156
|
+
# @param target_chain [Array<ConditionalGroup>] the chain the next rule needs
|
|
157
|
+
# @param indent [String]
|
|
158
|
+
# @return [Array<ConditionalGroup>] target_chain, for the caller to track as current
|
|
159
|
+
def sync_conditional_group_chain!(current_chain, target_chain, indent)
|
|
160
|
+
common = 0
|
|
161
|
+
common += 1 while common < current_chain.size && common < target_chain.size &&
|
|
162
|
+
current_chain[common].id == target_chain[common].id
|
|
163
|
+
|
|
164
|
+
close_conditional_groups!(current_chain.size - common, indent)
|
|
165
|
+
|
|
166
|
+
target_chain[common..].each do |group|
|
|
167
|
+
prelude = conditional_group_prelude(group)
|
|
168
|
+
prelude_part = prelude.empty? ? '' : " #{prelude}"
|
|
169
|
+
@result << indent << "@#{group.type}#{prelude_part} {\n"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
target_chain
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Reconstruct a group's prelude text - "name condition", "name", or
|
|
176
|
+
# "condition" depending on which are present (@supports only ever
|
|
177
|
+
# has condition; @container may have either or both).
|
|
178
|
+
def conditional_group_prelude(group)
|
|
179
|
+
[group.name, group.condition].compact.join(' ')
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def close_conditional_groups!(count, indent)
|
|
183
|
+
count.times { @result << indent << "}\n" }
|
|
184
|
+
end
|
|
185
|
+
|
|
126
186
|
# Does the rule's media query differ from the currently open media
|
|
127
187
|
# block, requiring a new "@media ... {" to be opened? Comma-separated
|
|
128
188
|
# lists are compared by list id; single queries are compared by content
|
|
@@ -286,6 +346,7 @@ module Cataract
|
|
|
286
346
|
current_media_query_list_id = nil
|
|
287
347
|
current_media_query = nil
|
|
288
348
|
in_media_block = false
|
|
349
|
+
current_cg_chain = []
|
|
289
350
|
rule_index = 0
|
|
290
351
|
|
|
291
352
|
@rules.each do |rule|
|
|
@@ -295,9 +356,11 @@ module Cataract
|
|
|
295
356
|
rule_media_query = rule_media_query_id ? @media_queries[rule_media_query_id] : nil
|
|
296
357
|
rule_media_query_list_id = rule_media_query_id ? @mq_id_to_list_id[rule_media_query_id] : nil
|
|
297
358
|
is_first_rule = (rule_index == 0)
|
|
359
|
+
rule_cg_chain = conditional_group_chain(rule.conditional_group_id)
|
|
298
360
|
|
|
299
361
|
if rule_media_query.nil?
|
|
300
362
|
if in_media_block
|
|
363
|
+
current_cg_chain = sync_conditional_group_chain!(current_cg_chain, [], @media_indent)
|
|
301
364
|
@result << "}\n"
|
|
302
365
|
in_media_block = false
|
|
303
366
|
current_media_query = nil
|
|
@@ -306,19 +369,22 @@ module Cataract
|
|
|
306
369
|
|
|
307
370
|
@result << "\n" if @add_blank_lines && !is_first_rule
|
|
308
371
|
|
|
372
|
+
current_cg_chain = sync_conditional_group_chain!(current_cg_chain, rule_cg_chain, '')
|
|
373
|
+
indent = current_cg_chain.empty? ? '' : @media_indent
|
|
374
|
+
|
|
309
375
|
if grouping_enabled && rule.is_a?(Rule) && rule.selector_list_id
|
|
310
376
|
selectors = find_groupable_selectors(rule, rule_media_query_id)
|
|
311
377
|
|
|
312
|
-
@result << selectors.join(', ') << @opening_brace
|
|
378
|
+
@result << indent << selectors.join(', ') << @opening_brace
|
|
313
379
|
if @decl_indent_base
|
|
314
380
|
serialize_declarations_formatted(rule.declarations, @decl_indent_base)
|
|
315
381
|
else
|
|
316
382
|
serialize_declarations(rule.declarations)
|
|
317
383
|
end
|
|
318
|
-
@result << @closing_brace
|
|
384
|
+
@result << indent << @closing_brace
|
|
319
385
|
else
|
|
320
386
|
if @decl_indent_base
|
|
321
|
-
serialize_rule_formatted(rule,
|
|
387
|
+
serialize_rule_formatted(rule, indent, true)
|
|
322
388
|
else
|
|
323
389
|
serialize_rule(rule)
|
|
324
390
|
end
|
|
@@ -329,6 +395,7 @@ module Cataract
|
|
|
329
395
|
rule_media_query, rule_media_query_list_id)
|
|
330
396
|
|
|
331
397
|
if needs_new_block
|
|
398
|
+
current_cg_chain = sync_conditional_group_chain!(current_cg_chain, [], @media_indent)
|
|
332
399
|
@result << "}\n" if in_media_block
|
|
333
400
|
@result << "\n" if @add_blank_lines && !is_first_rule
|
|
334
401
|
|
|
@@ -340,6 +407,8 @@ module Cataract
|
|
|
340
407
|
in_media_block = true
|
|
341
408
|
end
|
|
342
409
|
|
|
410
|
+
current_cg_chain = sync_conditional_group_chain!(current_cg_chain, rule_cg_chain, @media_indent)
|
|
411
|
+
|
|
343
412
|
if grouping_enabled && rule.is_a?(Rule) && rule.selector_list_id
|
|
344
413
|
selectors = find_groupable_selectors(rule, rule_media_query_id)
|
|
345
414
|
|
|
@@ -363,6 +432,8 @@ module Cataract
|
|
|
363
432
|
rule_index += 1
|
|
364
433
|
end
|
|
365
434
|
|
|
435
|
+
close_conditional_groups!(current_cg_chain.size, in_media_block ? @media_indent : '')
|
|
436
|
+
|
|
366
437
|
@result << "}\n" if in_media_block
|
|
367
438
|
end
|
|
368
439
|
|
|
@@ -441,6 +512,11 @@ module Cataract
|
|
|
441
512
|
|
|
442
513
|
# An at-rule (@keyframes, @font-face, etc), compact
|
|
443
514
|
def serialize_at_rule(at_rule)
|
|
515
|
+
if at_rule.content.nil?
|
|
516
|
+
@result << "#{at_rule.selector};\n"
|
|
517
|
+
return
|
|
518
|
+
end
|
|
519
|
+
|
|
444
520
|
@result << "#{at_rule.selector} {\n"
|
|
445
521
|
|
|
446
522
|
if at_rule.content.length > 0
|
|
@@ -600,6 +676,13 @@ module Cataract
|
|
|
600
676
|
end
|
|
601
677
|
|
|
602
678
|
def serialize_at_rule_formatted(at_rule, indent)
|
|
679
|
+
if at_rule.content.nil?
|
|
680
|
+
@result << indent
|
|
681
|
+
@result << at_rule.selector
|
|
682
|
+
@result << ";\n"
|
|
683
|
+
return
|
|
684
|
+
end
|
|
685
|
+
|
|
603
686
|
@result << indent
|
|
604
687
|
@result << at_rule.selector
|
|
605
688
|
@result << " {\n"
|
|
@@ -637,9 +720,12 @@ module Cataract
|
|
|
637
720
|
# @param selector_lists [Hash] Selector list ID => array of rule IDs (for grouping)
|
|
638
721
|
# @param media_queries [Array<MediaQuery>] Array of MediaQuery objects
|
|
639
722
|
# @param media_query_lists [Hash] List ID => array of MediaQuery IDs (for comma-separated queries)
|
|
723
|
+
# @param conditional_groups [Array<ConditionalGroup>] Array of ConditionalGroup objects (@supports/@layer/@container/@scope)
|
|
640
724
|
# @return [String] Compact CSS string
|
|
641
|
-
def stylesheet_to_s(rules, charset, has_nesting, selector_lists = {}, media_queries = [], media_query_lists = {}
|
|
642
|
-
|
|
725
|
+
def stylesheet_to_s(rules, charset, has_nesting, selector_lists = {}, media_queries = [], media_query_lists = {},
|
|
726
|
+
conditional_groups = [])
|
|
727
|
+
Serializer.new(rules, charset, has_nesting, selector_lists, media_queries, media_query_lists,
|
|
728
|
+
formatted: false, conditional_groups: conditional_groups).to_s
|
|
643
729
|
end
|
|
644
730
|
|
|
645
731
|
# Serialize stylesheet to formatted CSS string (with indentation).
|
|
@@ -650,11 +736,12 @@ module Cataract
|
|
|
650
736
|
# @param selector_lists [Hash] Selector list ID => array of rule IDs (for grouping)
|
|
651
737
|
# @param media_queries [Array<MediaQuery>] Array of MediaQuery objects
|
|
652
738
|
# @param media_query_lists [Hash] List ID => array of MediaQuery IDs (for comma-separated queries)
|
|
739
|
+
# @param conditional_groups [Array<ConditionalGroup>] Array of ConditionalGroup objects (@supports/@layer/@container/@scope)
|
|
653
740
|
# @return [String] Formatted CSS string
|
|
654
741
|
def stylesheet_to_formatted_s(rules, charset, has_nesting, selector_lists = {}, media_queries = [],
|
|
655
|
-
media_query_lists = {})
|
|
742
|
+
media_query_lists = {}, conditional_groups = [])
|
|
656
743
|
Serializer.new(rules, charset, has_nesting, selector_lists, media_queries, media_query_lists,
|
|
657
|
-
formatted: true).to_s
|
|
744
|
+
formatted: true, conditional_groups: conditional_groups).to_s
|
|
658
745
|
end
|
|
659
746
|
end
|
|
660
747
|
end
|
|
@@ -78,8 +78,16 @@ module Cataract
|
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
elsif letter?(byte)
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
ident_end = skip_identifier(selector, i + 1, len)
|
|
82
|
+
if ident_end < len && selector.getbyte(ident_end) == BYTE_PIPE
|
|
83
|
+
# Namespace prefix (ns|E, css-namespaces-3 qname grammar) -
|
|
84
|
+
# contributes nothing itself; the local name after '|' is the
|
|
85
|
+
# real type selector and gets counted on the next iteration.
|
|
86
|
+
i = ident_end + 1
|
|
87
|
+
else
|
|
88
|
+
element_count += 1
|
|
89
|
+
i = ident_end
|
|
90
|
+
end
|
|
83
91
|
else
|
|
84
92
|
# Whitespace, combinators, and the universal selector (*) all have
|
|
85
93
|
# zero specificity - just skip a single byte.
|
data/lib/cataract/pure.rb
CHANGED
|
@@ -37,6 +37,7 @@ require_relative 'declaration'
|
|
|
37
37
|
require_relative 'rule'
|
|
38
38
|
require_relative 'at_rule'
|
|
39
39
|
require_relative 'media_query'
|
|
40
|
+
require_relative 'conditional_group'
|
|
40
41
|
require_relative 'import_statement'
|
|
41
42
|
require_relative 'stylesheet_scope'
|
|
42
43
|
require_relative 'stylesheet'
|
data/lib/cataract/rule.rb
CHANGED
|
@@ -26,6 +26,7 @@ module Cataract
|
|
|
26
26
|
# @attr [Integer, nil] nesting_style 0=implicit, 1=explicit, nil=not nested
|
|
27
27
|
# @attr [Integer, nil] selector_list_id ID linking rules from same selector list (e.g., "h1, h2")
|
|
28
28
|
# @attr [Integer, nil] media_query_id ID of the MediaQuery this rule belongs to (nil if not in media query)
|
|
29
|
+
# @attr [Integer, nil] conditional_group_id ID of the ConditionalGroup (@supports/@layer/@container/@scope) this rule belongs to (nil if not in one)
|
|
29
30
|
Rule = Struct.new(
|
|
30
31
|
:id,
|
|
31
32
|
:selector,
|
|
@@ -34,7 +35,8 @@ module Cataract
|
|
|
34
35
|
:parent_rule_id,
|
|
35
36
|
:nesting_style,
|
|
36
37
|
:selector_list_id,
|
|
37
|
-
:media_query_id
|
|
38
|
+
:media_query_id,
|
|
39
|
+
:conditional_group_id
|
|
38
40
|
)
|
|
39
41
|
|
|
40
42
|
class Rule
|
|
@@ -49,6 +51,7 @@ module Cataract
|
|
|
49
51
|
# @param nesting_style [Integer, nil] Nesting style (0=implicit, 1=explicit, nil=not nested)
|
|
50
52
|
# @param selector_list_id [Integer, nil] Selector list ID for grouping
|
|
51
53
|
# @param media_query_id [Integer, nil] MediaQuery ID for rules in media queries
|
|
54
|
+
# @param conditional_group_id [Integer, nil] ConditionalGroup ID for rules in @supports/@layer/@container/@scope
|
|
52
55
|
# @return [Rule] New rule instance
|
|
53
56
|
#
|
|
54
57
|
# @example Create a rule with keyword arguments
|
|
@@ -60,10 +63,11 @@ module Cataract
|
|
|
60
63
|
# parent_rule_id: nil,
|
|
61
64
|
# nesting_style: nil,
|
|
62
65
|
# selector_list_id: nil,
|
|
63
|
-
# media_query_id: nil
|
|
66
|
+
# media_query_id: nil,
|
|
67
|
+
# conditional_group_id: nil
|
|
64
68
|
# )
|
|
65
|
-
def self.make(id:, selector:, declarations:, specificity: nil, parent_rule_id: nil, nesting_style: nil, selector_list_id: nil, media_query_id: nil)
|
|
66
|
-
new(id, selector, declarations, specificity, parent_rule_id, nesting_style, selector_list_id, media_query_id)
|
|
69
|
+
def self.make(id:, selector:, declarations:, specificity: nil, parent_rule_id: nil, nesting_style: nil, selector_list_id: nil, media_query_id: nil, conditional_group_id: nil)
|
|
70
|
+
new(id, selector, declarations, specificity, parent_rule_id, nesting_style, selector_list_id, media_query_id, conditional_group_id)
|
|
67
71
|
end
|
|
68
72
|
|
|
69
73
|
# Silence warning about method redefinition. We redefine below to lazily calculate
|
data/lib/cataract/stylesheet.rb
CHANGED
|
@@ -29,6 +29,9 @@ module Cataract
|
|
|
29
29
|
# @return [Array<MediaQuery>] Array of media query objects
|
|
30
30
|
attr_reader :media_queries
|
|
31
31
|
|
|
32
|
+
# @return [Array<ConditionalGroup>] Array of conditional-group objects (@supports/@layer/@container/@scope)
|
|
33
|
+
attr_reader :conditional_groups
|
|
34
|
+
|
|
32
35
|
# @return [Hash<Symbol, Array<Integer>>] Cached index mapping media query text to rule IDs
|
|
33
36
|
# Lazily build and return media_index.
|
|
34
37
|
# Only builds the index when first accessed, not eagerly during parse.
|
|
@@ -160,6 +163,8 @@ module Cataract
|
|
|
160
163
|
@_next_selector_list_id = 0 # Counter for selector list IDs
|
|
161
164
|
@_media_query_lists = {} # Hash: list_id => Array of MediaQuery IDs (for "screen, print" grouping)
|
|
162
165
|
@_next_media_query_list_id = 0 # Counter for media query list IDs
|
|
166
|
+
@conditional_groups = [] # Array of ConditionalGroup objects (@supports/@layer/@container/@scope)
|
|
167
|
+
@_next_conditional_group_id = 0 # Counter for ConditionalGroup IDs
|
|
163
168
|
@charset = nil
|
|
164
169
|
@imports = [] # Array of ImportStatement objects
|
|
165
170
|
@_has_nesting = nil # Set by parser (nil or boolean)
|
|
@@ -187,6 +192,8 @@ module Cataract
|
|
|
187
192
|
@_next_selector_list_id = source.next_selector_list_id
|
|
188
193
|
@_media_query_lists = source.media_query_lists.transform_values(&:dup)
|
|
189
194
|
@_next_media_query_list_id = source.next_media_query_list_id
|
|
195
|
+
@conditional_groups = source.conditional_groups.dup
|
|
196
|
+
@_next_conditional_group_id = source.next_conditional_group_id
|
|
190
197
|
@parser_options = source.parser_options.dup
|
|
191
198
|
clear_memoized_caches
|
|
192
199
|
@_hash = nil # Clear cached hash
|
|
@@ -474,7 +481,8 @@ module Cataract
|
|
|
474
481
|
# @example Filter to multiple media types
|
|
475
482
|
# sheet.to_s(media: [:screen, :print]) # => "@media screen { ... } @media print { ... }"
|
|
476
483
|
def to_s(media: :all)
|
|
477
|
-
@backend.stylesheet_to_s(filter_rules_by_media(media), @charset, @_has_nesting || false, @_selector_lists, @media_queries, @_media_query_lists
|
|
484
|
+
@backend.stylesheet_to_s(filter_rules_by_media(media), @charset, @_has_nesting || false, @_selector_lists, @media_queries, @_media_query_lists,
|
|
485
|
+
@conditional_groups)
|
|
478
486
|
end
|
|
479
487
|
alias to_css to_s
|
|
480
488
|
|
|
@@ -500,7 +508,8 @@ module Cataract
|
|
|
500
508
|
#
|
|
501
509
|
# @see #to_s For compact single-line output
|
|
502
510
|
def to_formatted_s(media: :all)
|
|
503
|
-
@backend.stylesheet_to_formatted_s(filter_rules_by_media(media), @charset, @_has_nesting || false, @_selector_lists, @media_queries, @_media_query_lists
|
|
511
|
+
@backend.stylesheet_to_formatted_s(filter_rules_by_media(media), @charset, @_has_nesting || false, @_selector_lists, @media_queries, @_media_query_lists,
|
|
512
|
+
@conditional_groups)
|
|
504
513
|
end
|
|
505
514
|
|
|
506
515
|
# Get number of rules
|
|
@@ -769,8 +778,9 @@ module Cataract
|
|
|
769
778
|
# Compare stylesheets for equality.
|
|
770
779
|
#
|
|
771
780
|
# Two stylesheets are equal if they have the same rules in the same order
|
|
772
|
-
# and the same media queries. Rule equality uses
|
|
773
|
-
# Order matters because CSS cascade depends
|
|
781
|
+
# and the same media queries and conditional groups. Rule equality uses
|
|
782
|
+
# shorthand-aware comparison. Order matters because CSS cascade depends
|
|
783
|
+
# on rule order.
|
|
774
784
|
#
|
|
775
785
|
# Charset is ignored since it's file encoding metadata, not semantic content.
|
|
776
786
|
#
|
|
@@ -780,6 +790,7 @@ module Cataract
|
|
|
780
790
|
return false unless other.is_a?(Stylesheet)
|
|
781
791
|
return false unless rules == other.rules
|
|
782
792
|
return false unless @media_queries == other.media_queries
|
|
793
|
+
return false unless @conditional_groups == other.conditional_groups
|
|
783
794
|
|
|
784
795
|
true
|
|
785
796
|
end
|
|
@@ -787,11 +798,12 @@ module Cataract
|
|
|
787
798
|
|
|
788
799
|
# Generate hash code for this stylesheet.
|
|
789
800
|
#
|
|
790
|
-
# Hash is based on rules and
|
|
801
|
+
# Hash is based on rules, media_queries, and conditional_groups to match
|
|
802
|
+
# equality semantics.
|
|
791
803
|
#
|
|
792
804
|
# @return [Integer] hash code
|
|
793
805
|
def hash
|
|
794
|
-
@_hash ||= [self.class, rules, @media_queries].hash # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
806
|
+
@_hash ||= [self.class, rules, @media_queries, @conditional_groups].hash # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
795
807
|
end
|
|
796
808
|
|
|
797
809
|
# Flatten all rules in this stylesheet according to CSS cascade rules.
|
|
@@ -852,11 +864,13 @@ module Cataract
|
|
|
852
864
|
list_id_offset = merge_selector_lists!(other.selector_lists, rule_id_offset: offset)
|
|
853
865
|
mq_id_offset = merge_media_queries!(other.media_queries)
|
|
854
866
|
merge_media_query_lists!(other.media_query_lists, mq_id_offset: mq_id_offset)
|
|
867
|
+
cg_id_offset = merge_conditional_groups!(other.conditional_groups)
|
|
855
868
|
|
|
856
869
|
# Add rules with updated IDs and cross-references
|
|
857
870
|
other.rules.each do |rule|
|
|
858
871
|
new_rule = rule.dup
|
|
859
|
-
rebase_rule!(new_rule, rule_id_offset: offset, list_id_offset: list_id_offset, mq_id_offset: mq_id_offset
|
|
872
|
+
rebase_rule!(new_rule, rule_id_offset: offset, list_id_offset: list_id_offset, mq_id_offset: mq_id_offset,
|
|
873
|
+
cg_id_offset: cg_id_offset)
|
|
860
874
|
@rules << new_rule
|
|
861
875
|
end
|
|
862
876
|
|
|
@@ -946,6 +960,10 @@ module Cataract
|
|
|
946
960
|
@_next_media_query_id
|
|
947
961
|
end
|
|
948
962
|
|
|
963
|
+
def next_conditional_group_id
|
|
964
|
+
@_next_conditional_group_id
|
|
965
|
+
end
|
|
966
|
+
|
|
949
967
|
def next_selector_list_id
|
|
950
968
|
@_next_selector_list_id
|
|
951
969
|
end
|
|
@@ -998,6 +1016,28 @@ module Cataract
|
|
|
998
1016
|
mq_id_offset
|
|
999
1017
|
end
|
|
1000
1018
|
|
|
1019
|
+
# Offset and append ConditionalGroup objects into @conditional_groups,
|
|
1020
|
+
# without mutating the source objects. Unlike merge_media_queries!,
|
|
1021
|
+
# each group's own parent_id (pointing at another ConditionalGroup, for
|
|
1022
|
+
# nesting) must also be rebased by the same offset.
|
|
1023
|
+
#
|
|
1024
|
+
# @param source_groups [Array<ConditionalGroup>, nil]
|
|
1025
|
+
# @return [Integer] id offset applied to each merged ConditionalGroup
|
|
1026
|
+
def merge_conditional_groups!(source_groups)
|
|
1027
|
+
cg_id_offset = @_next_conditional_group_id
|
|
1028
|
+
return cg_id_offset if source_groups.nil? || source_groups.empty?
|
|
1029
|
+
|
|
1030
|
+
source_groups.each do |group|
|
|
1031
|
+
new_group = group.dup
|
|
1032
|
+
new_group.id += cg_id_offset
|
|
1033
|
+
new_group.parent_id += cg_id_offset if new_group.parent_id
|
|
1034
|
+
@conditional_groups << new_group
|
|
1035
|
+
end
|
|
1036
|
+
@_next_conditional_group_id += source_groups.size
|
|
1037
|
+
|
|
1038
|
+
cg_id_offset
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1001
1041
|
# Offset and merge a selector_lists hash (list_id => [rule_ids]) into
|
|
1002
1042
|
# @_selector_lists. List ids are always rebased onto the next available
|
|
1003
1043
|
# id; rule ids are rebased by rule_id_offset (0 when the caller defers
|
|
@@ -1056,18 +1096,21 @@ module Cataract
|
|
|
1056
1096
|
end
|
|
1057
1097
|
|
|
1058
1098
|
# Rebase a rule/at-rule's own id and its cross-references (Rule's
|
|
1059
|
-
# selector_list_id, and media_query_id on both Rule
|
|
1060
|
-
# given offsets, mutating it in place. Callers
|
|
1061
|
-
# Stylesheet they don't own (e.g. concat) must dup
|
|
1099
|
+
# selector_list_id, and media_query_id/conditional_group_id on both Rule
|
|
1100
|
+
# and AtRule) by the given offsets, mutating it in place. Callers
|
|
1101
|
+
# merging from a live Stylesheet they don't own (e.g. concat) must dup
|
|
1102
|
+
# the rule first.
|
|
1062
1103
|
#
|
|
1063
1104
|
# @param rule [Rule, AtRule]
|
|
1064
1105
|
# @param rule_id_offset [Integer]
|
|
1065
1106
|
# @param list_id_offset [Integer]
|
|
1066
1107
|
# @param mq_id_offset [Integer]
|
|
1108
|
+
# @param cg_id_offset [Integer]
|
|
1067
1109
|
# @return [void]
|
|
1068
|
-
def rebase_rule!(rule, rule_id_offset:, list_id_offset: 0, mq_id_offset: 0)
|
|
1110
|
+
def rebase_rule!(rule, rule_id_offset:, list_id_offset: 0, mq_id_offset: 0, cg_id_offset: 0)
|
|
1069
1111
|
rule.id += rule_id_offset
|
|
1070
1112
|
rule.media_query_id += mq_id_offset if rule.media_query_id
|
|
1113
|
+
rule.conditional_group_id += cg_id_offset if rule.conditional_group_id
|
|
1071
1114
|
return unless rule.is_a?(Rule)
|
|
1072
1115
|
|
|
1073
1116
|
rule.selector_list_id += list_id_offset if rule.selector_list_id
|
|
@@ -1099,10 +1142,12 @@ module Cataract
|
|
|
1099
1142
|
list_id_offset = merge_selector_lists!(result[:_selector_lists], rule_id_offset: offset)
|
|
1100
1143
|
mq_id_offset = merge_media_queries!(result[:media_queries])
|
|
1101
1144
|
merge_media_query_lists!(result[:_media_query_lists], mq_id_offset: mq_id_offset)
|
|
1145
|
+
cg_id_offset = merge_conditional_groups!(result[:conditional_groups])
|
|
1102
1146
|
|
|
1103
1147
|
new_rules = result[:rules]
|
|
1104
1148
|
new_rules.each do |rule|
|
|
1105
|
-
rebase_rule!(rule, rule_id_offset: offset, list_id_offset: list_id_offset, mq_id_offset: mq_id_offset
|
|
1149
|
+
rebase_rule!(rule, rule_id_offset: offset, list_id_offset: list_id_offset, mq_id_offset: mq_id_offset,
|
|
1150
|
+
cg_id_offset: cg_id_offset)
|
|
1106
1151
|
@rules << rule
|
|
1107
1152
|
end
|
|
1108
1153
|
@_last_rule_id = offset + new_rules.length
|
|
@@ -1313,7 +1358,8 @@ module Cataract
|
|
|
1313
1358
|
# @return [void]
|
|
1314
1359
|
def merge_imported_sheet!(imported_sheet, import)
|
|
1315
1360
|
mq_id_offset = merge_media_queries!(imported_sheet.media_queries)
|
|
1316
|
-
|
|
1361
|
+
cg_id_offset = merge_conditional_groups!(imported_sheet.conditional_groups)
|
|
1362
|
+
rebase_imported_rules!(imported_sheet.rules, mq_id_offset, cg_id_offset, import.media_query_id)
|
|
1317
1363
|
|
|
1318
1364
|
insert_position = import.id
|
|
1319
1365
|
imported_sheet.rules.each_with_index do |rule, idx|
|
|
@@ -1324,15 +1370,16 @@ module Cataract
|
|
|
1324
1370
|
merge_media_query_lists!(imported_sheet.media_query_lists, mq_id_offset: mq_id_offset)
|
|
1325
1371
|
end
|
|
1326
1372
|
|
|
1327
|
-
# Rebase every imported rule/at-rule's media_query_id
|
|
1328
|
-
# stylesheet's own @media_queries
|
|
1329
|
-
#
|
|
1330
|
-
#
|
|
1331
|
-
# rule's media
|
|
1373
|
+
# Rebase every imported rule/at-rule's media_query_id and
|
|
1374
|
+
# conditional_group_id onto this stylesheet's own @media_queries/
|
|
1375
|
+
# @conditional_groups (now that the imported sheet's own copies have
|
|
1376
|
+
# been merged in), then, if the @import statement itself had a media
|
|
1377
|
+
# qualifier, combine it with (or assign it to) each rule's media
|
|
1378
|
+
# context.
|
|
1332
1379
|
#
|
|
1333
1380
|
# @return [void]
|
|
1334
|
-
def
|
|
1335
|
-
imported_rules.each { |rule| rebase_rule!(rule, rule_id_offset: 0, mq_id_offset: mq_id_offset) }
|
|
1381
|
+
def rebase_imported_rules!(imported_rules, mq_id_offset, cg_id_offset, import_media_query_id)
|
|
1382
|
+
imported_rules.each { |rule| rebase_rule!(rule, rule_id_offset: 0, mq_id_offset: mq_id_offset, cg_id_offset: cg_id_offset) }
|
|
1336
1383
|
|
|
1337
1384
|
return unless import_media_query_id
|
|
1338
1385
|
|
data/lib/cataract/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cataract
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Cook
|
|
@@ -29,6 +29,7 @@ files:
|
|
|
29
29
|
- ".overcommit.yml"
|
|
30
30
|
- ".rubocop.yml"
|
|
31
31
|
- ".rubocop_todo.yml"
|
|
32
|
+
- ".yardopts"
|
|
32
33
|
- BENCHMARKS.md
|
|
33
34
|
- CHANGELOG.md
|
|
34
35
|
- Gemfile
|
|
@@ -69,6 +70,7 @@ files:
|
|
|
69
70
|
- lib/cataract.rb
|
|
70
71
|
- lib/cataract/at_rule.rb
|
|
71
72
|
- lib/cataract/color_conversion.rb
|
|
73
|
+
- lib/cataract/conditional_group.rb
|
|
72
74
|
- lib/cataract/constants.rb
|
|
73
75
|
- lib/cataract/declaration.rb
|
|
74
76
|
- lib/cataract/declarations.rb
|