Class: Cataract::Backends::PureImpl Private

Inherits:
Object
  • Object
show all
Defined in:
lib/cataract/pure.rb,
lib/cataract/pure/parser.rb,
lib/cataract/pure/flatten.rb,
lib/cataract/pure/serializer.rb,
lib/cataract/pure/specificity.rb,
lib/cataract/pure/declarations.rb,
lib/cataract/pure/byte_constants.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Defined Under Namespace

Classes: FlattenImpl, Parser, Serializer

Constant Summary collapse

PURE_RUBY_LOADED =

Flag to indicate the pure Ruby backend is loaded

true
IMPLEMENTATION =

Implementation type constant

:ruby
COMPILE_FLAGS =

Compile flags (mimic C version)

{
  debug: false,
  str_buf_optimization: false,
  pure_ruby: true
}.freeze
Flatten =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

A frozen singleton, same rationale as Pure itself (see pure.rb) - none of FlattenImpl's methods touch instance state, so one shared instance is as safe as the bare module this replaces.

FlattenImpl.new.freeze
PSEUDO_ELEMENT_KEYWORDS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Legacy CSS2 pseudo-elements written with a single colon (e.g. :before) that must still be counted as pseudo-elements, not pseudo-classes.

%w[before after first-line first-letter selection].freeze
BYTE_SPACE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Whitespace bytes

32
BYTE_TAB =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

' '

9
BYTE_NEWLINE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'\t'

10
BYTE_CR =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'\n'

13
BYTE_AT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

CSS structural characters

64
BYTE_LBRACE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'@'

123
BYTE_RBRACE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'{'

125
BYTE_LPAREN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'}'

40
BYTE_RPAREN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'('

41
BYTE_LBRACKET =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

')'

91
BYTE_RBRACKET =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'['

93
BYTE_SEMICOLON =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

']'

59
BYTE_COLON =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

';'

58
BYTE_COMMA =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

':'

44
BYTE_SLASH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Comment characters

47
BYTE_STAR =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'/'

42
BYTE_SQUOTE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Quote characters

39
BYTE_DQUOTE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"'"

34
BYTE_HASH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Selector characters

35
BYTE_DOT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'#'

46
BYTE_GT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'.'

62
BYTE_PLUS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'>'

43
BYTE_TILDE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'+'

126
BYTE_ASTERISK =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'~'

42
BYTE_AMPERSAND =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'*'

38
BYTE_HYPHEN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Other

45
BYTE_UNDERSCORE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'-'

95
BYTE_BACKSLASH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'_'

92
BYTE_BANG =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'\'

33
BYTE_PERCENT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'!'

37
BYTE_EQUALS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'%'

61
BYTE_CARET =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'='

94
BYTE_DOLLAR =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'^'

36
BYTE_PIPE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'$'

124
BYTE_LOWER_U =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Specific lowercase letters (for keyword matching)

117
BYTE_LOWER_R =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'u'

114
BYTE_LOWER_L =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'r'

108
BYTE_LOWER_D =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'l'

100
BYTE_LOWER_T =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'd'

116
BYTE_LOWER_N =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

't'

110
BYTE_UPPER_U =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Specific uppercase letters (for case-insensitive matching)

85
BYTE_UPPER_R =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'U'

82
BYTE_UPPER_L =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'R'

76
BYTE_UPPER_D =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'L'

68
BYTE_UPPER_T =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'D'

84
BYTE_UPPER_N =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'T'

78
BYTE_LOWER_A =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Letter ranges (a-z, A-Z)

97
BYTE_LOWER_Z =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'a'

122
BYTE_UPPER_A =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'z'

65
BYTE_UPPER_Z =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'A'

90
BYTE_CASE_DIFF =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'Z'

32
BYTE_DIGIT_0 =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Digit range (0-9)

48
BYTE_DIGIT_9 =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'0'

57
NESTING_STYLE_IMPLICIT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Nesting styles (for CSS nesting support)

