antwort 0.0.12
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/.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
|