antwort 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +19 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +249 -0
- data/Gemfile +3 -0
- data/Guardfile +14 -0
- data/README.md +108 -0
- data/Rakefile +14 -0
- data/antwort.gemspec +45 -0
- data/bin/antwort +5 -0
- data/lib/antwort.rb +13 -0
- data/lib/antwort/builder.rb +8 -0
- data/lib/antwort/builder/builder.rb +104 -0
- data/lib/antwort/builder/email.rb +61 -0
- data/lib/antwort/builder/flattener.rb +37 -0
- data/lib/antwort/builder/helpers/logic.rb +82 -0
- data/lib/antwort/builder/helpers/sanitizers.rb +29 -0
- data/lib/antwort/builder/partial.rb +80 -0
- data/lib/antwort/builder/style.rb +59 -0
- data/lib/antwort/cli.rb +7 -0
- data/lib/antwort/cli/cli.rb +275 -0
- data/lib/antwort/cli/helpers.rb +44 -0
- data/lib/antwort/cli/send.rb +79 -0
- data/lib/antwort/cli/upload.rb +89 -0
- data/lib/antwort/helpers.rb +19 -0
- data/lib/antwort/server.rb +70 -0
- data/lib/antwort/server/assets.rb +23 -0
- data/lib/antwort/server/helpers.rb +67 -0
- data/lib/antwort/server/markup.rb +39 -0
- data/lib/antwort/version.rb +3 -0
- data/spec/builder/builder_spec.rb +30 -0
- data/spec/builder/email_spec.rb +21 -0
- data/spec/builder/flattener_spec.rb +64 -0
- data/spec/builder/helpers_logic_spec.rb +244 -0
- data/spec/builder/partial_spec.rb +87 -0
- data/spec/builder/style_spec.rb +66 -0
- data/spec/cli/helpers_spec.rb +60 -0
- data/spec/cli/upload_spec.rb +47 -0
- data/spec/cli_spec.rb +46 -0
- data/spec/fixtures/assets/images/1-demo/placeholder.png +0 -0
- data/spec/fixtures/assets/images/newsletter/placeholder.png +0 -0
- data/spec/fixtures/assets/images/shared/placeholder-grey.png +0 -0
- data/spec/fixtures/assets/images/shared/placeholder-white.png +0 -0
- data/spec/fixtures/build/demo-123456/build.html +7 -0
- data/spec/fixtures/build/demo-123457/build.html +7 -0
- data/spec/fixtures/build/demo-bar-123/build.html +7 -0
- data/spec/fixtures/build/foo-1/build.html +7 -0
- data/spec/fixtures/data/1-demo.yml +3 -0
- data/spec/fixtures/emails/1-demo/index.html.erb +9 -0
- data/spec/fixtures/emails/2-no-layout/index.html.erb +6 -0
- data/spec/fixtures/emails/3-no-title/index.html.erb +1 -0
- data/spec/fixtures/views/404.html.erb +1 -0
- data/spec/fixtures/views/index.html.erb +14 -0
- data/spec/fixtures/views/layout.erb +8 -0
- data/spec/fixtures/views/server.erb +5 -0
- data/spec/server_spec.rb +54 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/capture.rb +17 -0
- data/template/email/css/include.scss +5 -0
- data/template/email/css/inline.scss +5 -0
- data/template/email/email.html.erb +11 -0
- data/template/email/images/.empty_directory +0 -0
- data/template/project/.env.sample +21 -0
- data/template/project/.gitignore.tt +17 -0
- data/template/project/.ruby-version +1 -0
- data/template/project/Gemfile.tt +9 -0
- data/template/project/Guardfile +9 -0
- data/template/project/assets/css/demo/include.scss +3 -0
- data/template/project/assets/css/demo/inline.scss +33 -0
- data/template/project/assets/css/server.scss +167 -0
- data/template/project/assets/css/shared/_base.scss +64 -0
- data/template/project/assets/css/shared/_mixins.scss +25 -0
- data/template/project/assets/css/shared/_reset.scss +59 -0
- data/template/project/assets/css/shared/_vars.scss +12 -0
- data/template/project/assets/css/shared/include.scss +23 -0
- data/template/project/assets/css/shared/inline.scss +9 -0
- data/template/project/assets/images/.gitkeep +0 -0
- data/template/project/assets/images/shared/placeholder.png +0 -0
- data/template/project/build/.empty_directory +0 -0
- data/template/project/data/.empty_directory +0 -0
- data/template/project/data/config.yml +3 -0
- data/template/project/data/demo.yml +4 -0
- data/template/project/emails/demo/_partial.html.erb +9 -0
- data/template/project/emails/demo/index.html.erb +54 -0
- data/template/project/views/404.html.erb +8 -0
- data/template/project/views/index.html.erb +18 -0
- data/template/project/views/layout.erb +38 -0
- data/template/project/views/markup/_button.html.erb +5 -0
- data/template/project/views/markup/_image_tag.html.erb +10 -0
- data/template/project/views/server.erb +32 -0
- metadata +443 -0
data/antwort.gemspec
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'antwort/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'antwort'
|
8
|
+
s.version = Antwort::VERSION
|
9
|
+
s.summary = 'Antwort Generator'
|
10
|
+
s.description = 'E-Mail development, build and test system.'
|
11
|
+
s.authors = ['Julie Ng']
|
12
|
+
s.email = 'hello@antwort.co'
|
13
|
+
s.homepage = 'https://antwort.co'
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
s.required_ruby_version = '~> 2.0'
|
21
|
+
|
22
|
+
s.add_runtime_dependency 'rake'
|
23
|
+
s.add_runtime_dependency 'thor'
|
24
|
+
s.add_runtime_dependency 'sinatra'
|
25
|
+
s.add_runtime_dependency 'sinatra-contrib'
|
26
|
+
s.add_runtime_dependency 'sinatra-partial'
|
27
|
+
s.add_runtime_dependency 'dotenv'
|
28
|
+
s.add_runtime_dependency 'htmlentities'
|
29
|
+
s.add_runtime_dependency 'mail'
|
30
|
+
s.add_runtime_dependency 'nokogiri'
|
31
|
+
s.add_runtime_dependency 'roadie', '>= 3.0.0'
|
32
|
+
|
33
|
+
s.add_runtime_dependency 'sass'
|
34
|
+
s.add_runtime_dependency 'sprockets', '~> 3.0'
|
35
|
+
|
36
|
+
s.add_runtime_dependency 'thin'
|
37
|
+
s.add_runtime_dependency 'rack-livereload'
|
38
|
+
s.add_runtime_dependency 'guard-livereload'
|
39
|
+
s.add_runtime_dependency 'fog'
|
40
|
+
|
41
|
+
s.add_development_dependency 'pry'
|
42
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
43
|
+
s.add_development_dependency 'rspec-its'
|
44
|
+
s.add_development_dependency 'guard-rspec'
|
45
|
+
end
|
data/bin/antwort
ADDED
data/lib/antwort.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'antwort/builder/helpers/logic'
|
2
|
+
require 'antwort/builder/helpers/sanitizers'
|
3
|
+
require 'antwort/builder/builder'
|
4
|
+
require 'antwort/builder/email'
|
5
|
+
require 'antwort/builder/partial'
|
6
|
+
require 'antwort/builder/flattener'
|
7
|
+
require 'antwort/builder/style'
|
8
|
+
require 'antwort/cli/helpers'
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tilt'
|
3
|
+
require 'roadie'
|
4
|
+
require 'thor/shell'
|
5
|
+
|
6
|
+
module Antwort
|
7
|
+
class Builder
|
8
|
+
include Thor::Shell
|
9
|
+
include Antwort::Helpers
|
10
|
+
include Antwort::CLIHelpers
|
11
|
+
include Antwort::LogicHelpers
|
12
|
+
include Antwort::MarkupSanitizers
|
13
|
+
|
14
|
+
attr_reader :template_name, :build_id, :build_dir, :markup_dir, :source_dir, :scss_dir, :asset_server, :css, :css_style
|
15
|
+
|
16
|
+
def initialize(attrs = {})
|
17
|
+
attrs = symbolize_keys!(attrs)
|
18
|
+
@template_name = attrs[:email]
|
19
|
+
@build_id = attrs[:id]
|
20
|
+
@build_dir = "./build/#{template_name}-#{build_id}"
|
21
|
+
@markup_dir = "#{build_dir}/source"
|
22
|
+
@source_dir = "./emails/#{template_name}"
|
23
|
+
@scss_dir = "./assets/css/#{template_name}"
|
24
|
+
@css_style = attrs[:'css-style'].to_sym
|
25
|
+
@asset_server = ENV['ASSET_SERVER'] || '/assets'
|
26
|
+
post_initialize(attrs)
|
27
|
+
end
|
28
|
+
|
29
|
+
def post_initialize(*)
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_build_directories
|
34
|
+
return if Dir.exist? build_dir
|
35
|
+
Dir.mkdir "build" unless Dir.exist? "./build"
|
36
|
+
Dir.mkdir(build_dir)
|
37
|
+
Dir.mkdir("#{build_dir}/source")
|
38
|
+
end
|
39
|
+
|
40
|
+
def build
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_css
|
44
|
+
css_file = "#{markup_dir}/inline.css"
|
45
|
+
build_css unless File.file? css_file
|
46
|
+
css = File.read(css_file)
|
47
|
+
css
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_css
|
51
|
+
compile_scss(source: "#{scss_dir}/inline.scss", destination: "#{markup_dir}/inline.css")
|
52
|
+
compile_scss(source: "#{scss_dir}/include.scss", destination: "#{markup_dir}/include.css")
|
53
|
+
@css = load_css
|
54
|
+
end
|
55
|
+
|
56
|
+
def compile_scss(attrs = {})
|
57
|
+
source_file = attrs[:source]
|
58
|
+
destination_file = attrs[:destination]
|
59
|
+
|
60
|
+
if File.file? source_file
|
61
|
+
content = Tilt::ScssTemplate.new(source_file, style: @css_style).render
|
62
|
+
create_file(content: content, path: destination_file)
|
63
|
+
else
|
64
|
+
say 'Build failed. ', :red
|
65
|
+
say "#{source_file}.scss for #{template_name} not found."
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_file(attrs)
|
70
|
+
content = attrs[:content]
|
71
|
+
path = attrs[:path]
|
72
|
+
|
73
|
+
file = File.new(path, 'w')
|
74
|
+
file.write(content)
|
75
|
+
file.close
|
76
|
+
say ' create ', :green
|
77
|
+
say path.gsub(/\A.\//, '')
|
78
|
+
file
|
79
|
+
end
|
80
|
+
|
81
|
+
def use_asset_server(markup = '')
|
82
|
+
replaced = "#{asset_server}/"
|
83
|
+
output = markup.gsub('/assets/', replaced)
|
84
|
+
output
|
85
|
+
end
|
86
|
+
|
87
|
+
def flatten_inlined_css(markup)
|
88
|
+
copy = ''
|
89
|
+
|
90
|
+
# loop through lines so we have the line number
|
91
|
+
markup.lines.each_with_index do |line, i|
|
92
|
+
f = Flattener.new(line).flatten
|
93
|
+
if f.flattened?
|
94
|
+
say " flattened CSS #{f.flattened_keys} ", :yellow
|
95
|
+
say "#{i}: #{f.source.strip}"
|
96
|
+
copy << f.flattened
|
97
|
+
else
|
98
|
+
copy << f.source
|
99
|
+
end
|
100
|
+
end
|
101
|
+
copy
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Antwort
|
2
|
+
class EmailBuilder < Builder
|
3
|
+
attr_accessor :html_markup
|
4
|
+
|
5
|
+
def post_initialize(*)
|
6
|
+
app ||= Antwort::Server.new
|
7
|
+
mock ||= Rack::MockRequest.new(app)
|
8
|
+
|
9
|
+
request = mock.get("/template/#{template_name}")
|
10
|
+
if request.status == 200
|
11
|
+
create_build_directories
|
12
|
+
@html_markup = remove_livereload(request.body)
|
13
|
+
@inlined_file = "#{build_dir}/#{template_name}.html"
|
14
|
+
else
|
15
|
+
say 'Error: ', :red
|
16
|
+
say "Template '#{template_name}' not found."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def build
|
21
|
+
unless html_markup.nil?
|
22
|
+
build_css
|
23
|
+
build_html
|
24
|
+
inline_css
|
25
|
+
end
|
26
|
+
|
27
|
+
until File.exist?(@inlined_file)
|
28
|
+
sleep 1
|
29
|
+
end
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_html
|
34
|
+
markup = html_markup
|
35
|
+
markup = markup.gsub("/assets/#{template_name}/inline.css", 'inline.css')
|
36
|
+
.gsub("/assets/#{template_name}/include.css", 'include.css')
|
37
|
+
create_file(content: markup, path: "#{markup_dir}/#{template_name}.html")
|
38
|
+
end
|
39
|
+
|
40
|
+
def inline_css
|
41
|
+
markup = preserve_nbsps(html_markup)
|
42
|
+
document = Roadie::Document.new(markup)
|
43
|
+
|
44
|
+
document.asset_providers << Roadie::NullProvider.new
|
45
|
+
document.add_css(css)
|
46
|
+
|
47
|
+
inlined = restore_nbsps(document.transform)
|
48
|
+
inlined = cleanup_markup(inlined)
|
49
|
+
lnlined = remove_roadie_flags(inlined)
|
50
|
+
inlined = remove_excessive_newlines(inlined)
|
51
|
+
inlined = flatten_inlined_css(inlined)
|
52
|
+
create_file(content: inlined, path: @inlined_file)
|
53
|
+
end
|
54
|
+
|
55
|
+
def cleanup_markup(markup)
|
56
|
+
content = use_asset_server(markup)
|
57
|
+
content = add_included_css(content)
|
58
|
+
content
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Antwort
|
2
|
+
class Flattener
|
3
|
+
|
4
|
+
attr_reader :source, :styles, :flattened, :flattened_keys
|
5
|
+
|
6
|
+
def initialize(source='')
|
7
|
+
@source = source
|
8
|
+
@styles = find_styles
|
9
|
+
@flattened_keys = []
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def flatten
|
14
|
+
copy = String.new(@source)
|
15
|
+
@styles.each do |m|
|
16
|
+
style = Antwort::Style.new(m)
|
17
|
+
@flattened_keys.concat(style.duplicate_keys) if style.duplicates?
|
18
|
+
copy.sub!(m, style.flattened_str) # flattened_str removes extra spaces and trailing semicolons
|
19
|
+
end
|
20
|
+
|
21
|
+
@flattened = copy
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def flattened?
|
26
|
+
@flattened_keys.length > 0
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def find_styles
|
32
|
+
a = []
|
33
|
+
@source.scan(/style="(.+?)"/).each { |match| a.push(match.first) } # first is regex group `(.+?)`
|
34
|
+
a
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Antwort
|
2
|
+
module LogicHelpers
|
3
|
+
|
4
|
+
def preserve_nbsps(html = '')
|
5
|
+
html.gsub(/ /, '%nbspace%')
|
6
|
+
end
|
7
|
+
|
8
|
+
def restore_nbsps(html = '')
|
9
|
+
html.gsub(/%nbspace%/, ' ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def preserve_logic(html = '')
|
13
|
+
html = preserve_comments(html)
|
14
|
+
html = preserve_conditionals(html) # conditionals before loops, in case we have them inside loops
|
15
|
+
html = preserve_loops(html)
|
16
|
+
html = preserve_variables(html)
|
17
|
+
html = preserve_assignments(html)
|
18
|
+
html = convert_partials_to_includes(html)
|
19
|
+
html = convert_helper_wrappers(html)
|
20
|
+
html = preserve_leftover_statements(html) # must be last
|
21
|
+
html
|
22
|
+
end
|
23
|
+
|
24
|
+
def preserve_conditionals(html = '')
|
25
|
+
html.gsub(%r{<%\s*if (.*?)%>}, '{% if \1 %}') # if
|
26
|
+
.gsub(%r{<%\s*unless (.*?)%>}, '{% if !( \1) %}') # unless
|
27
|
+
.gsub(%r{<%\s*elsif(.*?)%>}, '{% elseif\1%}') # elsif
|
28
|
+
.gsub(%r{<%\s*else\s*%>}, '{% else %}') # else
|
29
|
+
.gsub(%r{<%\s*end\s*%>}, '{% end %}') # end
|
30
|
+
.gsub(/[ \t]{2,}%}/, ' %}') # remove extra white space, e.g. {% else %}
|
31
|
+
end
|
32
|
+
|
33
|
+
def preserve_loops(html = '')
|
34
|
+
html.gsub(%r{<%\s+(.*)(\.each\s+do\s+\|)\s*(\S+)\s*(\|\s+)%>}, '{% for \3 in \1 %}')
|
35
|
+
.gsub(%r{<%\s+(.*)(\.each_with_index\s+do\s+\|)\s*(\S+)\s*,\s*(\S+)\s*(\|\s+)%>}, '{% for \3 in \1 with: {@index: \4} %}')
|
36
|
+
.gsub(%r{<%\s*end\s*%>}, '{% end %}')
|
37
|
+
end
|
38
|
+
|
39
|
+
def preserve_comments(html = '')
|
40
|
+
html.gsub(%r{<%[ \t]*#(.*)%>},'{#\1#}')
|
41
|
+
.gsub(/{#([^=\s])/, '{# \1') # {#foo #} ->{# foo #}
|
42
|
+
end
|
43
|
+
|
44
|
+
def preserve_variables(html = '')
|
45
|
+
html.gsub(/\[:(.*?)\]/, '.\1') # a[:b][:c] -> a.b.c
|
46
|
+
.gsub(/\['(.*?)'\]/, '.\1') # a['b'] -> a.b
|
47
|
+
.gsub(/\["(.*?)"\]/, '.\1') # a["b"] -> a.b
|
48
|
+
.gsub(%r{<%=(.*?)%>}, '{{\1}}') # assume leftover erb output are variables
|
49
|
+
end
|
50
|
+
|
51
|
+
def preserve_assignments(html = '')
|
52
|
+
html.gsub(%r{<%\s+([A-Za-z0-9_]+)\s*(\|\|=)\s*(.*)\s+%>}, '{% set \1 = \1 || \3 %}')
|
53
|
+
.gsub(%r{<%\s+([A-Za-z0-9_]+)\s*(=)\s*(.*)\s+%>}, '{% set \1 = \3 %}')
|
54
|
+
end
|
55
|
+
|
56
|
+
def preserve_leftover_statements(html = '')
|
57
|
+
html.gsub(%r{<%\s*(.*?)?%>}, '{% \1%}') # no trailing space because group captures it
|
58
|
+
end
|
59
|
+
|
60
|
+
def restore_variables_in_links(html = '')
|
61
|
+
html.gsub('%7B%7B%20','{{ ')
|
62
|
+
.gsub('%20%7D%7D',' }}')
|
63
|
+
end
|
64
|
+
|
65
|
+
def convert_partials_to_includes(html = '')
|
66
|
+
html.gsub(%r{{{ partial :(.+?) }}}, '{% include \1 %}')
|
67
|
+
.gsub(%r{{% include (.+),\s+locals:(.+?)%}}, '{% include \1 with:\2%}')
|
68
|
+
.gsub(%r{(<%=\s+?partial\s+?:)(.+?),(.+?)(locals:)((.|\n)+?)(%>)}, '{% include \2 with:\5%}') # multiline partials not caught in presumably leftover variable
|
69
|
+
end
|
70
|
+
|
71
|
+
def convert_helper_wrappers(html = '')
|
72
|
+
html.gsub(%r{{{ button(.+?)}}}, '{% button\1%}')
|
73
|
+
.gsub(%r{{{ image_tag(.+?)}}}, '{% image_tag\1%}')
|
74
|
+
end
|
75
|
+
|
76
|
+
def cleanup_logic(html = '')
|
77
|
+
html.gsub(%r{({%|{{)(.*?)(<)(.*?)(}}|%})}, '\1\2<\4\5') # <
|
78
|
+
.gsub(%r{({%|{{)(.*?)(>)(.*?)(}}|%})}, '\1\2>\4\5') # >
|
79
|
+
.gsub(/&&/, '&&')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Antwort
|
2
|
+
module MarkupSanitizers
|
3
|
+
|
4
|
+
def remove_livereload(markup = '')
|
5
|
+
markup.gsub(/<script.*?>(\s|\S)*<\/script>/i, '')
|
6
|
+
.gsub(/(<head.*?>\n)(\s*\n)*/i, '\1')
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_included_css(markup = '')
|
10
|
+
css = File.read("#{markup_dir}/include.css")
|
11
|
+
css_markup = "</title>\n<style type=\"text/css\">\n" + css + "</style>\n"
|
12
|
+
markup.gsub(%r{</title>}i, css_markup)
|
13
|
+
end
|
14
|
+
|
15
|
+
def remove_roadie_flags(markup = '')
|
16
|
+
markup.gsub(' data-roadie-ignore', '')
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_excessive_newlines(markup = '')
|
20
|
+
markup.gsub(/^([ \t]*\n){3,}/m, "\n\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_extra_dom(html = '')
|
24
|
+
html.gsub(/\<!(.*)\<body.*?\>/im, '')
|
25
|
+
.gsub(%r{</body>.*?</html>}im, '')
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Antwort
|
2
|
+
class PartialBuilder < Builder
|
3
|
+
attr_reader :templates
|
4
|
+
|
5
|
+
def post_initialize(*)
|
6
|
+
@templates = list_partials(source_dir).push('index.html.erb')
|
7
|
+
if templates.length < 1
|
8
|
+
say 'Error: ', :red
|
9
|
+
puts "No partials found in #{template_name} folder."
|
10
|
+
return
|
11
|
+
else
|
12
|
+
create_build_directories
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def build
|
17
|
+
@css = load_css
|
18
|
+
templates.each { |t| build_html t }
|
19
|
+
end
|
20
|
+
|
21
|
+
def build_html(partial_name)
|
22
|
+
source_file = "#{source_dir}/#{partial_name}"
|
23
|
+
source = File.read(source_file)
|
24
|
+
markup = preserve_logic(source)
|
25
|
+
markup = preserve_nbsps(markup)
|
26
|
+
markup = preserve_operators_from_nokogiri(markup)
|
27
|
+
inlined = inline(markup)
|
28
|
+
inlined = restore_nbsps(inlined)
|
29
|
+
inlined = flatten_inlined_css(inlined)
|
30
|
+
filename = adjust_filename(partial_name)
|
31
|
+
create_file(content: inlined, path: "#{build_dir}/#{filename}")
|
32
|
+
end
|
33
|
+
|
34
|
+
def inline(markup)
|
35
|
+
# Force a complete DOM tree before inlining for nokogiri
|
36
|
+
# Otherwise we have random <p> at beginning of document
|
37
|
+
html = add_nokogiri_wrapper(markup)
|
38
|
+
|
39
|
+
document = Roadie::Document.new html
|
40
|
+
document.add_css(css)
|
41
|
+
inlined = document.transform
|
42
|
+
inlined = cleanup(inlined)
|
43
|
+
|
44
|
+
remove_nokogiri_wrapper(inlined)
|
45
|
+
end
|
46
|
+
|
47
|
+
def adjust_filename(filename)
|
48
|
+
filename = @template_name if filename == 'index.html.erb'
|
49
|
+
|
50
|
+
name = filename.gsub('.erb', '')
|
51
|
+
name << '.html' unless name[-5, 5] == '.html'
|
52
|
+
name = '_' << name unless name[0] == '_'
|
53
|
+
name
|
54
|
+
end
|
55
|
+
|
56
|
+
def cleanup(html = '')
|
57
|
+
code = remove_extra_dom(html)
|
58
|
+
code = cleanup_logic(code)
|
59
|
+
code = restore_variables_in_links(code)
|
60
|
+
code
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def add_nokogiri_wrapper(html = '')
|
66
|
+
html = '<div id="valid-dom-tree">' + html + '</div><!--/#valid-dom-tree-->'
|
67
|
+
html
|
68
|
+
end
|
69
|
+
|
70
|
+
def remove_nokogiri_wrapper(html = '')
|
71
|
+
html.gsub('<div id="valid-dom-tree">', '')
|
72
|
+
.gsub(%r{</div>(\s*)<!--/#valid-dom-tree-->}, '')
|
73
|
+
end
|
74
|
+
|
75
|
+
def preserve_operators_from_nokogiri(html = '')
|
76
|
+
html.gsub(%r{{%\s*if(.*?)<(.*?)%}}, '{%\1<\2%}')
|
77
|
+
.gsub(%r{{%\s*if(.*?)>(.*?)%}}, '{%\1>\2%}')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|