Class: WebComponentsRails::HTMLImportProcessor

Inherits:
Object
  • Object
show all
Defined in:
lib/web_components_rails/html_import_processor.rb

Overview

Processes web component HTML files that contains imports See also:

https://github.com/rails/sprockets/blob/master/UPGRADING.md
https://github.com/rails/sprockets/blob/3.x/lib/sprockets/directive_processor.rb

Constant Summary collapse

VERSION =
'10'
XML_SAVE_OPTIONS =
{
  save_with: ::Nokogiri::XML::Node::SaveOptions::DEFAULT_XML | ::Nokogiri::XML::Node::SaveOptions::NO_EMPTY_TAGS
}
URI_ATTRIBUTES =

URI attributes are determined by using the attributes list at html.spec.whatwg.org/#attributes-3 and looking for attributes that specify “Valid URL” as their values

%w(
  action
  cite
  data
  formaction
  href
  itemid
  manifest
  ping
  poster
  src
).freeze
BOOLEAN_ATTRIBUTES =

URI attributes are determined by using the attributes list at html.spec.whatwg.org/#attributes-3 and looking for attributes that specify “Boolean attribute” as their values

%w(
  allowfullscreen
  allowpaymentrequest
  allowusermedia
  async
  autofocus
  autoplay
  checked
  controls
  default
  defer
  disabled
  formnovalidate
  hidden
  ismap
  itemscope
  loop
  multiple
  muted
  nomodule
  novalidate
  open
  playsinline
  readonly
  required
  reversed
  selected
).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ HTMLImportProcessor



102
103
104
# File 'lib/web_components_rails/html_import_processor.rb', line 102

def initialize(options = {})
  @cache_key = [self.class.name, VERSION, options].freeze
end

Instance Attribute Details

#cache_keyObject (readonly)

Returns the value of attribute cache_key.



100
101
102
# File 'lib/web_components_rails/html_import_processor.rb', line 100

def cache_key
  @cache_key
end

Class Method Details

.cache_keyObject



68
69
70
# File 'lib/web_components_rails/html_import_processor.rb', line 68

def self.cache_key
  instance.cache_key
end

.call(input) ⇒ Object



64
65
66
# File 'lib/web_components_rails/html_import_processor.rb', line 64

def self.call(input)
  instance.call(input)
end

.doc_to_html(doc) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/web_components_rails/html_import_processor.rb', line 72

def self.doc_to_html(doc)
  doc_html = doc.to_html(encoding: 'UTF-8')

  uri_regex = Regexp.union(URI_ATTRIBUTES)
  square_brackets_regex = Regexp.new("(#{uri_regex})=\"%5B%5B(.+?)%5D%5D\"", 'i')
  curly_braces_regex = Regexp.new("(#{uri_regex})=\"%7B%7B(.+?)%7D%7D\"", 'i')

  selectors = (URI_ATTRIBUTES + BOOLEAN_ATTRIBUTES).map { |attribute| "*[#{attribute}]"}
  doc.css(selectors.join(',')).each do |node|
    # Nokogiri only writes out valid HTML so this outputs some nodes that would not be valid HTML
    # (e.g. nodes with boolean attributes that have values, or nodes that have unescaped URI attributes)
    # as both HTML and XML, and uses swap out the HTML for XML in the complete document HTML.
    pattern = node.to_html
    replacement = node.to_xml(XML_SAVE_OPTIONS)

    # Nokogiri/Nokogumbo are hard-coded to URI-escape certain attributes (src, href, action, and a[name]),
    # so we have to put in placeholders, and fix the values in the HTML string output afterwards
    # This doesn't work so well with framework-specific syntax (eg. <foo src="{{bar}}">)
    replacement.gsub!(square_brackets_regex, '\1="[[\2]]"')
    replacement.gsub!(curly_braces_regex, '\1="{{\2}}"')

    doc_html.gsub!(pattern, replacement)
  end

  doc_html
end

.instanceObject



60
61
62
# File 'lib/web_components_rails/html_import_processor.rb', line 60

def self.instance
  @instance ||= new
end

Instance Method Details

#call(input) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/web_components_rails/html_import_processor.rb', line 106

def call(input)
  # Sprockets::Environment for looking up assets, etc.
  @environment = input[:environment]
  @context = @environment.context_class.new(input)
  @data = input[:data]
  @filename = input[:filename]
  @dirname = File.dirname(@filename)

  @data, paths = process_imports(@data, @dirname)
  paths.each do |path|
    @context.require_asset(path)
  end

  @context..merge(data: @data)
end