Class: SyntaxTree::Tailwindcss::RubyMutationVisitor

Inherits:
CompleteMutationVisitor show all
Defined in:
lib/syntax_tree/tailwindcss/ruby_mutation_visitor.rb

Instance Method Summary collapse

Methods inherited from CompleteMutationVisitor

#copy_and_visit_children, #mutate, #visit, #visit_child

Constructor Details

#initialize(sorter) ⇒ RubyMutationVisitor

Returns a new instance of RubyMutationVisitor.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/syntax_tree/tailwindcss/ruby_mutation_visitor.rb', line 8

def initialize(sorter)
  super()
  @sorter = sorter

  # Rewrite `class: "foo bar"` and `:class => "foo bar"`
  pattern = <<~PATTERN
    Assoc[
      key: Label[value: 'class:'] | SymbolLiteral[value: Kw[value: 'class']],
      value: StringLiteral
    ]
  PATTERN
  mutate(pattern) { |node| node.copy(value: rewrite_string_literal(node.value)) }

  # Rewrite `class_names("foo bar", "lorem ipsum")`
  mutate("CallNode[message: Ident[value: 'class_names']]") do |node|
    next node unless node.arguments.is_a?(ArgParen)
    next node unless node.arguments.arguments.is_a?(Args)

    args = node.arguments.arguments
    node.copy(arguments: node.arguments.copy(arguments: rewrite_args(args)))
  end

  # Rewrite `class_names "foo bar", "lorem ipsum"`
  mutate("Command[message: Ident[value: 'class_names']]") do |node|
    next node unless node.arguments.is_a?(Args)

    node.copy(arguments: rewrite_args(node.arguments))
  end
end

Instance Method Details

#rewrite_args(args) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/syntax_tree/tailwindcss/ruby_mutation_visitor.rb', line 54

def rewrite_args(args)
  return args unless args.is_a?(Args)

  # Rewrite each string literal separately, e.g. 'mb-4 text-2xl'
  rewritten_parts = args.parts.map { |part| rewrite_string_literal(part) }

  # Reorder subsequent single-class strings, e.g. 'mb-4', 'text-2xl'
  reordered_parts =
    rewritten_parts
      .slice_when { |a, b| single_class_string_literal?(a) ^ single_class_string_literal?(b) }
      .map do |segment|
        next segment if segment.size == 1 || !single_class_string_literal?(segment.first)

        segment
          .uniq { |node| node.parts.first.value }
          .sort_by { |node| @sorter.sort_order(node.parts.first.value) }
      end
      .flatten

  args.copy(parts: reordered_parts)
end

#rewrite_string_literal(string_literal) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/syntax_tree/tailwindcss/ruby_mutation_visitor.rb', line 38

def rewrite_string_literal(string_literal)
  return string_literal unless string_literal.is_a?(StringLiteral)

  string_parts =
    string_literal.parts.map do |string_part|
      next string_part unless string_part.is_a?(TStringContent)

      value = string_part.value
      new_value = @sorter.sort(value.split).join(" ")
      new_value = " #{new_value}" if value.match?(/\A\s/)
      new_value = "#{new_value} " if value.match?(/\s\z/)
      string_part.copy(value: new_value)
    end
  string_literal.copy(parts: string_parts)
end

#single_class_string_literal?(string_literal) ⇒ Boolean

Returns:

  • (Boolean)


76
77
78
79
80
# File 'lib/syntax_tree/tailwindcss/ruby_mutation_visitor.rb', line 76

def single_class_string_literal?(string_literal)
  string_literal.is_a?(StringLiteral) && string_literal.parts.size == 1 &&
    string_literal.parts.first.is_a?(TStringContent) &&
    string_literal.parts.first.value.match?(/\A\S+\z/)
end