jekyll-assets 1.0.0 → 2.0.0.pre.beta1

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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -10
  3. data/LICENSE +16 -18
  4. data/README.md +94 -554
  5. data/Rakefile +2 -8
  6. data/lib/jekyll/assets.rb +18 -12
  7. data/lib/jekyll/assets/cached.rb +12 -0
  8. data/lib/jekyll/assets/configuration.rb +43 -87
  9. data/lib/jekyll/assets/context.rb +23 -0
  10. data/lib/jekyll/assets/env.rb +181 -0
  11. data/lib/jekyll/assets/extras/es6.rb +5 -0
  12. data/lib/jekyll/assets/extras/font-awesome.rb +1 -0
  13. data/lib/jekyll/assets/extras/helpers.rb +6 -0
  14. data/lib/jekyll/assets/extras/prefix.rb +5 -0
  15. data/lib/jekyll/assets/filters.rb +9 -11
  16. data/lib/jekyll/assets/helpers.rb +37 -0
  17. data/lib/jekyll/assets/hook.rb +45 -0
  18. data/lib/jekyll/assets/hooks/post_read.rb +3 -0
  19. data/lib/jekyll/assets/hooks/post_write.rb +3 -0
  20. data/lib/jekyll/assets/logger.rb +25 -0
  21. data/lib/jekyll/assets/patches/jekyll/cleaner.rb +11 -0
  22. data/lib/jekyll/assets/patches/jekyll/site.rb +5 -0
  23. data/lib/jekyll/assets/patches/sprockets/asset.rb +13 -0
  24. data/lib/jekyll/assets/tag.rb +136 -9
  25. data/lib/jekyll/assets/tag/parser.rb +129 -0
  26. data/lib/jekyll/assets/tag/proxied_asset.rb +84 -0
  27. data/lib/jekyll/assets/tag/proxies.rb +86 -0
  28. data/lib/jekyll/assets/tag/proxies/magick.rb +101 -0
  29. data/lib/jekyll/assets/version.rb +1 -1
  30. metadata +59 -149
  31. data/.gitignore +0 -27
  32. data/.rspec +0 -1
  33. data/.rubocop.yml +0 -43
  34. data/.rubocop_todo.yml +0 -10
  35. data/.travis.yml +0 -16
  36. data/.yardopts +0 -1
  37. data/Appraisals +0 -7
  38. data/Guardfile +0 -7
  39. data/HISTORY.md +0 -297
  40. data/gemfiles/jekyll_2.gemfile +0 -17
  41. data/gemfiles/jekyll_3.gemfile +0 -17
  42. data/jekyll-assets.gemspec +0 -34
  43. data/lib/jekyll-assets.rb +0 -2
  44. data/lib/jekyll/assets/asset_path.rb +0 -39
  45. data/lib/jekyll/assets/environment.rb +0 -62
  46. data/lib/jekyll/assets/patches.rb +0 -1
  47. data/lib/jekyll/assets/patches/asset_patch.rb +0 -102
  48. data/lib/jekyll/assets/patches/bundled_asset_patch.rb +0 -16
  49. data/lib/jekyll/assets/patches/context_patch.rb +0 -31
  50. data/lib/jekyll/assets/patches/index_patch.rb +0 -25
  51. data/lib/jekyll/assets/patches/processed_asset_patch.rb +0 -59
  52. data/lib/jekyll/assets/patches/site_patch.rb +0 -62
  53. data/lib/jekyll/assets/renderer.rb +0 -122
  54. data/spec/fixtures/.gitignore +0 -2
  55. data/spec/fixtures/.jekyll-metadata +0 -0
  56. data/spec/fixtures/_assets/alert.js +0 -1
  57. data/spec/fixtures/_assets/app.css.erb +0 -5
  58. data/spec/fixtures/_assets/app.js +0 -1
  59. data/spec/fixtures/_assets/app.min.css +0 -0
  60. data/spec/fixtures/_assets/app.min.js +0 -0
  61. data/spec/fixtures/_assets/fonts/vapor.eot +0 -0
  62. data/spec/fixtures/_assets/fonts/vapor.svg +0 -0
  63. data/spec/fixtures/_assets/fonts/vapor.ttf +0 -0
  64. data/spec/fixtures/_assets/fonts/vapor.woff +0 -0
  65. data/spec/fixtures/_assets/lib/relative.css.scss +0 -12
  66. data/spec/fixtures/_assets/noise.png +0 -0
  67. data/spec/fixtures/_assets/noize.png +0 -0
  68. data/spec/fixtures/_assets/should_be_blank.css.erb +0 -1
  69. data/spec/fixtures/_assets/should_fail.css.erb +0 -1
  70. data/spec/fixtures/_assets/vapor.css.scss +0 -13
  71. data/spec/fixtures/_assets/vapor.js +0 -2
  72. data/spec/fixtures/_assets/vendor/with_bootstrap.css.sass +0 -1
  73. data/spec/fixtures/_assets/vendor/with_bourbon.css.sass +0 -4
  74. data/spec/fixtures/_assets/vendor/with_compass.css.sass +0 -4
  75. data/spec/fixtures/_assets/vendor/with_neat.css.sass +0 -5
  76. data/spec/fixtures/_assets/wowscript.js +0 -0
  77. data/spec/fixtures/_assets/wowstyle.css +0 -0
  78. data/spec/fixtures/_config.yml +0 -2
  79. data/spec/fixtures/_layouts/default.html +0 -9
  80. data/spec/fixtures/_posts/2012-10-19-hello-world.md +0 -6
  81. data/spec/fixtures/_posts/2015-02-02-duplicates.md +0 -8
  82. data/spec/fixtures/index.html +0 -0
  83. data/spec/lib/jekyll/assets/configuration_spec.rb +0 -172
  84. data/spec/lib/jekyll/assets/environment_spec.rb +0 -18
  85. data/spec/lib/jekyll/assets/filters_spec.rb +0 -112
  86. data/spec/lib/jekyll/assets/patches/site_patch_spec.rb +0 -176
  87. data/spec/lib/jekyll/assets/renderer_spec.rb +0 -286
  88. data/spec/lib/jekyll/assets/tag_spec.rb +0 -139
  89. data/spec/spec_helper.rb +0 -58
  90. data/spec/support/fixtures_helpers.rb +0 -7
