jekyll-assets 1.0.0 → 2.0.0.pre.beta1

Sign up to get free protection for your applications and to get access to all the features.
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