0
NESTING_STYLE_EXPLICIT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Implicit nesting: .parent { .child { ... } } => .parent .child

1

Instance Method Summary collapse

Instance Method Details

#calculate_specificity(selector) ⇒ Integer

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Calculate CSS specificity for a selector

Specificity calculation (per CSS spec):

  • Count IDs (#id) - each worth 100
  • Count classes/attributes/pseudo-classes (.class, [attr], :pseudo) - each worth 10
  • Count elements/pseudo-elements (div, ::before) - each worth 1

Parameters:

  • selector (String)

    CSS selector

Returns:

  • (Integer)

    Specificity value



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/cataract/pure/specificity.rb', line 39

def calculate_specificity(selector)
  return 0 if selector.nil? || selector.empty?

  # Counters for specificity components
  id_count = 0
  class_count = 0
  attr_count = 0
  pseudo_class_count = 0
  pseudo_element_count = 0
  element_count = 0

  i = 0
  len = selector.length

  while i < len
    byte = selector.getbyte(i)

    if byte == BYTE_HASH
      id_count += 1
      i = skip_identifier(selector, i + 1, len)
    elsif byte == BYTE_DOT
      class_count += 1
      i = skip_identifier(selector, i + 1, len)
    elsif byte == BYTE_LBRACKET
      attr_count += 1
      i = skip_attribute_selector(selector, i, len)
    elsif byte == BYTE_COLON
      i, is_not, not_content, counts_as_element = parse_pseudo(selector, i, len)
      if not_content
        # :not() doesn't count itself, but its content does
        not_specificity = calculate_specificity(not_content)
        id_count += not_specificity / 100
        class_count += (not_specificity % 100) / 10
        element_count += not_specificity % 10
      elsif !is_not
        if counts_as_element
          pseudo_element_count += 1
        else
          pseudo_class_count += 1
        end
      end
    elsif letter?(byte)
      element_count += 1
      i = skip_identifier(selector, i + 1, len)
    else
      # Whitespace, combinators, and the universal selector (*) all have
      # zero specificity - just skip a single byte.
      i += 1
    end
  end

  # Calculate specificity using W3C formula
  (id_count * 100) +
    ((class_count + attr_count + pseudo_class_count) * 10) +
    ((element_count + pseudo_element_count) * 1)
end

#expand_shorthand(decl) ⇒ Array<Declaration>

Expand a single shorthand declaration into longhand declarations. Called by Rule#expanded_declarations - not meant for direct use.

Parameters:

Returns:

  • (Array<Declaration>)

    Array of expanded longhand declarations



100
101
102
# File 'lib/cataract/pure.rb', line 100

def expand_shorthand(decl)
  Flatten.expand_shorthand(decl)
end

#flatten(stylesheet) ⇒ Stylesheet

Flatten stylesheet rules according to CSS cascade rules

Parameters:

  • stylesheet (Stylesheet)

    Stylesheet to flatten

Returns:

  • (Stylesheet)

    New stylesheet with flattened rules



91
92
93
# File 'lib/cataract/pure.rb', line 91

def flatten(stylesheet)
  Flatten.flatten(stylesheet, mutate: false)
end

#parse(css_string, parser_options = {}) ⇒ Hash

Parse CSS string and return hash with rules, media_index, charset, etc. Called by Stylesheet#add_block - not meant for direct use.

}

Parameters:

  • css_string (String)

    CSS to parse

  • parser_options (Hash) (defaults to: {})

    Parser configuration options

Options Hash (parser_options):

  • :selector_lists (Boolean) — default: true

    Track selector lists