@@ -0,0 +1,37 @@
1
+ module Jekyll
2
+ module Assets
3
+ module Helpers
4
+ class << self
5
+ def has_javascript?
6
+ require "execjs"
7
+ if block_given?
8
+ yield
9
+ end
10
+ rescue LoadError, ExecJS::RuntimeUnavailable
11
+ Jekyll.logger.debug("ExecJS or JS Runtime not available." \
12
+ " Skipping loading of library.")
13
+ end
14
+
15
+ def try_require(file)
16
+ require file
17
+ if block_given?
18
+ yield
19
+ end
20
+ rescue LoadError
21
+ return nil
22
+ end
23
+
24
+ def try_require_if_javascript?(file)
25
+ ["execjs", file].map(&method(:require))
26
+ if block_given?
27
+ yield
28
+ end
29
+ rescue LoadError, ExecJS::RuntimeUnavailable
30
+ Jekyll.logger.debug("ExecJS, JS Runtime or `#{file}' not available." \
31
+ " Skipping the loading of libraries.")
32
+ return
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ module Jekyll
2
+ module Assets
3
+ class Hook
4
+ class UnknownHookError < RuntimeError
5
+ def initialize(base, point)
6
+ super "Unknown jekyll-assets hook point (#{point}) or base (#{base}) given."
7
+ end
8
+ end
9
+
10
+ HOOK_POINTS = {
11
+ :env => [
12
+ :pre_init, :post_init
13
+ ]
14
+ }
15
+
16
+ class << self
17
+ def all
18
+ @_all ||= {}
19
+ end
20
+
21
+ def trigger(base, point, *args)
22
+ if all[base][point]
23
+ then all[base][point].map do |v|
24
+ v.call(*args)
25
+ end
26
+ end
27
+ end
28
+
29
+ def point(base, point)
30
+ all[base][point] ||= Set.new
31
+ end
32
+
33
+ def register(base, point, &block)
34
+ if HOOK_POINTS.has_key?(base) && HOOK_POINTS[base].include?(point)
35
+ all[base] ||= {}
36
+ point(base, point) << \
37
+ block
38
+ else
39
+ raise UnknownHookError.new(base, point)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ Jekyll::Hooks.register :site, :post_read do |s|
2
+ Jekyll::Assets::Env.new(s)
3
+ end
@@ -0,0 +1,3 @@
1
+ Jekyll::Hooks.register :site, :post_write do |s|
2
+ s.sprockets.write_all
3
+ end
@@ -0,0 +1,25 @@
1
+ module Jekyll
2
+ module Assets
3
+
4
+ # TODO: jekyll/jekyll@upstream add support for blocks as messages...
5
+ # NOTE: This is a temporary class, until we can go upstream and fix
6
+ # the little known fact that it doesn't accept a block for a message
7
+ # it is passing on. Until then we are holding this.
8
+
9
+ class Logger
10
+ def instance
11
+ @logger ||= Jekyll.logger
12
+ end
13
+
14
+ %W(warn error info debug).each do |k|
15
+ define_method k do |msg = nil, &block|
16
+ instance.send(k, "Jekyll Assets:", block ? block.call : msg)
17
+ end
18
+ end
19
+
20
+ def log_level=(*a)
21
+ raise RuntimeError, "Please set log levels on Jekyll.logger"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Jekyll
2
+ class Cleaner
3
+ # TODO: jekyll/jekyll@upstream: This method should really have a hook ya.
4
+ alias_method :_old_obsolete_files, :obsolete_files
5
+ def obsolete_files
6
+ _old_obsolete_files.delete_if do |v|
7
+ v =~ %r!\A#{Regexp.escape(site.in_dest_dir("assets"))}(\/.*)?\Z!
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Jekyll
2
+ class Site
3
+ attr_accessor :sprockets
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ module Sprockets
2
+ class Asset
3
+ def data_uri
4
+ "data:#{content_type};base64,#{Rack::Utils.escape \
5
+ Base64.encode64(to_s)}"
6
+ end
7
+
8
+ def liquid_tags
9
+ metadata[:liquid_tags] ||= \
10
+ Set.new
11
+ end
12
+ end
13
+ end
@@ -1,19 +1,146 @@
1
- # 3rd-party
2
- require "liquid"
3
-
4
- # internal
5
- require "jekyll/assets/renderer"
6
-
7
1
  module Jekyll
