class Opal::Nodes::XStringNode

def self.single_line?(children)

the same line (e.g. because of interpolations)
Check if there's only one child or if they're all part of
def self.single_line?(children)
  (children.size == 1) || children.none? do |c|
    c.type == :str && c.loc.expression.source.end_with?("\n")
  end
end

def self.strip_empty_children(children)

}
foo()
%x{
# children before and after `foo()`
# this will generate two additional empty
@example

Will remove empty :str lines coming from cosmetic newlines in x-strings
def self.strip_empty_children(children)
  children = children.dup
  empty_line = ->(child) { child.nil? || (child.type == :str && child.loc.expression.source.rstrip.empty?) }
  children.shift while children.any? && empty_line[children.first]
  children.pop while children.any? && empty_line[children.last]
  children
end

def compile

def compile
  if compiler.backtick_javascript_or_warn?
    compile_javascript
  else
    compile_send
  end
end

def compile_child(child)

def compile_child(child)
  case child.type
  when :str
    value = child.loc.expression.source
    scope.self if value.include? 'self'
    push Fragment.new(value, scope, child)
  when :begin, :gvar, :ivar, :nil
    push expr(child)
  else
    raise "Unsupported xstr part: #{child.type}"
  end
end

def compile_javascript

def compile_javascript
  @should_add_semicolon = false
  unpacked_children = unpack_return(children)
  stripped_children = XStringNode.strip_empty_children(unpacked_children)
  if XStringNode.single_line?(stripped_children)
    # If it's a single line we'll try to:
    #
    # - strip empty lines
    # - remove a trailing `;`
    # - detect an embedded `return`
    # - prepend a `return` when needed
    # - append a `;` when needed
    # - warn the user not to use the semicolon in single-line x-strings
    compile_single_line(stripped_children)
  else
    # Here we leave to the user the responsibility to add
    # a return where it's due.
    unpacked_children.each { |c| compile_child(c) }
  end
  wrap '(', ')' if recv?
  push ';' if @should_add_semicolon
end

def compile_send

def compile_send
  sexp = s(:send, nil, :`, s(:dstr, *children))
  push process(sexp, @level)
end

def compile_single_line(children)

def compile_single_line(children)
  has_embeded_return = false
  first_child  = children.shift
  single_child = children.empty?
  first_child ||= s(:nil)
  if first_child.type == :str
    first_value = first_child.loc.expression.source.strip
    has_embeded_return = first_value =~ /^return\b/
  end
  push('return ') if @returning && !has_embeded_return
  last_child = children.pop || first_child
  last_value = extract_last_value(last_child) if last_child.type == :str
  unless single_child
    # assuming there's an interpolation somewhere (type != :str)
    @should_add_semicolon = false
    compile_child(first_child)
    children.each { |c| compile_child(c) }
  end
  if last_child.type == :str
    push Fragment.new(last_value, scope, last_child)
  else
    compile_child(last_child)
  end
end

def extract_last_value(last_child)

Will drop the trailing semicolon if all conditions are met
def extract_last_value(last_child)
  last_value = last_child.loc.expression.source.rstrip
  scope.self if last_value.include? 'self'
  if (@returning || expr?) && last_value.end_with?(';')
    compiler.warning(
      'Removed semicolon ending x-string expression, interpreted as unintentional',
      last_child.line,
    )
    last_value = last_value[0..-2]
  end
  @should_add_semicolon = true if @returning
  last_value
end

def unpack_return(children)

so we need to combine "return" with "raw_source"
Since we need to take original source of :str we have to use raw source
A case for manually created :js_return statement in Compiler#returns
def unpack_return(children)
  first_child = children.first
  @returning  = false
  if first_child.type == :js_return
    @returning = true
    children = first_child.children
  end
  children
end