Class: Oxc::MutationVisitor

Inherits:
Visitor
  • Object
show all
Defined in:
lib/oxc/mutation_visitor.rb,
sig/oxc/mutation_visitor.rbs

Overview

A visitor that rewrites the source it walked, by recording what to do to a node and splicing the original text at the end. Everything nothing touched survives byte for byte.

class Renamer < Oxc::MutationVisitor
  def visit_identifier(node)
    replace(node, "renamed") if node["name"] == "count"
  end
end

Renamer.new.rewrite("let count = 1")  #=> "let renamed = 1"

Defined Under Namespace

Classes: Edit, Invalid, Overlap

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Visitor

#visit

Instance Attribute Details

#parsedOxc::ParseResult (readonly)

Signature:

  • Oxc::ParseResult

Returns:



27
28
29
# File 'lib/oxc/mutation_visitor.rb', line 27

def parsed
  @parsed
end

#sourceString (readonly)

Signature:

  • String

Returns:

  • (String)


26
27
28
# File 'lib/oxc/mutation_visitor.rb', line 26

def source
  @source
end

Instance Method Details

#applyString

: () -> String

Returns:

  • (String)


115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/oxc/mutation_visitor.rb', line 115

def apply
  taken = 0
  result = +""

  @edits.sort_by { |edit| [edit.start, edit.order] }.each do |edit|
    result << source.byteslice(taken, edit.start - taken).to_s << edit.text

    taken = edit.finish
  end

  result << source.byteslice(taken..).to_s
end

#edit(start, finish, text) ⇒ void

This method returns an undefined value.

: (Integer, Integer, String) -> void

Parameters:

  • (Integer)
  • (Integer)
  • (String)


95
96
97
98
99
100
101
102
103
104
105
# File 'lib/oxc/mutation_visitor.rb', line 95

def edit(start, finish, text)
  @edits.each do |existing|
    next unless overlaps?(existing, start, finish)

    raise Overlap, "an edit at #{start}..#{finish} overlaps one at #{existing.start}..#{existing.finish}"
  end

  @edits << Edit.new(start: start, finish: finish, text: text, order: @edits.length)

  nil
end

#insert_after(node, text) ⇒ void

This method returns an undefined value.

: (Oxc::Node, String) -> void

Parameters:



61
62
63
# File 'lib/oxc/mutation_visitor.rb', line 61

def insert_after(node, text)
  edit(node.finish, node.finish, text)
end

#insert_before(node, text) ⇒ void

This method returns an undefined value.

: (Oxc::Node, String) -> void

Parameters:



56
57
58
# File 'lib/oxc/mutation_visitor.rb', line 56

def insert_before(node, text)
  edit(node.start, node.start, text)
end

#overlaps?(existing, start, finish) ⇒ Boolean

: (Edit, Integer, Integer) -> bool

Parameters:

  • (Edit)
  • (Integer)
  • (Integer)

Returns:

  • (Boolean)


108
109
110
111
112
# File 'lib/oxc/mutation_visitor.rb', line 108

def overlaps?(existing, start, finish)
  return false if existing.start == existing.finish && start == finish

  start < existing.finish && existing.start < finish
end

#remove(node) ⇒ void

This method returns an undefined value.

: (Oxc::Node) -> void

Parameters:



51
52
53
# File 'lib/oxc/mutation_visitor.rb', line 51

def remove(node)
  replace(node, "")
end

#replace(node, text) ⇒ void

This method returns an undefined value.

: (Oxc::Node, String) -> void

Parameters:



44
45
46
47
48
# File 'lib/oxc/mutation_visitor.rb', line 44

def replace(node, text)
  @replaced << [node.start, node.finish]

  edit(node.start, node.finish, text)
end

#replaced?(node) ⇒ Boolean

: (Oxc::Node) -> bool

Parameters:

Returns:

  • (Boolean)


90
91
92
# File 'lib/oxc/mutation_visitor.rb', line 90

def replaced?(node)
  @replaced.any? { |start, finish| node.start >= start && node.finish <= finish }
end

#rewrite(source, verify: true, **options) ⇒ String

: (String, ?verify: bool, **untyped) -> String

Parameters:

  • (String)
  • verify: (Boolean) (defaults to: true)
  • (Object)

Returns:

  • (String)


30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/oxc/mutation_visitor.rb', line 30

def rewrite(source, verify: true, **options)
  @source = source
  @edits = [] #: Array[Edit]
  @replaced = [] #: Array[[Integer, Integer]]
  @parsed = Oxc.parse(source, **options).validate!

  visit(parsed)

  rewritten = apply

  verify ? verified(rewritten, options) : rewritten
end

#verified(rewritten, options) ⇒ String

: (String, Hash[Symbol, untyped]) -> String

Parameters:

  • (String)
  • (Hash[Symbol, untyped])

Returns:

  • (String)


81
82
83
84
85
86
87
# File 'lib/oxc/mutation_visitor.rb', line 81

def verified(rewritten, options)
  answer = Oxc.parse(rewritten, **options, ast: false)

  return rewritten unless answer.errors?

  raise Invalid, "what was rewritten no longer reads as JavaScript: #{answer.errors.first&.message}"
end

#visit_children(node) ⇒ void

This method returns an undefined value.

: (Oxc::Node) -> void

Parameters:



72
73
74
75
76
# File 'lib/oxc/mutation_visitor.rb', line 72

def visit_children(node)
  return nil if replaced?(node)

  super
end

#wrap(node, before, after) ⇒ void

This method returns an undefined value.

: (Oxc::Node, String, String) -> void

Parameters:



66
67
68
69
# File 'lib/oxc/mutation_visitor.rb', line 66

def wrap(node, before, after)
  insert_before(node, before)
  insert_after(node, after)
end