8
2
  module Assets
3
+
4
+ # TODO: Somewhere in here we need to designate the proxy as an asset
5
+ # so that the env does not need to be aware of anything.
6
+
9
7
  class Tag < Liquid::Tag
8
+ require_relative "tag/proxied_asset"
9
+ require_relative "tag/parser"
10
+ attr_reader :args
11
+
12
+ class AssetNotFoundError < StandardError
13
+ def initialize(asset)
14
+ super "Could not find the asset `#{asset}'"
15
+ end
16
+ end
17
+
18
+ TAGS = {
19
+ "css" => %Q{<link type="text/css" rel="stylesheet" href="%s"%s>},
20
+ "js" => %Q{<script type="text/javascript" src="%s"%s></script>},
21
+ "img" => %Q{<img src="%s"%s>}
22
+ }
23
+
24
+ ALIAS = {
25
+ "image" => "img",
26
+ "stylesheet" => "css",
27
+ "javascript" => "js",
28
+ "style" => "css"
29
+ }
30
+
31
+ def initialize(tag, args, tokens)
32
+ @tokens = tokens
33
+ @tag = from_alias(tag)
34
+ @args = Parser.new(args, @tag)
35
+ @og_tag = tag
36
+ super
37
+ end
38
+
39
+ # NOTE: We only attach to the regenerator if you are using digested
40
+ # assets, otherwise we forego any association with it so that we keep
41
+ # your builds ultra fast, this is ideal in dev. Disable digests and
42
+ # let us process independent so the entire site isn't regenerated
43
+ # because of a single asset change.
44
+
10
45
  def render(context)
