html-to-haml 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/html_to_haml/converter.rb +45 -0
- data/lib/html_to_haml/helpers/haml_whitespace_cleaner.rb +9 -0
- data/lib/html_to_haml/html_to_haml.rb +16 -0
- data/lib/html_to_haml/tools/erb/control_flow_matcher.rb +45 -0
- data/lib/html_to_haml/tools/erb/indentation_tracker.rb +37 -0
- data/lib/html_to_haml/tools/html/attribute_handler.rb +53 -0
- data/lib/html_to_haml/tools/html/indentation_tracker.rb +31 -0
- data/lib/html_to_haml/tools/non_html_selector_blocks/indentation_tracker.rb +35 -0
- data/lib/html_to_haml/tools/non_html_selector_blocks/tag_type_matchers.rb +35 -0
- data/lib/html_to_haml/use_cases/erb/basic_conversion_use_case.rb +39 -0
- data/lib/html_to_haml/use_cases/erb/indentation_conversion_use_case.rb +85 -0
- data/lib/html_to_haml/use_cases/html/attribute_conversion_use_case.rb +48 -0
- data/lib/html_to_haml/use_cases/html/comment_conversion_use_case.rb +34 -0
- data/lib/html_to_haml/use_cases/html/conversion_use_case.rb +19 -0
- data/lib/html_to_haml/use_cases/html/default_conversion_use_case.rb +73 -0
- data/lib/html_to_haml/use_cases/non_html_selector_blocks/basic_conversion_use_case.rb +74 -0
- data/lib/html_to_haml/use_cases/non_html_selector_blocks/script_conversion_use_case.rb +16 -0
- data/lib/html_to_haml/use_cases/non_html_selector_blocks/style_conversion_use_case.rb +10 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 15bb392209552a1c2c5ed225486c98bb620bdb1c
|
4
|
+
data.tar.gz: 3b55b8c57a4577aed92b1766ffabb5ec6f0eb0e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c251c16238f91a8123f7d4f36a5a1516a5668ce43a0fe78be96915ce8d28134b1251632de2cf12d34528235e5767b3f53f5fa27fce1dfb8459581a115dbbb09b
|
7
|
+
data.tar.gz: f048b0b54befe18ff66d807f699664537954e803f67ae48003b963c1e39dc180e69f2fc0ba96f66c59367e39a3e5987a6abd0e93d692a1c510f4b972595cc22c
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative './use_cases/html/conversion_use_case'
|
2
|
+
require_relative './use_cases/erb/basic_conversion_use_case'
|
3
|
+
require_relative './use_cases/non_html_selector_blocks/style_conversion_use_case'
|
4
|
+
require_relative './use_cases/non_html_selector_blocks/script_conversion_use_case'
|
5
|
+
|
6
|
+
module HtmlToHaml
|
7
|
+
class Converter
|
8
|
+
def initialize(html)
|
9
|
+
@html = html
|
10
|
+
end
|
11
|
+
|
12
|
+
def convert
|
13
|
+
whitespace_free_html = remove_html_whitespace(html: @html)
|
14
|
+
erb_converted_haml = Erb::BasicConversionUseCase.new(whitespace_free_html).convert
|
15
|
+
haml = NonHtmlSelectorBlocks::StyleConversionUseCase.new(erb_converted_haml).convert
|
16
|
+
haml = NonHtmlSelectorBlocks::ScriptConversionUseCase.new(haml).convert
|
17
|
+
Html::ConversionUseCase.new(haml, remove_whitespace: false).convert
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def remove_html_whitespace(html:)
|
23
|
+
html.gsub(/#{html_with_important_whitespace}|^\s*|\n/) do |matching_html|
|
24
|
+
case matching_html
|
25
|
+
when /#{html_with_important_whitespace}/
|
26
|
+
initial_indentation = matching_html.gsub("\n", '').match(/^\s*/).to_s
|
27
|
+
matching_html.gsub(/^#{initial_indentation}/, "\n")
|
28
|
+
else
|
29
|
+
""
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def html_with_important_whitespace
|
35
|
+
important_whitespace_classes.map do |klass|
|
36
|
+
"^\\s*<#{klass::HTML_TAG_NAME}.*?>(.|\n)*?<\/#{klass::HTML_TAG_NAME}>"
|
37
|
+
end.join("|")
|
38
|
+
end
|
39
|
+
|
40
|
+
def important_whitespace_classes
|
41
|
+
[NonHtmlSelectorBlocks::ScriptConversionUseCase,
|
42
|
+
NonHtmlSelectorBlocks::StyleConversionUseCase]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module HtmlToHaml
|
4
|
+
module Erb
|
5
|
+
class ControlFlowMatcher
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
CONTROL_FLOW_MIDDLE_OF_LINE_KEYWORDS = ["do"]
|
9
|
+
CONTROL_FLOW_BEGINNING_OF_LINE_KEYWORDS = ["if", "unless"]
|
10
|
+
CONTROL_FLOW_CONTINUE_KEYWORDS = ["elsif", "else", "when"]
|
11
|
+
|
12
|
+
def begin_case_statement?(erb:)
|
13
|
+
matches_keywords_at_beginning_of_line?(erb: erb, keywords: ["case"])
|
14
|
+
end
|
15
|
+
|
16
|
+
def begin_indented_control_flow?(erb:)
|
17
|
+
matches_keywords?(erb: erb, keywords: CONTROL_FLOW_MIDDLE_OF_LINE_KEYWORDS) ||
|
18
|
+
matches_keywords_at_beginning_of_line?(erb: erb, keywords: CONTROL_FLOW_BEGINNING_OF_LINE_KEYWORDS)
|
19
|
+
end
|
20
|
+
|
21
|
+
def continue_indented_control_flow?(erb:)
|
22
|
+
matches_keywords_at_beginning_of_line?(erb: erb, keywords: CONTROL_FLOW_CONTINUE_KEYWORDS)
|
23
|
+
end
|
24
|
+
|
25
|
+
def end_of_block?(erb:)
|
26
|
+
erb_without_strings(erb: erb) =~ /\s*-\send/
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def matches_keywords_at_beginning_of_line?(erb:, keywords:)
|
32
|
+
erb_without_strings(erb: erb) =~ /\s*(-|=)\s*(#{keywords.join("|")})(\s|$)/
|
33
|
+
end
|
34
|
+
|
35
|
+
def matches_keywords?(erb:, keywords:)
|
36
|
+
erb_without_strings(erb: erb) =~ /\s*(-|=)(.*)\s+(#{keywords.join("|")})(\s|$)/
|
37
|
+
end
|
38
|
+
|
39
|
+
def erb_without_strings(erb:)
|
40
|
+
erb.gsub(/".*?"/, '').gsub(/'.*?'/, '')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module HtmlToHaml
|
2
|
+
module Erb
|
3
|
+
class IndentationTracker
|
4
|
+
attr_accessor :indentation_level
|
5
|
+
attr_reader :indentation_amount, :case_statement_level
|
6
|
+
def initialize(indentation_level:, indentation_amount:)
|
7
|
+
@indentation_level = indentation_level
|
8
|
+
@indentation_amount = indentation_amount
|
9
|
+
@case_statement_level = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def begin_case_statement
|
13
|
+
self.indentation_level += indentation_amount * 2
|
14
|
+
case_statement_level << indentation_level
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_indentation
|
18
|
+
self.indentation_level += indentation_amount
|
19
|
+
end
|
20
|
+
|
21
|
+
def end_block
|
22
|
+
if indentation_level == @case_statement_level.last
|
23
|
+
case_statement_level.pop
|
24
|
+
self.indentation_level -= indentation_amount * 2
|
25
|
+
else
|
26
|
+
self.indentation_level -= indentation_amount
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# I may allow people to use this for nested case statements,
|
31
|
+
# but reserve the right to be snarky about it when they do.
|
32
|
+
def nested_case_statement?
|
33
|
+
case_statement_level.length > 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module HtmlToHaml
|
2
|
+
module Html
|
3
|
+
class AttributeHandler
|
4
|
+
HTML_ONLY_ATTRIBUTE_REGEX = /=['"][^#\{]*['"]/
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@ids = []
|
8
|
+
@classes = []
|
9
|
+
@plain_attributes = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_attribute(attr:)
|
13
|
+
if use_id_syntax?(attr: attr)
|
14
|
+
@ids += extract_attribute_value(attr: attr).split(' ')
|
15
|
+
elsif use_class_syntax?(attr: attr)
|
16
|
+
@classes += extract_attribute_value(attr: attr).split(' ')
|
17
|
+
else
|
18
|
+
@plain_attributes << attr.strip.gsub(/=/, ': ')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def attributes_string
|
23
|
+
"#{format_ids}#{format_classes}#{format_attributes}"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def use_id_syntax?(attr:)
|
29
|
+
attr =~ /id#{HTML_ONLY_ATTRIBUTE_REGEX}/
|
30
|
+
end
|
31
|
+
|
32
|
+
def use_class_syntax?(attr:)
|
33
|
+
attr =~ /class#{HTML_ONLY_ATTRIBUTE_REGEX}/
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_attribute_value(attr:)
|
37
|
+
attr.gsub(/.*=['"](.*)['"]/, '\1').strip
|
38
|
+
end
|
39
|
+
|
40
|
+
def format_attributes
|
41
|
+
@plain_attributes.empty? ? '' : "{ #{@plain_attributes.join(', ')} }"
|
42
|
+
end
|
43
|
+
|
44
|
+
def format_ids
|
45
|
+
@ids.empty? ? '' : "##{@ids.join('#')}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def format_classes
|
49
|
+
@classes.empty? ? '' : ".#{@classes.join('.')}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative '../../html_to_haml'
|
2
|
+
|
3
|
+
module HtmlToHaml::Html
|
4
|
+
class IndentationTracker
|
5
|
+
def initialize(indentation_amount:)
|
6
|
+
@indentation_level = 0
|
7
|
+
@inside_self_closing_tag = false
|
8
|
+
@indentation_amount = indentation_amount
|
9
|
+
end
|
10
|
+
|
11
|
+
def start_html_tag
|
12
|
+
@indentation_level += @indentation_amount unless @inside_self_closing_tag
|
13
|
+
@inside_self_closing_tag = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_self_closing_tag
|
17
|
+
@inside_self_closing_tag = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def close_html_tag
|
21
|
+
@indentation_level -= @indentation_amount
|
22
|
+
if @indentation_level < 0
|
23
|
+
raise ParseError, 'The html is malformed and is attempting to close an html tag that was never started'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def indentation
|
28
|
+
" " * @indentation_level
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module HtmlToHaml
|
2
|
+
module NonHtmlSelectorBlocks
|
3
|
+
class IndentationTracker
|
4
|
+
attr_reader :indented, :adjust_whitespace
|
5
|
+
|
6
|
+
def initialize(indented:, adjust_whitespace:)
|
7
|
+
@indented = indented
|
8
|
+
@reset_adjust_whitespace = false
|
9
|
+
@adjust_whitespace = adjust_whitespace
|
10
|
+
end
|
11
|
+
|
12
|
+
def indent
|
13
|
+
return if indented
|
14
|
+
@indented = true
|
15
|
+
@reset_adjust_whitespace = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def outdent
|
19
|
+
return unless indented
|
20
|
+
@indented = false
|
21
|
+
@adjust_whitespace = false
|
22
|
+
@reset_adjust_whitespace = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def adjust_whitespace?(reset_value: adjust_whitespace)
|
26
|
+
if @reset_adjust_whitespace
|
27
|
+
@reset_adjust_whitespace = false
|
28
|
+
@adjust_whitespace = reset_value
|
29
|
+
else
|
30
|
+
@adjust_whitespace
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module HtmlToHaml
|
2
|
+
module NonHtmlSelectorBlocks
|
3
|
+
module TagTypeMatchers
|
4
|
+
TAG_TYPE_REGEX = "type=('|\")(.*?)('|\")"
|
5
|
+
TAG_TYPE_FROM_REGEX = '\2'
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def opening_tag?(tag:, in_block:)
|
10
|
+
!in_block && tag =~ /#{opening_tag_regex}/
|
11
|
+
end
|
12
|
+
|
13
|
+
def opening_tag_regex
|
14
|
+
"<#{self.class::HTML_TAG_NAME}.*?>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def closing_tag?(tag:, in_block:)
|
18
|
+
in_block && tag =~ /#{closing_tag_regex}/
|
19
|
+
end
|
20
|
+
|
21
|
+
def closing_tag_regex
|
22
|
+
"<\/#{self.class::HTML_TAG_NAME}>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def tag_type(tag:)
|
26
|
+
specified_tag_type(tag: tag) || self.class::DEFAULT_TAG_TYPE
|
27
|
+
end
|
28
|
+
|
29
|
+
def specified_tag_type(tag:)
|
30
|
+
type_match = tag.match(/#{self.class::TAG_TYPE_REGEX}/)
|
31
|
+
type_match && type_match.to_s.gsub(/#{self.class::TAG_TYPE_REGEX}/, self.class::TAG_TYPE_FROM_REGEX).split('/').last
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative '../../html_to_haml'
|
2
|
+
require_relative '../../helpers/haml_whitespace_cleaner'
|
3
|
+
require_relative 'indentation_conversion_use_case'
|
4
|
+
|
5
|
+
module HtmlToHaml::Erb
|
6
|
+
class BasicConversionUseCase
|
7
|
+
include HtmlToHaml::HamlWhitespaceCleaner
|
8
|
+
|
9
|
+
def initialize(erb)
|
10
|
+
@erb = erb
|
11
|
+
end
|
12
|
+
|
13
|
+
def convert
|
14
|
+
sanitized_erb = remove_newlines_within_erb_statements(erb: @erb)
|
15
|
+
erb = convert_syntax(erb: sanitized_erb)
|
16
|
+
haml = convert_indentation(erb: erb)
|
17
|
+
remove_haml_whitespace(haml: haml)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def remove_newlines_within_erb_statements(erb:)
|
23
|
+
erb.gsub(/<%(.*?)\n(.*?)%>/) do |erb_statement|
|
24
|
+
erb_statement.gsub("\n", " ")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert_syntax(erb:)
|
29
|
+
erb.gsub(/\s*?\n?(<%=|<%-|<%)\s?/) do |erb_selector|
|
30
|
+
erb_selector_index = erb_selector =~ /-|=/
|
31
|
+
erb_selector_index ? "\n#{erb_selector[erb_selector_index]} " : "\n- "
|
32
|
+
end.gsub(/\s?(-%>|%>)/, "\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
def convert_indentation(erb:)
|
36
|
+
IndentationConversionUseCase.instance.convert_indentation(erb: erb)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'forwardable'
|
3
|
+
require_relative '../../html_to_haml'
|
4
|
+
require_relative '../../tools/erb/indentation_tracker'
|
5
|
+
require_relative '../../tools/erb/control_flow_matcher'
|
6
|
+
|
7
|
+
module HtmlToHaml::Erb
|
8
|
+
class IndentationConversionUseCase
|
9
|
+
include Singleton
|
10
|
+
extend ::Forwardable
|
11
|
+
|
12
|
+
SNARKY_COMMENT_FOR_HAVING_NESTED_CASE_STATEMENTS = <<-HAML
|
13
|
+
/ It looks like this is the start of a nested case statement
|
14
|
+
/ Are you REALLY sure you want or need one? Really?
|
15
|
+
/ This converter will convert it for you below, but you should
|
16
|
+
/ seriously rethink your code right now.
|
17
|
+
HAML
|
18
|
+
|
19
|
+
def convert_indentation(erb:)
|
20
|
+
indentation_converter = new_indentation_converter
|
21
|
+
erb.split("\n").map do |erb_line|
|
22
|
+
indentation = indentation_for_line_or_error(erb: erb_line, indentation_level: indentation_converter.indentation_level)
|
23
|
+
adjust_indentation_level(erb: erb_line, indentation_converter: indentation_converter)
|
24
|
+
construct_haml_line(erb: erb_line, indentation: indentation, indentation_converter: indentation_converter)
|
25
|
+
end.join
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def_delegators :control_flow_matcher, :begin_case_statement?,
|
31
|
+
:begin_indented_control_flow?, :end_of_block?,
|
32
|
+
:continue_indented_control_flow?
|
33
|
+
|
34
|
+
def adjust_indentation_level(erb:, indentation_converter:)
|
35
|
+
if begin_case_statement?(erb: erb)
|
36
|
+
indentation_converter.begin_case_statement
|
37
|
+
elsif begin_indented_control_flow?(erb: erb)
|
38
|
+
indentation_converter.add_indentation
|
39
|
+
elsif end_of_block?(erb: erb)
|
40
|
+
indentation_converter.end_block
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def indentation_for_line_or_error(erb:, indentation_level:)
|
45
|
+
adjusted_indentation_level = adjusted_indentation_level_for_line(erb: erb, indentation_level: indentation_level)
|
46
|
+
if adjusted_indentation_level < 0
|
47
|
+
raise ParseError, 'The erb is malformed. Please make sure you have the correct number of "end" statements'
|
48
|
+
else
|
49
|
+
" " * adjusted_indentation_level
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def adjusted_indentation_level_for_line(erb:, indentation_level:)
|
54
|
+
if continue_indented_control_flow?(erb: erb)
|
55
|
+
indentation_level - HtmlToHaml::INDENTATION_AMOUNT
|
56
|
+
else
|
57
|
+
indentation_level
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def construct_haml_line(erb:, indentation:, indentation_converter:)
|
62
|
+
indentation_adjusted_haml_line = "#{indentation}"
|
63
|
+
if begin_nested_case_statement?(erb: erb, indentation_converter: indentation_converter)
|
64
|
+
indentation_adjusted_haml_line << nested_case_statement_commentary(indentation: indentation)
|
65
|
+
end
|
66
|
+
indentation_adjusted_haml_line << "#{erb}\n" unless end_of_block?(erb: erb)
|
67
|
+
end
|
68
|
+
|
69
|
+
def begin_nested_case_statement?(erb:, indentation_converter:)
|
70
|
+
begin_case_statement?(erb: erb) && indentation_converter.nested_case_statement?
|
71
|
+
end
|
72
|
+
|
73
|
+
def nested_case_statement_commentary(indentation:)
|
74
|
+
SNARKY_COMMENT_FOR_HAVING_NESTED_CASE_STATEMENTS.gsub("\n", "\n#{indentation}")
|
75
|
+
end
|
76
|
+
|
77
|
+
def new_indentation_converter
|
78
|
+
IndentationTracker.new(indentation_level: 0, indentation_amount: HtmlToHaml::INDENTATION_AMOUNT)
|
79
|
+
end
|
80
|
+
|
81
|
+
def control_flow_matcher
|
82
|
+
ControlFlowMatcher.instance
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require_relative '../../html_to_haml'
|
3
|
+
require_relative '../../tools/html/attribute_handler'
|
4
|
+
|
5
|
+
module HtmlToHaml::Html
|
6
|
+
class AttributeConversionUseCase
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
HAML_TAG_LINES = "^\s*%(.*)$"
|
10
|
+
MULTILINE_ERB_ATTRIBUTES_REGEX = /^\s*%.*[a-zA-Z1-9]+?=('|")[^'"]*\s*\n\s*=.*\n\s*[^'"]*("|')/
|
11
|
+
|
12
|
+
def convert_attributes(html:)
|
13
|
+
erb_converted_html = remove_erb_newlines(html: html)
|
14
|
+
erb_converted_html.gsub(/#{HAML_TAG_LINES}/) do |matched_elem|
|
15
|
+
haml_with_replaced_attributes(haml: matched_elem)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def haml_with_replaced_attributes(haml:)
|
22
|
+
attribute_handler = AttributeHandler.new
|
23
|
+
haml_without_attributes = haml.gsub(/\s*([a-zA-Z1-9]+?)=('|").*?('|")/) do |matched_elem|
|
24
|
+
# This could go into the AttributeHandler, but at the moment this is the only place
|
25
|
+
# that is aware of the erb syntax and that makes the AttributeHandler more isolated.
|
26
|
+
attr = escape_erb_attributes(attr: matched_elem)
|
27
|
+
attribute_handler.add_attribute(attr: attr)
|
28
|
+
''
|
29
|
+
end
|
30
|
+
"#{haml_without_attributes}#{attribute_handler.attributes_string}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_erb_newlines(html:)
|
34
|
+
# Erb attributes may be malformed to have their own line and a '=' in front
|
35
|
+
# (if the html already went through the erb converter)
|
36
|
+
# This changes them back into something that can be updated above
|
37
|
+
html.gsub(MULTILINE_ERB_ATTRIBUTES_REGEX) do |erb_attr|
|
38
|
+
erb_attr.gsub(/\n\s*(=|.*?'|.*?")/, ' \1')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def escape_erb_attributes(attr:)
|
43
|
+
attr.gsub(/=('|")(.*?)\s*=\s*([^\s]*)(\s*.*)('|")/, '=\1\2 #{\3}\4\5') # put erb text inside #{} globally
|
44
|
+
.gsub(/('|")\s*#\{(.*)\}\s*('|")/, '\2') # Remove strings and #{} around simple erb expressions (with no non-erb)
|
45
|
+
.gsub(/\s('|")$/, '\1') # Get rid of any extra whitespace.
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative '../../html_to_haml'
|
2
|
+
|
3
|
+
module HtmlToHaml::Html
|
4
|
+
class CommentConversionUseCase
|
5
|
+
HTML_COMMENT_REGEX = "<!--(.|\n)*?-->"
|
6
|
+
HTML_USING_HAML_COMMENT_SYNTAX = "^\s*\/"
|
7
|
+
|
8
|
+
def initialize(html)
|
9
|
+
@html = html
|
10
|
+
end
|
11
|
+
|
12
|
+
def convert
|
13
|
+
haml = @html.gsub(/#{HTML_COMMENT_REGEX}|#{HTML_USING_HAML_COMMENT_SYNTAX}/) do |comment|
|
14
|
+
case comment
|
15
|
+
when /#{HTML_COMMENT_REGEX}/
|
16
|
+
"\n/ #{format_html_comment_for_haml(comment: comment)}\n"
|
17
|
+
when /#{HTML_USING_HAML_COMMENT_SYNTAX}/
|
18
|
+
escape_misleading_forward_slash(comment: comment)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
haml.gsub(/\n\s*\n/, "\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def format_html_comment_for_haml(comment:)
|
27
|
+
comment.gsub(/\n\s*/, "\n/ ")[4..-4].strip
|
28
|
+
end
|
29
|
+
|
30
|
+
def escape_misleading_forward_slash(comment:)
|
31
|
+
comment.gsub(/^(\s*)\//, '\1\/')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../../html_to_haml'
|
2
|
+
require_relative './attribute_conversion_use_case'
|
3
|
+
require_relative './comment_conversion_use_case'
|
4
|
+
require_relative './default_conversion_use_case'
|
5
|
+
|
6
|
+
module HtmlToHaml::Html
|
7
|
+
class ConversionUseCase
|
8
|
+
def initialize(html, remove_whitespace: true)
|
9
|
+
@html = html
|
10
|
+
@remove_whitespace = remove_whitespace
|
11
|
+
end
|
12
|
+
|
13
|
+
def convert
|
14
|
+
html_with_haml_comments = CommentConversionUseCase.new(@html).convert
|
15
|
+
haml = DefaultConversionUseCase.new(html_with_haml_comments, remove_whitespace: @remove_whitespace).convert
|
16
|
+
AttributeConversionUseCase.instance.convert_attributes(html: haml)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative '../../html_to_haml'
|
2
|
+
require_relative '../../helpers/haml_whitespace_cleaner'
|
3
|
+
require_relative '../../tools/html/indentation_tracker'
|
4
|
+
|
5
|
+
module HtmlToHaml::Html
|
6
|
+
class DefaultConversionUseCase
|
7
|
+
include HtmlToHaml::HamlWhitespaceCleaner
|
8
|
+
|
9
|
+
HAML_SYMBOL_REGEX = ">\s*(\/|-|=)"
|
10
|
+
ERB_LINE_REGEX = "\n\s*(-|=).*$"
|
11
|
+
CLOSING_HTML_REGEX = "<\/.*?>"
|
12
|
+
# For self-closing html tags that aren't self-closing by default
|
13
|
+
SELF_CLOSING_HTML_REGEX = "\/>"
|
14
|
+
SELF_CLOSING_TAGS = %w(area base br col command embed hr img input keygen link meta param source track wbr)
|
15
|
+
|
16
|
+
def initialize(html, remove_whitespace: true)
|
17
|
+
# Since Haml uses whitespace in a way html doesn't, this starts by stripping
|
18
|
+
# whitespace to start the next gsub with a clean slate. Unless the caller opts
|
19
|
+
# out.
|
20
|
+
@html = if remove_whitespace
|
21
|
+
remove_html_whitespace(html: html)
|
22
|
+
else
|
23
|
+
html
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def convert
|
28
|
+
indentation_tracker = IndentationTracker.new(indentation_amount: HtmlToHaml::INDENTATION_AMOUNT)
|
29
|
+
haml = @html.gsub(/#{ERB_LINE_REGEX}|#{CLOSING_HTML_REGEX}|#{SELF_CLOSING_HTML_REGEX}|#{self_closing_tag_regex}|#{HAML_SYMBOL_REGEX}|<|>|\n/) do |matched_elem|
|
30
|
+
adjust_indentation_level(html: matched_elem, indentation_tracker: indentation_tracker)
|
31
|
+
start_of_line = "\n#{indentation_tracker.indentation}"
|
32
|
+
case matched_elem
|
33
|
+
when /#{ERB_LINE_REGEX}/
|
34
|
+
"#{start_of_line}#{matched_elem[1..-1]}"
|
35
|
+
when /#{self_closing_tag_regex}/
|
36
|
+
"#{start_of_line}%#{matched_elem[1..-1]}"
|
37
|
+
when /#{HAML_SYMBOL_REGEX}/
|
38
|
+
"#{start_of_line}#{matched_elem[1..-1].insert(-2, "\\")}"
|
39
|
+
when "<"
|
40
|
+
"#{start_of_line}%"
|
41
|
+
else
|
42
|
+
start_of_line
|
43
|
+
end
|
44
|
+
end
|
45
|
+
remove_haml_whitespace(haml: haml)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self_closing_tag_regex
|
49
|
+
"<#{SELF_CLOSING_TAGS.join('|<')}\\s"
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def adjust_indentation_level(html:, indentation_tracker:)
|
55
|
+
case html
|
56
|
+
when /#{CLOSING_HTML_REGEX}/
|
57
|
+
indentation_tracker.close_html_tag
|
58
|
+
when /#{self_closing_tag_regex}/
|
59
|
+
indentation_tracker.start_self_closing_tag
|
60
|
+
when ">", /#{HAML_SYMBOL_REGEX}/
|
61
|
+
indentation_tracker.start_html_tag
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def remove_html_whitespace(html:)
|
66
|
+
html.gsub(/^\s*/, "").delete("\n")
|
67
|
+
end
|
68
|
+
|
69
|
+
def remove_haml_whitespace(haml:)
|
70
|
+
haml.lstrip.gsub(/(\n\s*)\n\s*%/, '\1%').gsub(/\n\s*\n/, "\n")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative '../../html_to_haml'
|
2
|
+
require_relative '../../helpers/haml_whitespace_cleaner'
|
3
|
+
require_relative '../../tools/non_html_selector_blocks/indentation_tracker'
|
4
|
+
require_relative '../../tools/non_html_selector_blocks/tag_type_matchers'
|
5
|
+
|
6
|
+
module HtmlToHaml
|
7
|
+
module NonHtmlSelectorBlocks
|
8
|
+
class BasicConversionUseCase
|
9
|
+
include HtmlToHaml::HamlWhitespaceCleaner
|
10
|
+
include TagTypeMatchers
|
11
|
+
|
12
|
+
RUBY_HAML_REGEX = "\n\s*=.*\n"
|
13
|
+
|
14
|
+
def initialize(special_html)
|
15
|
+
@special_html = special_html
|
16
|
+
end
|
17
|
+
|
18
|
+
def convert
|
19
|
+
indentation_tracker = IndentationTracker.new(indented: false, adjust_whitespace: false)
|
20
|
+
haml = @special_html.gsub(/#{opening_tag_regex}|#{closing_tag_regex}|#{RUBY_HAML_REGEX}|(\n\s*)/) do |tag|
|
21
|
+
replace_tag_value(tag: tag, indentation_tracker: indentation_tracker)
|
22
|
+
end
|
23
|
+
remove_haml_whitespace(haml: haml)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def replace_tag_value(tag:, indentation_tracker:)
|
29
|
+
if opening_tag?(tag: tag, in_block: indentation_tracker.indented)
|
30
|
+
open_tag(tag: tag, indentation_tracker: indentation_tracker)
|
31
|
+
elsif closing_tag?(tag: tag, in_block: indentation_tracker.indented)
|
32
|
+
close_tag(indentation_tracker: indentation_tracker)
|
33
|
+
elsif use_string_interpolation?(tag: tag, in_block: indentation_tracker.indented)
|
34
|
+
string_interpolated_ruby(tag: tag)
|
35
|
+
elsif adjust_whitespace?(tag: tag, indentation_tracker: indentation_tracker)
|
36
|
+
"#{tag}#{indentation}"
|
37
|
+
else
|
38
|
+
tag
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def open_tag(tag:, indentation_tracker:)
|
43
|
+
indentation_tracker.indent
|
44
|
+
opening_tag(tag: tag)
|
45
|
+
end
|
46
|
+
|
47
|
+
def opening_tag(tag:)
|
48
|
+
":#{tag_type(tag: tag)}\n#{indentation}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def close_tag(indentation_tracker:)
|
52
|
+
indentation_tracker.outdent
|
53
|
+
"\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
def use_string_interpolation?(tag:, in_block:)
|
57
|
+
in_block && tag =~ /#{RUBY_HAML_REGEX}/
|
58
|
+
end
|
59
|
+
|
60
|
+
def string_interpolated_ruby(tag:)
|
61
|
+
"\#{#{tag.strip[1..-1].strip}}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def adjust_whitespace?(indentation_tracker:, tag:)
|
65
|
+
tag_indented = tag.include?(indentation)
|
66
|
+
tag =~ /\n/ && indentation_tracker.adjust_whitespace?(reset_value: !tag_indented)
|
67
|
+
end
|
68
|
+
|
69
|
+
def indentation
|
70
|
+
" " * HtmlToHaml::INDENTATION_AMOUNT
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'basic_conversion_use_case'
|
2
|
+
|
3
|
+
module HtmlToHaml
|
4
|
+
module NonHtmlSelectorBlocks
|
5
|
+
class ScriptConversionUseCase < BasicConversionUseCase
|
6
|
+
HTML_TAG_NAME = "script"
|
7
|
+
DEFAULT_TAG_TYPE = "javascript"
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def opening_tag?(tag:, in_block:)
|
12
|
+
super && tag !~ (/\ssrc=['"]/)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: html-to-haml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Natasha Hull-Richter
|
8
|
+
autorequire: html_to_haml/converter.rb
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |-
|
14
|
+
This app takes in html and erb code and turns it into haml. It supports script and style tags
|
15
|
+
and html comments as well.
|
16
|
+
email: nhull-richter@pivotal.io
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/html_to_haml/converter.rb
|
22
|
+
- lib/html_to_haml/helpers/haml_whitespace_cleaner.rb
|
23
|
+
- lib/html_to_haml/html_to_haml.rb
|
24
|
+
- lib/html_to_haml/tools/erb/control_flow_matcher.rb
|
25
|
+
- lib/html_to_haml/tools/erb/indentation_tracker.rb
|
26
|
+
- lib/html_to_haml/tools/html/attribute_handler.rb
|
27
|
+
- lib/html_to_haml/tools/html/indentation_tracker.rb
|
28
|
+
- lib/html_to_haml/tools/non_html_selector_blocks/indentation_tracker.rb
|
29
|
+
- lib/html_to_haml/tools/non_html_selector_blocks/tag_type_matchers.rb
|
30
|
+
- lib/html_to_haml/use_cases/erb/basic_conversion_use_case.rb
|
31
|
+
- lib/html_to_haml/use_cases/erb/indentation_conversion_use_case.rb
|
32
|
+
- lib/html_to_haml/use_cases/html/attribute_conversion_use_case.rb
|
33
|
+
- lib/html_to_haml/use_cases/html/comment_conversion_use_case.rb
|
34
|
+
- lib/html_to_haml/use_cases/html/conversion_use_case.rb
|
35
|
+
- lib/html_to_haml/use_cases/html/default_conversion_use_case.rb
|
36
|
+
- lib/html_to_haml/use_cases/non_html_selector_blocks/basic_conversion_use_case.rb
|
37
|
+
- lib/html_to_haml/use_cases/non_html_selector_blocks/script_conversion_use_case.rb
|
38
|
+
- lib/html_to_haml/use_cases/non_html_selector_blocks/style_conversion_use_case.rb
|
39
|
+
homepage: https://github.com/NatashaHull/html-to-haml.git
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '2.0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.5.1
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: A program that turns html.erb code into haml code.
|
63
|
+
test_files: []
|