html2slim2 0.3.0
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/README.md +53 -0
- data/bin/erb2slim +7 -0
- data/bin/html2slim +7 -0
- data/lib/html2slim/command.rb +108 -0
- data/lib/html2slim/converter.rb +53 -0
- data/lib/html2slim/nokogiri_monkeypatches.rb +138 -0
- data/lib/html2slim/version.rb +3 -0
- data/lib/html2slim.rb +12 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c6ff64c9a5fd086b72c5db5c43df4416f4408052a1bf5e35f920bf1a26df46c5
|
4
|
+
data.tar.gz: 92df93ab18679e51386f70c9045fd168b5507094a070e5a5e219dfbb60795dbe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2f56c1abc267eb88573528a0546bf34265da07578923854fc1f82ecbe92a4af262ae6a88059d8d1680d1fda5a3fa19d96245a15a9e5082958679e88d04c373c7
|
7
|
+
data.tar.gz: bd67aa82f0336fa656c93ac391db4c1b6529ed1b5e288c5295c8601c9ef3a501a10f51b4f5a93b7f9608a03a17566fe100b0468712b7b38b1a212bb2df23738b
|
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# HTML2Slim2
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
<!--
|
6
|
+
[](https://travis-ci.org/slim-template/html2slim)
|
7
|
+
|
8
|
+
[](https://codeclimate.com/github/slim-template/html2slim)
|
9
|
+
-->
|
10
|
+
|
11
|
+
Script for converting HTML and ERB files to [slim](http://slim-lang.com/).
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
You may convert files using the included executables `html2slim` and `erb2slim`.
|
16
|
+
|
17
|
+
# html2slim -h
|
18
|
+
|
19
|
+
Usage: html2slim INPUT_FILENAME_OR_DIRECTORY [OUTPUT_FILENAME_OR_DIRECTORY] [options]
|
20
|
+
--trace Show a full traceback on error
|
21
|
+
-d, --delete Delete HTML files
|
22
|
+
-h, --help Show this message
|
23
|
+
-v, --version Print version
|
24
|
+
|
25
|
+
# erb2slim -h
|
26
|
+
|
27
|
+
Usage: erb2slim INPUT_FILENAME_OR_DIRECTORY [OUTPUT_FILENAME_OR_DIRECTORY] [options]
|
28
|
+
--trace Show a full traceback on error
|
29
|
+
-d, --delete Delete ERB files
|
30
|
+
-h, --help Show this message
|
31
|
+
-v, --version Print version
|
32
|
+
|
33
|
+
Alternatively, to convert files or strings on the fly in your application, you may do so by calling `HTML2Slim.convert!(file, format)` where format is either `:html` or `:erb`.
|
34
|
+
|
35
|
+
## License
|
36
|
+
|
37
|
+
This project is released under the MIT license.
|
38
|
+
|
39
|
+
## Author
|
40
|
+
|
41
|
+
[Maiz Lulkin](https://github.com/joaomilho) and [contributors](https://github.com/sanadan/html2slim/graphs/contributors)
|
42
|
+
|
43
|
+
## Maintained repo
|
44
|
+
|
45
|
+
[https://github.com/sanadan/html2slim](https://github.com/sanadan/html2slim)
|
46
|
+
|
47
|
+
## OFFICIAL REPO
|
48
|
+
|
49
|
+
[https://github.com/slim-template/html2slim](https://github.com/slim-template/html2slim)
|
50
|
+
|
51
|
+
## GOOD TO KNOW
|
52
|
+
|
53
|
+
ERB requires a full Ruby parser, so it doesn't really work for all use cases, but it's certainly helpful.
|
data/bin/erb2slim
ADDED
data/bin/html2slim
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'html2slim'
|
3
|
+
|
4
|
+
module HTML2Slim
|
5
|
+
class Command
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@args = args
|
9
|
+
@options = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
@opts = OptionParser.new(&method(:set_opts))
|
14
|
+
@opts.parse!(@args)
|
15
|
+
process!
|
16
|
+
exit 0
|
17
|
+
rescue Exception => ex
|
18
|
+
raise ex if @options[:trace] || SystemExit === ex
|
19
|
+
$stderr.print "#{ex.class}: " if ex.class != RuntimeError
|
20
|
+
$stderr.puts ex.message
|
21
|
+
$stderr.puts ' Use --trace for backtrace.'
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def format
|
28
|
+
@format ||= (self.class.to_s =~ /ERB/ ? :erb : :html)
|
29
|
+
end
|
30
|
+
|
31
|
+
def command_name
|
32
|
+
@command_name ||= format == :html ? "html2slim" : "erb2slim"
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_opts(opts)
|
36
|
+
opts.banner = "Usage: #{command_name} INPUT_FILENAME_OR_DIRECTORY [OUTPUT_FILENAME_OR_DIRECTORY] [options]"
|
37
|
+
|
38
|
+
opts.on('--trace', :NONE, 'Show a full traceback on error') do
|
39
|
+
@options[:trace] = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
43
|
+
puts opts
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on_tail('-v', '--version', 'Print version') do
|
48
|
+
puts "#{command_name} #{HTML2Slim::VERSION}"
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on('-d', '--delete', "Delete #{format.upcase} files") do
|
53
|
+
@options[:delete] = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def process!
|
58
|
+
args = @args.dup
|
59
|
+
|
60
|
+
@options[:input] = file = args.shift
|
61
|
+
@options[:output] = destination = args.shift
|
62
|
+
|
63
|
+
@options[:input] = file = "-" unless file
|
64
|
+
|
65
|
+
if File.directory?(@options[:input])
|
66
|
+
Dir["#{@options[:input]}/**/*.#{format}"].each { |file| _process(file, destination) }
|
67
|
+
else
|
68
|
+
_process(file, destination)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def input_is_dir?
|
75
|
+
File.directory? @options[:input]
|
76
|
+
end
|
77
|
+
|
78
|
+
def _process(file, destination = nil)
|
79
|
+
require 'fileutils'
|
80
|
+
slim_file = file.sub(/\.#{format}/, '.slim')
|
81
|
+
|
82
|
+
if input_is_dir? && destination
|
83
|
+
FileUtils.mkdir_p(File.dirname(slim_file).sub(@options[:input].chomp('/'), destination))
|
84
|
+
slim_file.sub!(@options[:input].chomp('/'), destination)
|
85
|
+
else
|
86
|
+
slim_file = destination || slim_file
|
87
|
+
end
|
88
|
+
|
89
|
+
fail(ArgumentError, "Source and destination files can't be the same.") if @options[:input] != '-' && file == slim_file
|
90
|
+
|
91
|
+
in_file = if @options[:input] == "-"
|
92
|
+
$stdin
|
93
|
+
else
|
94
|
+
File.open(file, 'r')
|
95
|
+
end
|
96
|
+
|
97
|
+
@options[:output] = slim_file && slim_file != '-' ? File.open(slim_file, 'w') : $stdout
|
98
|
+
@options[:output].puts HTML2Slim.convert!(in_file, format)
|
99
|
+
@options[:output].close
|
100
|
+
|
101
|
+
File.delete(file) if @options[:delete]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
class HTMLCommand < Command
|
105
|
+
end
|
106
|
+
class ERBCommand < Command
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'nokogiri_monkeypatches'
|
2
|
+
|
3
|
+
module HTML2Slim
|
4
|
+
class Converter
|
5
|
+
def initialize(html)
|
6
|
+
nokogiri = html[..1] == '<!' ? Nokogiri.parse(html) : Nokogiri::HTML.fragment(html)
|
7
|
+
@slim = nokogiri.to_slim
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
@slim
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class HTMLConverter < Converter
|
16
|
+
def initialize(file)
|
17
|
+
html = file.read
|
18
|
+
super(html)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ERBConverter < Converter
|
23
|
+
def initialize(file)
|
24
|
+
erb = file.read
|
25
|
+
|
26
|
+
erb.gsub!(/<%(.+?)\s*\{\s*(\|.+?\|)?\s*%>/) { %(<%#{$1} do #{$2}%>) }
|
27
|
+
# case, if, for, unless, until, while, and blocks...
|
28
|
+
erb.gsub!(/<%(-\s+)?((\s*(case|if|for|unless|until|while) .+?)|.+?do\s*(\|.+?\|)?\s*)-?%>/) do
|
29
|
+
%(<ruby code="#{escape($2)}">)
|
30
|
+
end
|
31
|
+
# else
|
32
|
+
erb.gsub!(/<%-?\s*else\s*-?%>/, %(</ruby><ruby code="else">))
|
33
|
+
# elsif
|
34
|
+
erb.gsub!(/<%-?\s*(elsif .+?)\s*-?%>/) { %(</ruby><ruby code="#{escape($1)}">) }
|
35
|
+
# when
|
36
|
+
erb.gsub!(/<%-?\s*(when .+?)\s*-?%>/) { %(</ruby><ruby code="#{escape($1)}">) }
|
37
|
+
erb.gsub!(/<%\s*(end|}|end\s+-)\s*%>/, %(</ruby>))
|
38
|
+
erb.gsub!(/<%-?\n?(.+?)\s*-?%>/m) { %(<ruby code="#{escape($1)}"></ruby>) }
|
39
|
+
|
40
|
+
super(erb)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def escape(str)
|
46
|
+
str.gsub!('&', '&')
|
47
|
+
str.gsub!('"', '"')
|
48
|
+
str.gsub!("\n", '\n')
|
49
|
+
str.gsub!('<', '<')
|
50
|
+
str
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
class Nokogiri::XML::Text
|
4
|
+
def to_slim(lvl = 0)
|
5
|
+
str = escape(content)
|
6
|
+
return nil if str.strip.empty?
|
7
|
+
|
8
|
+
(' ' * lvl) + %(| #{str.gsub(/\s+/, ' ')})
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def escape(str)
|
14
|
+
str.gsub!('&', '&')
|
15
|
+
str.gsub!('©', '©')
|
16
|
+
str.gsub!("\u00A0", ' ')
|
17
|
+
str.gsub!('»', '»')
|
18
|
+
str.gsub!('<', '<')
|
19
|
+
str.gsub!('>', '>')
|
20
|
+
str
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Nokogiri::XML::DTD
|
25
|
+
def to_slim(_lvl = 0)
|
26
|
+
if to_xml.include?('xml')
|
27
|
+
to_xml.include?('iso-8859-1') ? 'doctype xml ISO-88591' : 'doctype xml'
|
28
|
+
elsif to_xml.include?('XHTML') || to_xml.include?('HTML 4.01')
|
29
|
+
available_versions = Regexp.union ['Basic', '1.1', 'strict', 'Frameset', 'Mobile', 'Transitional']
|
30
|
+
version = to_xml.match(available_versions).to_s.downcase
|
31
|
+
"doctype #{version}"
|
32
|
+
else
|
33
|
+
'doctype html'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Nokogiri::XML::Element
|
39
|
+
BLANK_RE = /\A[[:space:]]*\z/.freeze
|
40
|
+
|
41
|
+
def slim(lvl = 0)
|
42
|
+
r = ' ' * lvl
|
43
|
+
|
44
|
+
return r + slim_ruby_code(r) if ruby?
|
45
|
+
|
46
|
+
r += name unless skip_tag_name?
|
47
|
+
r += slim_id
|
48
|
+
r += slim_class
|
49
|
+
r += slim_attributes
|
50
|
+
r
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_slim(lvl = 0)
|
54
|
+
if children.count.positive?
|
55
|
+
%(#{slim(lvl)}\n#{children.filter_map { |c| c.to_slim(lvl + 1) }.join("\n")})
|
56
|
+
else
|
57
|
+
slim(lvl)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def slim_ruby_code(r)
|
64
|
+
(code.strip[0] == '=' ? '' : '- ') + code.strip.gsub('\\n', "\n#{r}- ")
|
65
|
+
end
|
66
|
+
|
67
|
+
def code
|
68
|
+
attributes['code'].to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def skip_tag_name?
|
72
|
+
div? && (has_id? || has_class?)
|
73
|
+
end
|
74
|
+
|
75
|
+
def slim_id
|
76
|
+
has_id? ? "##{self['id']}" : ''
|
77
|
+
end
|
78
|
+
|
79
|
+
def slim_class
|
80
|
+
has_class? ? ".#{self['class'].to_s.strip.split(/\s+/).join('.')}" : ''
|
81
|
+
end
|
82
|
+
|
83
|
+
def slim_attributes
|
84
|
+
remove_attribute('class')
|
85
|
+
remove_attribute('id')
|
86
|
+
has_attributes? ? "[#{attributes_as_html.to_s.strip}]" : ''
|
87
|
+
end
|
88
|
+
|
89
|
+
def has_attributes? # rubocop:disable Naming/PredicateName
|
90
|
+
attributes.to_hash.any?
|
91
|
+
end
|
92
|
+
|
93
|
+
def has_id? # rubocop:disable Naming/PredicateName
|
94
|
+
has_attribute?('id') && !(BLANK_RE === self['id'])
|
95
|
+
end
|
96
|
+
|
97
|
+
def has_class? # rubocop:disable Naming/PredicateName
|
98
|
+
has_attribute?('class') && !(BLANK_RE === self['class'])
|
99
|
+
end
|
100
|
+
|
101
|
+
def ruby?
|
102
|
+
name == 'ruby'
|
103
|
+
end
|
104
|
+
|
105
|
+
def div?
|
106
|
+
name == 'div'
|
107
|
+
end
|
108
|
+
|
109
|
+
def html_quote(str)
|
110
|
+
"\"#{str.gsub('"', '\\"')}\""
|
111
|
+
end
|
112
|
+
|
113
|
+
def attributes_as_html
|
114
|
+
attributes.map do |aname, aval|
|
115
|
+
" #{aname}" + (aval ? "=#{html_quote aval.to_s}" : '')
|
116
|
+
end.join
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Nokogiri::XML::DocumentFragment
|
121
|
+
def to_slim
|
122
|
+
if children.count.positive?
|
123
|
+
children.filter_map(&:to_slim).join("\n")
|
124
|
+
else
|
125
|
+
''
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Nokogiri::XML::Document
|
131
|
+
def to_slim
|
132
|
+
if children.count.positive?
|
133
|
+
children.filter_map(&:to_slim).join("\n")
|
134
|
+
else
|
135
|
+
''
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/html2slim.rb
ADDED
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: html2slim2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- sanadan
|
8
|
+
- Maiz Lulkin
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-05-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop-rails-omakase
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: slim
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Convert HTML to Slim templates. Because HTML sux and Slim rules. That's
|
84
|
+
why.
|
85
|
+
email:
|
86
|
+
- jecy00@gmail.com
|
87
|
+
- maiz@lulk.in
|
88
|
+
executables:
|
89
|
+
- erb2slim
|
90
|
+
- html2slim
|
91
|
+
extensions: []
|
92
|
+
extra_rdoc_files:
|
93
|
+
- README.md
|
94
|
+
files:
|
95
|
+
- README.md
|
96
|
+
- bin/erb2slim
|
97
|
+
- bin/html2slim
|
98
|
+
- lib/html2slim.rb
|
99
|
+
- lib/html2slim/command.rb
|
100
|
+
- lib/html2slim/converter.rb
|
101
|
+
- lib/html2slim/nokogiri_monkeypatches.rb
|
102
|
+
- lib/html2slim/version.rb
|
103
|
+
homepage: https://github.com/sanadan/html2slim
|
104
|
+
licenses: []
|
105
|
+
metadata: {}
|
106
|
+
rdoc_options:
|
107
|
+
- "--charset=UTF-8"
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubygems_version: 3.6.7
|
122
|
+
specification_version: 4
|
123
|
+
summary: HTML to Slim converter.
|
124
|
+
test_files: []
|