Returns:

  • (Hash)

    { rules: Array, # Flat array of Rule/AtRule structs _media_index: Hash, # Symbol => Array of rule IDs charset: String|nil, # @charset value if present _has_nesting: Boolean # Whether any nested rules exist



82
83
84
85
# File 'lib/cataract/pure.rb', line 82

def parse(css_string, parser_options = {})
  parser = Parser.new(css_string, parser_options: parser_options)
  parser.parse
end

#parse_declarations(str) ⇒ Array<Declaration>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parse a standalone CSS declaration-list string ("color: red; margin: 10px") into an array of Declaration structs.

Parameters:

  • str (String)

    CSS declarations, optionally wrapped in { ... }

Returns:



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/cataract/pure/declarations.rb', line 24

def parse_declarations(str)
  fin = str.bytesize
  pos = 0

  # Strip outer braces and whitespace (css_parser compatibility)
  pos += 1 while pos < fin && (decl_whitespace?(str.getbyte(pos)) || str.getbyte(pos) == BYTE_LBRACE)
  fin -= 1 while fin > pos && (decl_whitespace?(str.getbyte(fin - 1)) || str.getbyte(fin - 1) == BYTE_RBRACE)

  declarations = []

  while pos < fin
    pos += 1 while pos < fin && (decl_whitespace?(str.getbyte(pos)) || str.getbyte(pos) == BYTE_SEMICOLON)
    break if pos >= fin

    span = scan_one_declaration(str, pos, fin)
    break unless span # No colon found - stop parsing entirely

    pos = span[:next_pos]
    next unless span[:val_end] > span[:val_start] # Skip if value is empty

    property = str.byteslice(span[:prop_start], span[:prop_end] - span[:prop_start])
                  .force_encoding(Encoding::US_ASCII).downcase
    value = str.byteslice(span[:val_start], span[:val_end] - span[:val_start]).force_encoding(Encoding::UTF_8)

    declarations << Declaration.new(property, value, span[:important])
  end

  declarations
end

#stylesheet_to_formatted_s(rules, charset, has_nesting, selector_lists = {}, media_queries = [], media_query_lists = {}) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Serialize stylesheet to formatted CSS string (with indentation).

Parameters:

  • rules (Array<Rule>)

    Array of rules

  • charset (String, nil)

    @charset value

  • has_nesting (Boolean)

    Whether any nested rules exist

  • selector_lists (Hash) (defaults to: {})

    Selector list ID => array of rule IDs (for grouping)

  • media_queries (Array<MediaQuery>) (defaults to: [])

    Array of MediaQuery objects

  • media_query_lists (Hash) (defaults to: {})

    List ID => array of MediaQuery IDs (for comma-separated queries)

Returns:

  • (String)

    Formatted CSS string



654
655
656
657
658
# File 'lib/cataract/pure/serializer.rb', line 654

def stylesheet_to_formatted_s(rules, charset, has_nesting, selector_lists = {}, media_queries = [],
                              media_query_lists = {})
  Serializer.new(rules, charset, has_nesting, selector_lists, media_queries, media_query_lists,
                 formatted: true).to_s
end

#stylesheet_to_s(rules, charset, has_nesting, selector_lists = {}, media_queries = [], media_query_lists = {}) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Serialize stylesheet to compact CSS string.

Parameters:

  • rules (Array<Rule>)

    Array of rules

  • charset (String, nil)

    @charset value

  • has_nesting (Boolean)

    Whether any nested rules exist

  • selector_lists (Hash) (defaults to: {})

    Selector list ID => array of rule IDs (for grouping)

  • media_queries (Array<MediaQuery>) (defaults to: [])

    Array of MediaQuery objects

  • media_query_lists (Hash) (defaults to: {})

    List ID => array of MediaQuery IDs (for comma-separated queries)

Returns:

  • (String)

    Compact CSS string



641
642
643
# File 'lib/cataract/pure/serializer.rb', line 641

def stylesheet_to_s(rules, charset, has_nesting, selector_lists = {}, media_queries = [], media_query_lists = {})
  Serializer.new(rules, charset, has_nesting, selector_lists, media_queries, media_query_lists, formatted: false).to_s
end