bunto-assets 1.0.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +19 -0
  3. data/LICENSE +21 -0
  4. data/README.md +275 -0
  5. data/Rakefile +3 -0
  6. data/lib/bunto/assets/addons/autoprefixer.rb +13 -0
  7. data/lib/bunto/assets/addons/bootstrap.rb +7 -0
  8. data/lib/bunto/assets/addons/font_awesome.rb +7 -0
  9. data/lib/bunto/assets/addons/javascript.rb +11 -0
  10. data/lib/bunto/assets/cached.rb +20 -0
  11. data/lib/bunto/assets/config.rb +76 -0
  12. data/lib/bunto/assets/env.rb +207 -0
  13. data/lib/bunto/assets/hook.rb +78 -0
  14. data/lib/bunto/assets/hooks/bunto/drops.rb +9 -0
  15. data/lib/bunto/assets/hooks/bunto/setup.rb +13 -0
  16. data/lib/bunto/assets/hooks/bunto/write.rb +9 -0
  17. data/lib/bunto/assets/hooks/cache.rb +15 -0
  18. data/lib/bunto/assets/hooks/compression.rb +17 -0
  19. data/lib/bunto/assets/hooks/configuration.rb +12 -0
  20. data/lib/bunto/assets/hooks/context.rb +11 -0
  21. data/lib/bunto/assets/hooks/erb.rb +15 -0
  22. data/lib/bunto/assets/hooks/helpers.rb +14 -0
  23. data/lib/bunto/assets/hooks/logger.rb +9 -0
  24. data/lib/bunto/assets/hooks/sources.rb +13 -0
  25. data/lib/bunto/assets/hooks/sprockets.rb +9 -0
  26. data/lib/bunto/assets/hooks/version.rb +11 -0
  27. data/lib/bunto/assets/hooks.rb +20 -0
  28. data/lib/bunto/assets/liquid/drop.rb +76 -0
  29. data/lib/bunto/assets/liquid/filters.rb +28 -0
  30. data/lib/bunto/assets/liquid/tag/defaults/image.rb +69 -0
  31. data/lib/bunto/assets/liquid/tag/defaults.rb +23 -0
  32. data/lib/bunto/assets/liquid/tag/parser.rb +200 -0
  33. data/lib/bunto/assets/liquid/tag/proxied_asset.rb +114 -0
  34. data/lib/bunto/assets/liquid/tag/proxies.rb +118 -0
  35. data/lib/bunto/assets/liquid/tag.rb +203 -0
  36. data/lib/bunto/assets/liquid.rb +15 -0
  37. data/lib/bunto/assets/logger.rb +59 -0
  38. data/lib/bunto/assets/patches/bunto/cleaner.rb +16 -0
  39. data/lib/bunto/assets/patches/bunto/site.rb +11 -0
  40. data/lib/bunto/assets/patches/kernel.rb +29 -0
  41. data/lib/bunto/assets/patches/sprockets/asset.rb +21 -0
  42. data/lib/bunto/assets/patches.rb +10 -0
  43. data/lib/bunto/assets/processors/liquid.rb +42 -0
  44. data/lib/bunto/assets/proxies/magick.rb +155 -0
  45. data/lib/bunto/assets/version.rb +11 -0
  46. data/lib/bunto/assets.rb +24 -0
  47. data/lib/bunto-assets.rb +7 -0
  48. metadata +197 -0