11
- Renderer.new(context, @markup).send :"render_#{@tag_name}"
46
+ site = context.registers[:site]
47
+ page = context.registers.fetch(:page, {}).fetch("path", nil)
48
+ sprockets = site.sprockets
49
+
50
+ asset = find_asset(sprockets)
51
+ add_as_jekyll_dependency(site, sprockets, page, asset)
52
+ process_tag(sprockets, asset)
53
+ rescue => e
54
+ capture_and_out_error \
55
+ site, e
56
+ end
57
+
58
+ private
59
+ def from_alias(tag)
60
+ ALIAS[tag] || \
61
+ tag
62
+ end
63
+
64
+ private
65
+ def process_tag(sprockets, asset)
66
+ set_img_alt asset if @tag == "img"
67
+ out = get_path sprockets, asset
68
+
69
+ sprockets.used.add(asset)
70
+
71
+ if @tag == "asset_path"
72
+ return out
73
+
74
+ elsif @args[:data][:uri]
75
+ return TAGS[@tag] % [
76
+ asset.data_uri, @args.to_html
77
+ ]
78
+
79
+ else
80
+ return TAGS[@tag] % [
81
+ out, @args.to_html
82
+ ]
83
+ end
84
+ end
85
+
86
+ private
87
+ def get_path(sprockets, asset)
88
+ sprockets.prefix_path(
89
+ sprockets.digest?? asset.digest_path : asset.logical_path
90
+ )
91
+ end
92
+
93
+ private
94
+ def set_img_alt(asset)
95
+ if !@args[:html]["alt"]
96
+ return @args[:html]["alt"] = asset.logical_path
97
+ end
98
+ end
99
+
100
+ private
101
+ def add_as_jekyll_dependency(site, sprockets, page, asset)
102
+ if page && sprockets.digest?
103
+ site.regenerator.add_dependency(
104
+ site.in_source_dir(page), site.in_source_dir(asset.logical_path)
105
+ )
106
+ end
107
+ end
108
+
109
+ private
110
+ def find_asset(sprockets)
111
+ if !(out = sprockets.find_asset(@args[:file], @args[:sprockets]))
112
+ raise AssetNotFoundError, @args[:file]
113
+ else
114
+ out.liquid_tags << self
115
+ if !@args.has_proxies?
116
+ out else ProxiedAsset.new(
117
+ out, @args, sprockets, self
118
+ )
119
+ end
120
+ end
121
+ end
122
+
123
+ # There is no guarantee that Jekyll will pass on the error for
124
+ # some reason (unless you are just booting up) so we capture that error
125
+ # and always output it, it can lead to some double errors but
126
+ # I would rather there be a double error than no error.
127
+
128
+ private
129
+ def capture_and_out_error(site, error)
130
+ if error.is_a?(Sass::SyntaxError)
131
+ file = error.sass_filename.gsub(/#{Regexp.escape(site.source)}\//, "")
132
+ Jekyll.logger.error(%Q{Error in #{file}:#{error.sass_line} #{error}})
133
+ else
134
+ Jekyll.logger.error \
135
+ "", error.to_s
136
+ end
137
+
138
+ raise error
12
139
  end
13
140
  end
14
141
  end
15
142
  end
16
143
 
17
- %w(asset asset_path image javascript stylesheet).each do |tag|
18
- Liquid::Template.register_tag tag, Jekyll::Assets::Tag
144
+ %W(js css img image javascript stylesheet style asset_path).each do |t|
145
+ Liquid::Template.register_tag t, Jekyll::Assets::Tag
19
146
  end
@@ -0,0 +1,129 @@
1
+ require_relative "proxies"
2
+ require "forwardable"
3
+
4
+ module Jekyll
5
+ module Assets
6
+
7
+ # Examples:
8
+ # - {% tag value argument:value %}
9
+ # - {% tag value "argument:value" %}
10
+ # - {% tag value argument:"I have spaces" %}
11
+ # - {% tag value argument:value\:with\:colon %}
12
+ # - {% tag value argument:"I can even escape \\: here too!" %}
13
+ # - {% tag value proxy:key:value %}
14
+
15
+ class Tag
16
+ class Parser
17
+ attr_reader :args, :raw_args
18
+ extend Forwardable
19
+
20
+ def_delegator :@args, :to_h
21
+ def_delegator :@args, :has_key?
22
+ def_delegator :@args, :fetch
23
+ def_delegator :@args, :[]
24
+
25
+ ACCEPT = {
26
+ "css" => "text/css", "js" => "application/javascript"
27
+ }
28
+
29
+ class UnescapedColonError < StandardError
30
+ def initialize
31
+ super "Unescaped double colon argument."
32
+ end
33
+ end
34
+
35
+ class UnknownProxyError < StandardError
36
+ def initialize
37
+ super "Unknown proxy argument."
38
+ end
39
+ end
40
+
41
+ def initialize(args, tag)
42
+ @raw_args, @tags = args, tag
43
+ @tag = tag
44
+ parse_raw
45
+ set_accept
46
+ end
47
+
48
+ def to_html
49
+ @args[:html].map do |k, v|
50
+ %Q{ #{k}="#{v}"}
51
+ end. \
52
+ join
53
+ end
54
+
55
+ def proxies
56
+ keys = (args.keys - Proxies.base_keys - [:file, :html])
57
+ args.select do |k, v|
58
+ keys.include?(k)
59
+ end
60
+ end
61
+
62
+ def has_proxies?
63
+ proxies.any?
64
+ end
65
+
66
+ private
67
+ def parse_raw
68
+ @args = from_shellwords.each_with_index.inject(dhash) do |h, (k, i)|
69
+ if i == 0 then h[:file] = k
70
+ elsif k =~ /:/ && (k = k.split(/(?<!\\):/)) then parse_col h, k
71
+ else h[:html][k] = true
72
+ end
73
+
74
+ h
75
+ end
76
+ end
77
+
78
+ private
79
+ def parse_col(h, k)
80
+ k[-1] = k[-1].gsub(/\\:/, ":")
81
+ if k.size == 3 then as_proxy h, k
82
+ elsif k.size == 2 then as_bool_or_html h, k
83
+ else raise UnescapedColonError
84
+ end
85
+ end
86
+
87
+ private
88
+ def as_bool_or_html(h, k)
89
+ key, sub_key = k
90
+ if Proxies.has?(key, @tag, "@#{sub_key}")
91
+ h[key.to_sym][sub_key.to_sym] = true
92
+ else
93
+ h[:html][key] = k[1]
94
+ end
95
+ end
96
+
97
+ private
98
+ def as_proxy(h, k)
99
+ key, sub_key, value = k
100
+ if Proxies.has?(key, @tag, sub_key)
101
+ h[key.to_sym][sub_key.to_sym] = \
102
+ value
103
+ elsif Proxies.has?(key)
104
+ raise UnknownProxyError
105
+ end
106
+ end
107
+
108
+ private
109
+ def set_accept
110
+ if (accept = ACCEPT[@tag]) && !args[:sprockets][:accept]
111
+ @args[:sprockets][:accept] = accept
112
+ end
113
+ end
114
+
115
+ private
116
+ def dhash
117
+ Hash.new do |h, k|
118
+ h[k] = {}
119
+ end
120
+ end
121
+
122
+ private
123
+ def from_shellwords
124
+ Shellwords.shellwords(@raw_args)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,84 @@
1
+ require "forwardable"
2
+
3
+ module Jekyll
4
+ module Assets
5
+ class Tag
6
+ class ProxiedAsset
7
+ attr_reader :args, :asset, :env
8
+ extend Forwardable
9
+
10
+ def_delegator :@asset, :liquid_tags
11
+ def_delegator :@asset, :content_type
12
+ def_delegator :@asset, :filename
13
+
14
+ def initialize(asset, args, env, tag)
15
+ @env = env
16
+ @asset = asset
17
+ @args = args
18
+ @tag = tag
19
+ cache_file
20
+ proxy_file
21
+ end
22
+
23
+ def cached?
24
+ @_cached
25
+ end
26
+
27
+ def source
28
+ File.binread(
29
+ filename
30
+ )
31
+ end
32
+
33
+ def filename
34
+ env.in_cache_dir(
35
+ digest_path
36
+ )
37
+ end
38
+
39
+ def digest
40
+ Digest::SHA2.hexdigest(
41
+ args.proxies.to_s
42
+ )
43
+ end
44
+
45
+ # We always digest a proxied asset so it's uniq based on what
46
+ # proxies you give us, it would be ignorant to treat it otherwise,
47
+ # we also make sure they are URL safe by digesting the args.
48
+
49
+ def logical_path
50
+ digest_path
51
+ end
52
+
53
+ def digest_path
54
+ name = asset.logical_path
55
+ ext = File.extname(name)
56
+ "#{File.basename(name, ext)}-#{digest}#{ext}"
57
+ end
58
+
59
+ def write_to(name)
60
+ File.binwrite(
61
+ name, source
62
+ )
63
+ end
64
+
65
+ private
66
+ def proxy_file
67
+ unless cached?
68
+ args.proxies.each do |k, v|
69
+ Proxies.get(k).first[:cls].new(self, v).process
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+ def cache_file
76
+ if File.file?(filename)
77
+ @_cached = true else @_cached = false
78
+ FileUtils.cp asset.filename, filename
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end