lib/erb/linter/converter.rb
def erb2html(source)
    source = +source

    # encode all erb tags so that the HTML looks valid
    erb_tags = {}
    source.gsub!(ERB_TAG) do |tag|
      uid = ["ERB_", SecureRandom.uuid].join.delete('-')
      erb_tags[uid] = tag
      uid
    end

    erb_tags_matcher = Regexp.union(erb_tags.keys)

    # transform/escape all the erb-attributes first
    source.gsub!(HTML_TAG).each do |match|
      line = Regexp.last_match.to_a[1..-1]
      tag = [line[0], line[2]+line[3]]

      # scan each attribute into an array
      attr_scanner = StringScanner.new(line[1])
      attributes = []
      attributes << (attr_scanner.scan(HTML_ATTR) or raise "Can't scan: #{attr_scanner.string}") until attr_scanner.eos?
      # attributes.compact!

      i = -1
      attributes.map! do |attribute|
        if attribute.match?(erb_tags_matcher)
          space, attribute = attribute.scan(/\A(\s+)(.*)\z/m).flatten
          attribute.gsub!(erb_tags_matcher, erb_tags)

          attribute =
            case attribute
            when /\A#{ERB_TAG}\z/
              %{data-erb-#{i += 1}="#{CGI.escapeHTML(attribute)}"}
            when /\A#{ATTR_NAME}=/
              name, value = attribute.split("=", 2)
              quote = '"'
              if value.match(/\A['"]/)
                quote = value[0]
                value = value[1...-1]
              end
              %{data-erb-#{name}=#{quote}#{CGI.escapeHTML(value)}#{quote}}
            else
              raise "Don't know how to process attribute: #{attribute.inspect}"
            end

          "#{space}#{attribute}"
        else
          attribute
        end
      end

      [tag[0], *attributes, tag[1]].join
    end

    # restore the encoded erb tags
    source.gsub!(erb_tags_matcher, erb_tags)

    # replaces all <% %> not inside opening html tags
    replacements = [
      { %r{<%\s*end\s*-?%>} => "</erb>" },
      { %r{(^\s*)<%(\s*(?:else|elsif\b.*)\s*)-?%>} => "\\1</erb>\n\\1<erb silent erb-code-start\\2erb-code-end>" },
      { "<%=" => "<erb loud erb-code-start" },
      { "<%" => "<erb silent erb-code-start" },
      { "-%>" => "erb-code-end>" },
      { "%>" => "erb-code-end>" },
    ]

    replacements.each{ |h| h.each { |replace, with| source.gsub! replace, with } }

    source.scan(/(erb-code-start)((?:(?!erb-code-end)[\s\S])*)(erb-code-end)/).each do |match|
      source.sub!("#{match[0]}#{match[1]}#{match[2]}") do |_m|
        code = match[1]

        # is nil when the parsing is broken, meaning it's an open expression
        if Ripper.sexp(code).nil?
          "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\""
        else
          "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\"></erb"
        end
      end
    end

    source
  end
# (credit goes to the Deface gem for the original implementation)
(credit goes to the Deface gem for the original implementation)
( credit goes to the Deface gem for the original implementation )
def erb2html(source)
    source = +source
    erb_tags = {}
    source.gsub!(ERB_TAG) do |tag|
      uid = ["ERB_", SecureRandom.uuid].join.delete('-')
      erb_tags[uid] = tag
      uid
    end
    erb_tags_matcher = Regexp.union(erb_tags.keys)
    source.gsub!(HTML_TAG).each do |match|
      line = Regexp.last_match.to_a[1..-1]
      tag = [line[0], line[2]+line[3]]
      attr_scanner = StringScanner.new(line[1])
      attributes = []
      attributes << (attr_scanner.scan(HTML_ATTR) or raise "Can't scan: #{attr_scanner.string}") until attr_scanner.eos?
      i = -1
      attributes.map! do |attribute|
        if attribute.match?(erb_tags_matcher)
          space, attribute = attribute.scan(/\A(\s+)(.*)\z/m).flatten
          attribute.gsub!(erb_tags_matcher, erb_tags)
          attribute =
            case attribute
            when /\A#{ERB_TAG}\z/
              %{data-erb-#{i += 1}="#{CGI.escapeHTML(attribute)}"}
            when /\A#{ATTR_NAME}=/
              name, value = attribute.split("=", 2)
              quote = '"'
              if value.match(/\A['"]/)
                quote = value[0]
                value = value[1...-1]
              end
              %{data-erb-#{name}=#{quote}#{CGI.escapeHTML(value)}#{quote}}
            else
              raise "Don't know how to process attribute: #{attribute.inspect}"
            end
          "#{space}#{attribute}"
        else
          attribute
        end
      end
      [tag[0], *attributes, tag[1]].join
    end
    source.gsub!(erb_tags_matcher, erb_tags)
    replacements = [
      { %r{<%\s*end\s*-?%>} => "</erb>" },
      { %r{(^\s*)<%(\s*(?:else|elsif\b.*)\s*)-?%>} => "\\1</erb>\n\\1<erb silent erb-code-start\\2erb-code-end>" },
      { "<%=" => "<erb loud erb-code-start" },
      { "<%" => "<erb silent erb-code-start" },
      { "-%>" => "erb-code-end>" },
      { "%>" => "erb-code-end>" },
    ]
    replacements.each{ |h| h.each { |replace, with| source.gsub! replace, with } }
    source.scan(/(erb-code-start)((?:(?!erb-code-end)[\s\S])*)(erb-code-end)/).each do |match|
      source.sub!("#{match[0]}#{match[1]}#{match[2]}") do |_m|
        code = match[1]
        if Ripper.sexp(code).nil?
          "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\""
        else
          "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\"></erb"
        end
      end
    end
    source
  end
def erb2html ( source ) source = + source erb_tags = { } source . gsub! ( ERB_TAG ) do | tag | uid = [ "ERB_" , SecureRandom . uuid ] . join . delete ( '-' ) erb_tags [ uid ] = tag uid end erb_tags_matcher = Regexp . union ( erb_tags . keys ) source . gsub! ( HTML_TAG ) . each do | match | line = Regexp . last_match . to_a [ 1 .. - 1 ] tag = [ line [ 0 ] , line [ 2 ] + line [ 3 ] ] attr_scanner = StringScanner . new ( line [ 1 ] ) attributes = [ ] attributes << ( attr_scanner . scan ( HTML_ATTR ) or raise "Can't scan: #{attr_scanner.string}" ) until attr_scanner . eos? i = - 1 attributes . map! do | attribute | if attribute . match? ( erb_tags_matcher ) space , attribute = attribute . scan ( / \A ( \s +)(.*) \z /m ) . flatten attribute . gsub! ( erb_tags_matcher , erb_tags ) attribute = case attribute when / \A #{ ERB_TAG } \z / %{data-erb-#{i += 1}="#{CGI.escapeHTML(attribute)}"} when / \A #{ ATTR_NAME } = / name , value = attribute . split ( "=" , 2 ) quote = '"' if value . match ( / \A ['"] / ) quote = value [ 0 ] value = value [ 1 ... - 1 ] end %{data-erb-#{name}=#{quote}#{CGI.escapeHTML(value)}#{quote}} else raise "Don't know how to process attribute: #{attribute.inspect}" end "#{space}#{attribute}" else attribute end end [ tag [ 0 ] , * attributes , tag [ 1 ] ] . join end source . gsub! ( erb_tags_matcher , erb_tags ) replacements = [ { %r{ <% \s *end \s *-?%> } => "</erb>" } , { %r{ (^ \s *)<%( \s *(?:else|elsif \b .*) \s *)-?%> } => "\\1</erb>\n\\1<erb silent erb-code-start\\2erb-code-end>" } , { "<%=" => "<erb loud erb-code-start" } , { "<%" => "<erb silent erb-code-start" } , { "-%>" => "erb-code-end>" } , { "%>" => "erb-code-end>" } , ] replacements . each { | h | h . each { | replace , with | source . gsub! replace , with } } source . scan ( / (erb-code-start)((?:(?!erb-code-end)[ \s \S ])*)(erb-code-end) / ) . each do | match | source . sub! ( "#{match[0]}#{match[1]}#{match[2]}" ) do | _m | code = match [ 1 ] if Ripper . sexp ( code ) . nil? "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\"" else "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", "&#10;"))}\"></erb" end end end source end
(credit goes to the Deface gem for the original implementation)
( credit goes to the Deface gem for the original implementation )
# encode all erb tags so that the HTML looks valid # transform/escape all the erb-attributes first # scan each attribute into an array # attributes.compact! # restore the encoded erb tags # replaces all <% %> not inside opening html tags # is nil when the parsing is broken, meaning it's an open expression
source