@@ -0,0 +1,114 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ require "forwardable"
8
+
9
+ module Bunto
10
+ module Assets
11
+ module Liquid
12
+ class Tag
13
+ class ProxiedAsset
14
+ attr_reader :args, :asset, :env
15
+ extend Forwardable
16
+
17
+ # ------------------------------------------------------------------
18
+
19
+ def_delegator :@asset, :liquid_tags
20
+ def_delegator :@asset, :filename, :source_filename
21
+ def_delegator :@asset, :content_type
22
+ def_delegator :@asset, :mtime
23
+
24
+ # ------------------------------------------------------------------
25
+
26
+ def initialize(asset, args, env, tag)
27
+ @env = env
28
+ @asset = asset
29
+ @args = args
30
+ @tag = tag
31
+
32
+ cache_file
33
+ proxy_file
34
+ end
35
+
36
+ # ------------------------------------------------------------------
37
+
38
+ def cached?
39
+ @_cached
40
+ end
41
+
42
+ # ------------------------------------------------------------------
43
+
44
+ def write_to(name)
45
+ FileUtils.mkdir_p File.dirname(name)
46
+ Sprockets::PathUtils.atomic_write(name) do |f|
47
+ f.write source
48
+ end
49
+ end
50
+
51
+ # ------------------------------------------------------------------
52
+
53
+ def source
54
+ File.binread(filename)
55
+ end
56
+
57
+ # ------------------------------------------------------------------
58
+
59
+ def filename
60
+ env.in_cache_dir(digest_path)
61
+ end
62
+
63
+ # ------------------------------------------------------------------
64
+
65
+ def digest
66
+ Digest::SHA2.hexdigest(args.proxies.to_s)
67
+ end
68
+
69
+ # ------------------------------------------------------------------
70
+ # We always digest a proxied asset so it's uniq based on what
71
+ # proxies you give us, it would be ignorant to treat it otherwise,
72
+ # we also make sure they are URL safe by digesting the args.
73
+ # ------------------------------------------------------------------
74
+
75
+ def logical_path
76
+ digest_path
77
+ end
78
+
79
+ # ------------------------------------------------------------------
80
+
81
+ def digest_path
82
+ name = asset.logical_path
83
+ ext = File.extname(name)
84
+ "#{name.chomp(ext)}-#{digest}#{ext}"
85
+ end
86
+
87
+ # ------------------------------------------------------------------
88
+
89
+ private
90
+ def proxy_file
91
+ unless cached?
92
+ args.proxies.each do |key, val|
93
+ Proxies.get(key).first[:class].new(self, val, @args).process
94
+ end
95
+ end
96
+ end
97
+
98
+ # ------------------------------------------------------------------
99
+
100
+ private
101
+ def cache_file
102
+ @_cached = File.file?(filename)
103
+ return @_cached if @_cached
104
+
105
+ dir = File.dirname(filename)
106
+ FileUtils.mkdir_p dir unless dir == env.in_cache_dir
107
+ FileUtils.cp asset.filename, filename
108
+ true
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,118 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ module Bunto
8
+ module Assets
9
+ module Liquid
10
+ class Tag
11
+ module Proxies
12
+ def self.add_by_class(class_, name, tag, *args)
13
+ proc_ = proc { |v| [v.to_s, v, v.to_sym] }
14
+ all << {
15
+ :name => proc_.call(name).uniq,
16
+ :args => [args].flatten.map(&proc_).flatten.uniq,
17
+ :tags => [ tag].flatten.map(&proc_).flatten.uniq,
18
+ :class => class_
19
+ }
20
+
21
+ all
22
+ end
23
+
24
+ # ------------------------------------------------------------------
25
+
26
+ def self.add(name, tag, *args, &block)
27
+ add_by_class(*generate_class(name, tag, &block), *args)
28
+ end
29
+
30
+ # ------------------------------------------------------------------
31
+
32
+ def self.keys
33
+ all.select { |val| !val[:class].is_a?(Symbol) }.map do |val|
34
+ val[:name]
35
+ end.flatten
36
+ end
37
+
38
+ # ------------------------------------------------------------------
39
+
40
+ def self.base_keys
41
+ all.select { |val| val[:class].is_a?(Symbol) }.map do |val|
42
+ val[:name]
43
+ end.flatten
44
+ end
45
+
46
+ # ------------------------------------------------------------------
47
+
48
+ def self.has?(name, tag = nil, arg = nil)
49
+ get(name, tag, arg).any?
50
+ end
51
+
52
+ # ------------------------------------------------------------------
53
+
54
+ def self.get(name, tag = nil, arg = nil)
55
+ if name && tag && arg then get_by_name_and_tag_and_arg(name, tag, arg)
56
+ elsif name && tag
57
+ get_by_name_and_tag(name, tag)
58
+
59
+ else
60
+ all.select do |val|
61
+ val[:name].include?(name)
62
+ end
63
+ end
64
+ end
65
+
66
+ # ------------------------------------------------------------------
67
+
68
+ def self.get_by_name_and_tag_and_arg(name, tag, arg)
69
+ all.select do |val|
70
+ val[:name].include?(name) && (val[:tags].include?(:all) || val[:tags] \
71
+ .include?(tag)) && val[:args].include?(arg)
72
+ end
73
+ end
74
+
75
+ # ------------------------------------------------------------------
76
+
77
+ def self.get_by_name_and_tag(name, tag)
78
+ all.select do |val|
79
+ val[:name].include?(name) && (val[:tags].include?(:all) || val[:tags] \
80
+ .include?(tag))
81
+ end
82
+ end
83
+
84
+ # ------------------------------------------------------------------
85
+
86
+ def self.all
87
+ @_all ||= Set.new
88
+ end
89
+
90
+ # ------------------------------------------------------------------
91
+
92
+ private
93
+ def self.generate_class(name, tag, &block)
94
+ class_ = const_set(random_name, Class.new)
95
+ class_.class_eval(&block)
96
+ return class_, name, tag
97
+ end
98
+
99
+ # ------------------------------------------------------------------
100
+
101
+ private
102
+ def self.random_name
103
+ (0...12).map { ("a".."z").to_a.values_at(rand(26)) }.join.capitalize
104
+ end
105
+
106
+ # ------------------------------------------------------------------
107
+ # TODO: Put in a better place.
108
+ # ------------------------------------------------------------------
109
+
110
+ add_by_class :internal, :data, :all, ["@uri"]
111
+ add_by_class :internal, :sprockets, :all, [
112
+ "accept", "write_to"
113
+ ]
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,203 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ require "fastimage"
8
+
9
+ module Bunto
10
+ module Assets
11
+ module Liquid
12
+ class Tag < ::Liquid::Tag
13
+ require_relative "tag/proxies"
14
+ require_relative "tag/proxied_asset"
15
+ require_relative "tag/defaults"
16
+ require_relative "tag/parser"
17
+ attr_reader :args
18
+
19
+ # --------------------------------------------------------------------
20
+
21
+ class << self
22
+ public :new
23
+ end
24
+
25
+ # --------------------------------------------------------------------
26
+
27
+ class AssetNotFoundError < StandardError
28
+ def initialize(asset)
29
+ super "Could not find the asset `#{asset}'"
30
+ end
31
+ end
32
+
33
+ # --------------------------------------------------------------------
34
+ # Tags that we allow our users to use.
35
+ # --------------------------------------------------------------------
36
+
37
+ AcceptableTags = %W(
38
+ img
39
+ image
40
+ javascript
41
+ asset_source
42
+ stylesheet
43
+ asset_path
44
+ style
45
+ asset
46
+ css
47
+ js
48
+ ).freeze
49
+
50
+ # --------------------------------------------------------------------
51
+ # The HTML version of every tag that we accept.
52
+ # --------------------------------------------------------------------
53
+
54
+ Tags = {
55
+ "css" => %{<link type="text/css" rel="stylesheet" href="%s"%s>},
56
+ "js" => %{<script type="text/javascript" src="%s"%s></script>},
57
+ "img" => %{<img src="%s"%s>}
58
+ }.freeze
59
+
60
+ # --------------------------------------------------------------------
61
+ # Allows us to normalize tags so we can simplify logic.
62
+ # --------------------------------------------------------------------
63
+
64
+ Alias = {
65
+ "image" => "img",
66
+ "stylesheet" => "css",
67
+ "javascript" => "js",
68
+ "style" => "css"
69
+ }.freeze
70
+
71
+ # --------------------------------------------------------------------
72
+
73
+ def initialize(tag, args, tokens)
74
+ tag = tag.to_s
75
+ @tokens = tokens
76
+ @tag = from_alias(tag)
77
+ @args = Parser.new(args, @tag)
78
+ @og_tag = tag
79
+ super
80
+ end
81
+
82
+ # --------------------------------------------------------------------
83
+ # NOTE: We only attach to the regenerator if you are using digested
84
+ # assets, otherwise we forego any association with it so that we keep
85
+ # your builds ultra fast, this is ideal in dev. Disable digests and
86
+ # let us process independent so the entire site isn't regenerated
87
+ # because of a single asset change.
88
+ # --------------------------------------------------------------------
89
+
90
+ def render(context)
91
+ site = context.registers[:site]
92
+ page = context.registers.fetch(:page, {})
93
+ args = @args.parse_liquid(context)
94
+ sprockets = site.sprockets
95
+ page = page["path"]
96
+
97
+ asset = find_asset(args, sprockets)
98
+ add_as_bunto_dependency(site, sprockets, page, asset)
99
+ process_tag(args, sprockets, asset)
100
+
101
+ rescue => e
102
+ capture_and_out_error(
103
+ site, e
104
+ )
105
+ end
106
+
107
+ # --------------------------------------------------------------------
108
+
109
+ private
110
+ def from_alias(tag)
111
+ Alias.key?(tag) ? Alias[tag] : tag
112
+ end
113
+
114
+ # --------------------------------------------------------------------
115
+
116
+ private
117
+ def process_tag(args, sprockets, asset)
118
+ sprockets.used.add(asset) unless @tag == "asset_source"
119
+ Defaults.set_defaults_for!(@tag, args ||= {}, asset, sprockets)
120
+ build_html(args, sprockets, asset)
121
+ end
122
+
123
+ # --------------------------------------------------------------------
124
+
125
+ private
126
+ def build_html(args, sprockets, asset, path = get_path(sprockets, asset))
127
+ return path if @tag == "asset_path"
128
+ return asset.to_s if @tag == "asset" || @tag == "asset_source"
129
+ return format(Tags[@tag], asset.data_uri, args.to_html) if args.key?(:data) && args[:data].key?(:uri)
130
+ format(Tags[@tag], path, args.to_html)
131
+ end
132
+
133
+ # --------------------------------------------------------------------
134
+
135
+ private
136
+ def get_path(sprockets, asset)
137
+ sprockets.prefix_path(
138
+ sprockets.digest?? asset.digest_path : asset.logical_path
139
+ )
140
+ end
141
+
142
+ # --------------------------------------------------------------------
143
+
144
+ private
145
+ def add_as_bunto_dependency(site, sprockets, page, asset)
146
+ if page && sprockets.digest?
147
+ site.regenerator.add_dependency(
148
+ site.in_source_dir(page), site.in_source_dir(asset.logical_path)
149
+ )
150
+ end
151
+ end
152
+
153
+ # --------------------------------------------------------------------
154
+
155
+ private
156
+ def find_asset(args, sprockets)
157
+ sprockets_, file = args[:sprockets] ||= {}, args[:file]
158
+ if !(out = sprockets.find_asset(file, sprockets_))
159
+ raise(
160
+ AssetNotFoundError, args[:file]
161
+ )
162
+
163
+ else
164
+ out.liquid_tags << self
165
+ !args.proxies?? out : ProxiedAsset.new(
166
+ out, args, sprockets, self
167
+ )
168
+ end
169
+ end
170
+
171
+ # --------------------------------------------------------------------
172
+ # There is no guarantee that Bunto will pass on the error for some
173
+ # reason (unless you are just booting up) so we capture that error and
174
+ # always output it, it can lead to some double errors but I would
175
+ # rather there be a double error than no error.
176
+ # --------------------------------------------------------------------
177
+
178
+ private
179
+ def capture_and_out_error(site, error)
180
+ if error.is_a?(Sass::SyntaxError)
181
+ file = error.sass_filename.gsub(/#{Regexp.escape(site.source)}\//, "")
182
+ Bunto.logger.error(%{Error in #{file}:#{error.sass_line} #{
183
+ error
184
+ }})
185
+
186
+ else
187
+ Bunto.logger.error(
188
+ "", error.to_s
189
+ )
190
+ end
191
+
192
+ raise error
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ # ----------------------------------------------------------------------------
200
+
201
+ Bunto::Assets::Liquid::Tag::AcceptableTags.each do |tag|
202
+ Liquid::Template.register_tag tag, Bunto::Assets::Liquid::Tag
203
+ end
@@ -0,0 +1,15 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ module Bunto
8
+ module Assets
9
+ module Liquid
10
+ require_relative "liquid/drop"
11
+ require_relative "liquid/filters"
12
+ require_relative "liquid/tag"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,59 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ module Bunto
8
+ module Assets
9
+ class Logger
10
+ PREFIX = "Bunto Assets:"
11
+
12
+ def log
13
+ @_log ||= Bunto.logger
14
+ end
15
+
16
+ # ----------------------------------------------------------------------
17
+ # Log Level: 1
18
+ # ----------------------------------------------------------------------
19
+
20
+ def warn(msg = nil, &block)
21
+ msg = (block_given?? block.call : msg)
22
+ log.warn(PREFIX, msg)
23
+ end
24
+
25
+ # ----------------------------------------------------------------------
26
+ # Log Level: 1
27
+ # ----------------------------------------------------------------------
28
+
29
+ def error(msg = nil, &block)
30
+ msg = (block_given?? block.call : msg)
31
+ log.error(PREFIX, msg)
32
+ end
33
+
34
+ # ----------------------------------------------------------------------
35
+ # Log Level: 2
36
+ # ----------------------------------------------------------------------
37
+
38
+ def info(msg = nil, &block)
39
+ msg = (block_given?? block.call : msg)
40
+ log.info(PREFIX, msg)
41
+ end
42
+
43
+ # ----------------------------------------------------------------------
44
+ # Log Level: 3
45
+ # ----------------------------------------------------------------------
46
+
47
+ def debug(msg = nil, &block)
48
+ msg = (block_given?? block.call : msg)
49
+ log.debug(PREFIX, msg)
50
+ end
51
+
52
+ # ----------------------------------------------------------------------
53
+
54
+ def log_level=(*)
55
+ raise RuntimeError, "Please set log levels on Bunto.logger"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,16 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ module Bunto
8
+ class Cleaner
9
+ alias _old_obsolete_files obsolete_files
10
+ def obsolete_files
11
+ _old_obsolete_files.delete_if do |path|
12
+ path =~ %r!\A#{Regexp.escape(site.in_dest_dir("assets"))}(\/.*)?\Z!
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ module Bunto
8
+ class Site
9
+ attr_accessor :sprockets
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ module Kernel
8
+ def try_require(file)
9
+ require file
10
+ if block_given?
11
+ yield
12
+ end
13
+ rescue LoadError
14
+ return nil
15
+ end
16
+
17
+ # --------------------------------------------------------------------------
18
+
19
+ def try_require_if_javascript(file)
20
+ ["execjs", file].map(&method(:require))
21
+ if block_given?
22
+ yield
23
+ end
24
+ rescue LoadError, ExecJS::RuntimeUnavailable
25
+ Bunto.logger.debug("ExecJS, JS Runtime or `#{file}' not available." \
26
+ " Skipping the loading of libraries.")
27
+ return
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ module Sprockets
8
+ class Asset
9
+ def liquid_tags
10
+ metadata[:liquid_tags] ||= Set.new
11
+ end
12
+
13
+ # ------------------------------------------------------------------------
14
+
15
+ def data_uri
16
+ "data:#{content_type};base64,#{Rack::Utils.escape(
17
+ Base64.encode64(to_s)
18
+ )}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ # ----------------------------------------------------------------------------
2
+ # Frozen-string-literal: true
3
+ # Copyright: 2016-present - MIT License
4
+ # Encoding: utf-8
5
+ # ----------------------------------------------------------------------------
6
+
7
+ require_relative "patches/bunto/cleaner"
8
+ require_relative "patches/sprockets/asset"
9
+ require_relative "patches/bunto/site"
10
+ require_relative "patches/kernel"
@@ -0,0 +1,42 @@
1
+ module Bunto
2
+ module Assets
3
+ module Processors
4
+ class Liquid
5
+ FOR = %W(
6
+ text/css text/sass text/less application/javascript
7
+ text/scss text/coffeescript text/javascript).freeze
8
+ EXT = %W(.liquid .js.liquid .css.liquid .scss.liquid).freeze
9
+
10
+ # --------------------------------------------------------------------
11
+
12
+ def self.call(context, bunto = context[:environment].bunto)
13
+ if context[:environment].parent.asset_config["features"]["liquid"] || \
14
+ File.extname(context[:filename]) == ".liquid"
15
+
16
+ payload_ = bunto.site_payload
17
+ renderer = bunto.liquid_renderer.file(context[:filename])
18
+ context[:data] = renderer.parse(context[:data]).render! payload_, :registers => {
19
+ :site => bunto
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # ----------------------------------------------------------------------------
29
+ # There might be a few missing, if there is please do let me know.
30
+ # ----------------------------------------------------------------------------
31
+
32
+ Bunto::Assets::Processors::Liquid::EXT.each do |ext|
33
+ Sprockets.register_engine(
34
+ ext, Bunto::Assets::Processors::Liquid
35
+ )
36
+ end
37
+
38
+ Bunto::Assets::Processors::Liquid::FOR.each do |val|
39
+ Sprockets.register_preprocessor(
40
+ val, Bunto::Assets::Processors::Liquid
41
+ )
42
+ end