jekyll-minibundle 1.0.0 → 1.1.0

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.
@@ -6,12 +6,12 @@ module Jekyll::Minibundle
6
6
  def initialize(config)
7
7
  @type = config['type']
8
8
  asset_source_dir = File.join config['site_dir'], config['source_dir']
9
- asset_destination_path = config['destination_path']
9
+ destination_path = config['destination_path']
10
10
 
11
11
  @files = config['assets'].map do |asset_path|
12
12
  asset_basename = "#{asset_path}.#{@type}"
13
13
  asset_source = File.join asset_source_dir, asset_basename
14
- asset_destination = File.join asset_destination_path, asset_basename
14
+ asset_destination = File.join destination_path, asset_basename
15
15
  DevelopmentFile.new asset_source, asset_destination
16
16
  end
17
17
 
@@ -27,7 +27,7 @@ module Jekyll::Minibundle
27
27
 
28
28
  def markup
29
29
  @files.
30
- map { |f| AssetTagMarkup.make_markup @type, f.asset_path, @attributes }.
30
+ map { |f| AssetTagMarkup.make_markup @type, f.asset_destination_path, @attributes }.
31
31
  join('')
32
32
  end
33
33
  end
@@ -0,0 +1,14 @@
1
+ module Jekyll::Minibundle
2
+ module Environment
3
+ def self.command_for(type)
4
+ key = "JEKYLL_MINIBUNDLE_CMD_#{type.upcase}"
5
+ cmd = ENV[key]
6
+ raise "You need to set command for minification in $#{key}" if !cmd
7
+ cmd
8
+ end
9
+
10
+ def self.development?
11
+ ENV['JEKYLL_MINIBUNDLE_MODE'] == 'development'
12
+ end
13
+ end
14
+ end
@@ -1,6 +1,7 @@
1
1
  require 'yaml'
2
2
  require 'jekyll/minibundle/bundle_file'
3
3
  require 'jekyll/minibundle/development_file_collection'
4
+ require 'jekyll/minibundle/environment'
4
5
 
5
6
  module Jekyll::Minibundle
6
7
  class MiniBundleBlock < Liquid::Block
@@ -11,19 +12,12 @@ module Jekyll::Minibundle
11
12
 
12
13
  def render(context)
13
14
  site = context.registers[:site]
14
- config = get_current_config YAML.load(super), site
15
-
16
- file = if ENV['JEKYLL_MINIBUNDLE_MODE'] == 'development'
17
- DevelopmentFileCollection.new config
18
- else
19
- BundleFile.new config
20
- end
21
-
15
+ file = get_file get_current_config(YAML.load(super), site)
22
16
  file.static_file! site
23
17
  file.markup
24
18
  end
25
19
 
26
- def default_config
20
+ def self.default_config
27
21
  {
28
22
  'source_dir' => '_assets',
29
23
  'destination_path' => 'assets/site',
@@ -35,10 +29,18 @@ module Jekyll::Minibundle
35
29
  private
36
30
 
37
31
  def get_current_config(user_config, site)
38
- default_config.
32
+ MiniBundleBlock.default_config.
39
33
  merge(user_config).
40
34
  merge({ 'type' => @type, 'site_dir' => site.source })
41
35
  end
36
+
37
+ def get_file(config)
38
+ if Environment.development?
39
+ DevelopmentFileCollection.new config
40
+ else
41
+ BundleFile.new config
42
+ end
43
+ end
42
44
  end
43
45
  end
44
46
 
@@ -9,9 +9,19 @@ module Jekyll::Minibundle
9
9
 
10
10
  def render(context)
11
11
  site = context.registers[:site]
12
- file = StampFile.new File.join(site.source, @asset_source), @asset_destination
12
+ file = StampFile.new(File.join(site.source, @asset_source), @asset_destination, &get_basenamer)
13
13
  file.static_file! site
14
- file.asset_path
14
+ file.markup
15
+ end
16
+
17
+ private
18
+
19
+ def get_basenamer
20
+ if Environment.development?
21
+ ->(base, ext, _) { base + ext }
22
+ else
23
+ ->(base, ext, stamper) { "#{base}-#{stamper.call}#{ext}" }
24
+ end
15
25
  end
16
26
  end
17
27
  end
@@ -11,11 +11,28 @@ module Jekyll::Minibundle
11
11
 
12
12
  attr_reader :asset_source_path, :asset_destination_dir
13
13
 
14
- def initialize(asset_source_path, asset_destination_path)
14
+ def initialize(asset_source_path, asset_destination_path, &basenamer)
15
+ @basenamer = basenamer
15
16
  @asset_source_path = asset_source_path
16
17
  @asset_destination_dir = File.dirname asset_destination_path
17
18
  @asset_destination_extension = File.extname asset_destination_path
18
19
  @asset_destination_base_prefix = File.basename(asset_destination_path)[0 .. -(@asset_destination_extension.size + 1)]
20
+ @was_modified = false
21
+ end
22
+
23
+ def markup
24
+ # we must regenerate the fingerprint here, if at all, in order
25
+ # to make sure the markup and generated file have the same
26
+ # fingerprint
27
+ if modified?
28
+ @asset_stamp = nil
29
+ @@mtimes[path] = mtime
30
+ @was_modified = true
31
+ else
32
+ @was_modified = false
33
+ end
34
+
35
+ asset_destination_path
19
36
  end
20
37
 
21
38
  def last_mtime_of(path)
@@ -23,14 +40,9 @@ module Jekyll::Minibundle
23
40
  end
24
41
 
25
42
  def write(site_destination_dir)
26
- is_modified = modified?
27
-
28
- clear_asset_stamp if is_modified
29
-
30
- if destination_exists?(site_destination_dir) && !is_modified
43
+ if destination_exists?(site_destination_dir) && !@was_modified
31
44
  false
32
45
  else
33
- update_mtime
34
46
  write_destination site_destination_dir
35
47
  true
36
48
  end
@@ -39,19 +51,11 @@ module Jekyll::Minibundle
39
51
  private
40
52
 
41
53
  def asset_destination_basename
42
- "#{@asset_destination_base_prefix}-#{asset_stamp}#{@asset_destination_extension}"
54
+ @basenamer.call @asset_destination_base_prefix, @asset_destination_extension, -> { asset_stamp }
43
55
  end
44
56
 
45
57
  def asset_stamp
46
58
  @asset_stamp ||= AssetStamp.from_file(path)
47
59
  end
48
-
49
- def clear_asset_stamp
50
- @asset_stamp = nil
51
- end
52
-
53
- def update_mtime
54
- @@mtimes[path] = mtime
55
- end
56
60
  end
57
61
  end
@@ -1,5 +1,5 @@
1
1
  module Jekyll
2
2
  module Minibundle
3
- VERSION = '1.0.0'
3
+ VERSION = '1.1.0'
4
4
  end
5
5
  end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ filename = ARGV.first
4
+
5
+ count = if File.exists? filename
6
+ File.read(filename).strip.to_i
7
+ else
8
+ 0
9
+ end
10
+
11
+ File.write filename, "#{count + 1}\n"
12
+
13
+ exec(*ARGV[1..-1])
@@ -5,8 +5,8 @@ module Jekyll::Minibundle::Test
5
5
  class MiniBundleDevelopmentModeTest < TestCase
6
6
  include FixtureConfig
7
7
 
8
- EXPECTED_CSS_ASSET_PATHS = %w{reset common}.map { |f| File.join(CSS_BUNDLE_DESTINATION_DIR, "#{f}.css") }
9
- EXPECTED_JS_ASSET_PATHS = %w{dependency app}.map { |f| File.join(JS_BUNDLE_DESTINATION_DIR, "#{f}.js") }
8
+ EXPECTED_CSS_ASSET_PATHS = %w{reset common}.map { |f| File.join(CSS_BUNDLE_DESTINATION_PATH, "#{f}.css") }
9
+ EXPECTED_JS_ASSET_PATHS = %w{dependency app}.map { |f| File.join(JS_BUNDLE_DESTINATION_PATH, "#{f}.js") }
10
10
 
11
11
  def test_css_assets_have_tags_in_configured_order
12
12
  with_precompiled_site :development do
@@ -38,35 +38,38 @@ module Jekyll::Minibundle::Test
38
38
 
39
39
  def test_changing_css_asset_copies_it_to_destination_dir
40
40
  with_site do
41
- destination = destination_path CSS_BUNDLE_DESTINATION_DIR, 'common.css'
41
+ generate_site :development
42
+ destination = destination_path CSS_BUNDLE_DESTINATION_PATH, 'common.css'
42
43
  source = source_path CSS_BUNDLE_SOURCE_DIR, 'common.css'
44
+ ensure_file_mtime_changes { File.write source, 'h1 {}' }
43
45
 
44
- generate_site :development
45
- ensure_file_mtime_change { File.write source, 'h1 {}' }
46
46
  refute_equal File.read(destination), File.read(source)
47
+
47
48
  generate_site :development
49
+
48
50
  assert_equal File.read(destination), File.read(source)
49
51
  end
50
52
  end
51
53
 
52
54
  def test_changing_js_asset_copies_it_to_destination_dir
53
55
  with_site do
54
- destination = destination_path JS_BUNDLE_DESTINATION_DIR, 'app.js'
56
+ generate_site :development
57
+ destination = destination_path JS_BUNDLE_DESTINATION_PATH, 'app.js'
55
58
  source = source_path JS_BUNDLE_SOURCE_DIR, 'app.js'
59
+ ensure_file_mtime_changes { File.write source, '(function() {})()' }
56
60
 
57
- generate_site :development
58
- ensure_file_mtime_change { File.write source, '(function() {})()' }
59
61
  refute_equal File.read(destination), File.read(source)
62
+
60
63
  generate_site :development
64
+
61
65
  assert_equal File.read(destination), File.read(source)
62
66
  end
63
67
  end
64
68
 
65
69
  def test_supports_relative_and_absolute_destination_paths
66
70
  with_site do
67
- expected_css_path = destination_path CSS_BUNDLE_DESTINATION_DIR, 'common.css'
68
- expected_js_path = destination_path JS_BUNDLE_DESTINATION_DIR, 'app.js'
69
-
71
+ expected_css_path = destination_path CSS_BUNDLE_DESTINATION_PATH, 'common.css'
72
+ expected_js_path = destination_path JS_BUNDLE_DESTINATION_PATH, 'app.js'
70
73
  generate_site :development
71
74
 
72
75
  assert File.exists?(expected_css_path)
@@ -75,7 +78,6 @@ module Jekyll::Minibundle::Test
75
78
  assert_equal 'assets/site/app.js', find_js_paths_from_index.last
76
79
 
77
80
  find_and_gsub_in_file source_path('index.html'), 'destination_path: assets/site', 'destination_path: /assets/site'
78
-
79
81
  generate_site :development
80
82
 
81
83
  assert File.exists?(expected_css_path)
@@ -94,6 +96,23 @@ module Jekyll::Minibundle::Test
94
96
  end
95
97
  end
96
98
 
99
+ def test_do_not_copy_source_when_other_files_change
100
+ with_site do
101
+ generate_site :development
102
+ expected_js_path = destination_path JS_BUNDLE_DESTINATION_PATH, 'app.js'
103
+ org_mtime = mtime_of expected_js_path
104
+ ensure_file_mtime_changes { File.write source_path(JS_BUNDLE_SOURCE_DIR, 'dependency.js'), '(function() {})()' }
105
+ generate_site :development
106
+
107
+ assert_equal org_mtime, mtime_of(expected_js_path)
108
+
109
+ ensure_file_mtime_changes { FileUtils.touch 'index.html' }
110
+ generate_site :development
111
+
112
+ assert_equal org_mtime, mtime_of(expected_js_path)
113
+ end
114
+ end
115
+
97
116
  private
98
117
 
99
118
  def find_css_paths_from_index
@@ -112,10 +131,5 @@ module Jekyll::Minibundle::Test
112
131
  assert File.exists?(actual)
113
132
  assert_equal File.read(expected), File.read(actual)
114
133
  end
115
-
116
- def ensure_file_mtime_change(&block)
117
- sleep 1.5 # ensures file mtime gets changed
118
- yield
119
- end
120
134
  end
121
135
  end
@@ -5,12 +5,9 @@ module Jekyll::Minibundle::Test
5
5
  class MiniBundleProductionModeTest < TestCase
6
6
  include FixtureConfig
7
7
 
8
- EXPECTED_CSS_ASSET_PATH = 'assets/site-b2e0ecc1c100effc2c7353caad20c327.css'
9
- EXPECTED_JS_ASSET_PATH = 'assets/site-4782a1f67803038d4f8351051e67deb8.js'
10
-
11
8
  def test_css_asset_bundle_has_configured_attributes
12
9
  with_precompiled_site :production do
13
- element = find_html_element_from_index %{head link[href="#{EXPECTED_CSS_ASSET_PATH}"]}
10
+ element = find_html_element_from_index %{head link[href="#{EXPECTED_CSS_BUNDLE_PATH}"]}
14
11
  assert_equal 'my-styles', element['id']
15
12
  assert_equal 'projection', element['media']
16
13
  end
@@ -18,52 +15,52 @@ module Jekyll::Minibundle::Test
18
15
 
19
16
  def test_js_asset_bundle_has_configured_attributes
20
17
  with_precompiled_site :production do
21
- element = find_html_element_from_index %{body script[src="#{EXPECTED_JS_ASSET_PATH}"]}
18
+ element = find_html_element_from_index %{body script[src="#{EXPECTED_JS_BUNDLE_PATH}"]}
22
19
  assert_equal 'my-scripts', element['id']
23
20
  end
24
21
  end
25
22
 
26
23
  def test_css_asset_bundle_has_stamp
27
24
  with_precompiled_site :production do
28
- assert_equal EXPECTED_CSS_ASSET_PATH, find_css_path_from_index
25
+ assert_equal EXPECTED_CSS_BUNDLE_PATH, find_css_path_from_index
29
26
  end
30
27
  end
31
28
 
32
29
  def test_js_asset_bundle_has_stamp
33
30
  with_precompiled_site :production do
34
- assert_equal EXPECTED_JS_ASSET_PATH, find_js_path_from_index
31
+ assert_equal EXPECTED_JS_BUNDLE_PATH, find_js_path_from_index
35
32
  end
36
33
  end
37
34
 
38
35
  def test_css_asset_bundle_is_copied_to_destination_dir
39
36
  with_precompiled_site :production do
40
- assert File.exists?(destination_path(EXPECTED_CSS_ASSET_PATH))
37
+ assert File.exists?(destination_path(EXPECTED_CSS_BUNDLE_PATH))
41
38
  end
42
39
  end
43
40
 
44
41
  def test_js_asset_bundle_is_copied_to_destination_dir
45
42
  with_precompiled_site :production do
46
- assert File.exists?(destination_path(EXPECTED_JS_ASSET_PATH))
43
+ assert File.exists?(destination_path(EXPECTED_JS_BUNDLE_PATH))
47
44
  end
48
45
  end
49
46
 
50
47
  def test_css_asset_bundle_is_concatenated_in_configured_order
51
48
  with_precompiled_site :production do
52
- bundle = File.read(destination_path(EXPECTED_CSS_ASSET_PATH))
49
+ bundle = File.read(destination_path(EXPECTED_CSS_BUNDLE_PATH))
53
50
  assert_operator bundle.index('html { margin: 0; }'), :<, bundle.index('p { margin: 0; }')
54
51
  end
55
52
  end
56
53
 
57
54
  def test_js_asset_bundle_is_concatenated_in_configured_order
58
55
  with_precompiled_site :production do
59
- bundle = File.read(destination_path(EXPECTED_JS_ASSET_PATH))
56
+ bundle = File.read(destination_path(EXPECTED_JS_BUNDLE_PATH))
60
57
  assert_operator bundle.index('root.dependency = {};'), :<, bundle.index('root.app = {};')
61
58
  end
62
59
  end
63
60
 
64
61
  def test_js_asset_bundle_has_inserted_semicolons_between_assets
65
62
  with_precompiled_site :production do
66
- bundle = File.read(destination_path(EXPECTED_JS_ASSET_PATH))
63
+ bundle = File.read(destination_path(EXPECTED_JS_BUNDLE_PATH))
67
64
  assert_match(%r|}\)\(window\)\n;\n\(function|, bundle)
68
65
  end
69
66
  end
@@ -71,7 +68,7 @@ module Jekyll::Minibundle::Test
71
68
  def test_css_asset_bundle_is_minified
72
69
  with_precompiled_site :production do
73
70
  source_contents_size = source_assets_size(CSS_BUNDLE_SOURCE_DIR, %w{reset common}, 'css')
74
- destination_contents_size = File.read(destination_path(EXPECTED_CSS_ASSET_PATH)).size
71
+ destination_contents_size = File.read(destination_path(EXPECTED_CSS_BUNDLE_PATH)).size
75
72
  assert_operator destination_contents_size, :<, source_contents_size
76
73
  end
77
74
  end
@@ -79,7 +76,7 @@ module Jekyll::Minibundle::Test
79
76
  def test_js_asset_bundle_is_minified
80
77
  with_precompiled_site :production do
81
78
  source_contents_size = source_assets_size(JS_BUNDLE_SOURCE_DIR, %w{dependency app}, 'js')
82
- destination_contents_size = File.read(destination_path(EXPECTED_JS_ASSET_PATH)).size
79
+ destination_contents_size = File.read(destination_path(EXPECTED_JS_BUNDLE_PATH)).size
83
80
  assert_operator destination_contents_size, :<, source_contents_size
84
81
  end
85
82
  end
@@ -87,11 +84,16 @@ module Jekyll::Minibundle::Test
87
84
  def test_changing_css_assets_changes_bundle
88
85
  with_site do
89
86
  generate_site :production
90
- assert File.exists?(destination_path(EXPECTED_CSS_ASSET_PATH))
91
- File.write source_path('_assets/styles/common.css'), 'h1 {}'
92
- generate_site :production
93
- refute File.exists?(destination_path(EXPECTED_CSS_ASSET_PATH))
87
+
88
+ assert File.exists?(destination_path(EXPECTED_CSS_BUNDLE_PATH))
89
+
90
+ ensure_file_mtime_changes { File.write source_path(CSS_BUNDLE_SOURCE_DIR, 'common.css'), 'h1 {}' }
91
+ generate_site :production, clear_cache: false
92
+
93
+ refute File.exists?(destination_path(EXPECTED_CSS_BUNDLE_PATH))
94
+
94
95
  expected_new_path = 'assets/site-9fd3995d6f0fce425db81c3691dfe93f.css'
96
+
95
97
  assert_equal expected_new_path, find_css_path_from_index
96
98
  assert File.exists?(destination_path(expected_new_path))
97
99
  end
@@ -100,11 +102,16 @@ module Jekyll::Minibundle::Test
100
102
  def test_changing_js_assets_changes_bundle
101
103
  with_site do
102
104
  generate_site :production
103
- assert File.exists?(destination_path(EXPECTED_JS_ASSET_PATH))
104
- File.write source_path('_assets/scripts/app.js'), '(function() {})()'
105
- generate_site :production
106
- refute File.exists?(destination_path(EXPECTED_JS_ASSET_PATH))
105
+
106
+ assert File.exists?(destination_path(EXPECTED_JS_BUNDLE_PATH))
107
+
108
+ ensure_file_mtime_changes { File.write source_path(JS_BUNDLE_SOURCE_DIR, 'app.js'), '(function() {})()' }
109
+ generate_site :production, clear_cache: false
110
+
111
+ refute File.exists?(destination_path(EXPECTED_JS_BUNDLE_PATH))
112
+
107
113
  expected_new_path = 'assets/site-375a0b430b0c5555d0edd2205d26c04d.js'
114
+
108
115
  assert_equal expected_new_path, find_js_path_from_index
109
116
  assert File.exists?(destination_path(expected_new_path))
110
117
  end
@@ -112,24 +119,55 @@ module Jekyll::Minibundle::Test
112
119
 
113
120
  def test_supports_relative_and_absolute_destination_paths
114
121
  with_site do
115
- expected_css_path = destination_path EXPECTED_CSS_ASSET_PATH
116
- expected_js_path = destination_path EXPECTED_JS_ASSET_PATH
117
-
118
122
  generate_site :production
123
+ expected_css_path = destination_path EXPECTED_CSS_BUNDLE_PATH
124
+ expected_js_path = destination_path EXPECTED_JS_BUNDLE_PATH
119
125
 
120
126
  assert File.exists?(expected_css_path)
121
127
  assert File.exists?(expected_js_path)
122
- assert_equal EXPECTED_CSS_ASSET_PATH, find_css_path_from_index
123
- assert_equal EXPECTED_JS_ASSET_PATH, find_js_path_from_index
128
+ assert_equal EXPECTED_CSS_BUNDLE_PATH, find_css_path_from_index
129
+ assert_equal EXPECTED_JS_BUNDLE_PATH, find_js_path_from_index
124
130
 
125
131
  find_and_gsub_in_file source_path('index.html'), 'destination_path: assets/site', 'destination_path: /assets/site'
126
-
127
132
  generate_site :production
128
133
 
129
134
  assert File.exists?(expected_css_path)
130
135
  assert File.exists?(expected_js_path)
131
- assert_equal "/#{EXPECTED_CSS_ASSET_PATH}", find_css_path_from_index
132
- assert_equal "/#{EXPECTED_JS_ASSET_PATH}", find_js_path_from_index
136
+ assert_equal "/#{EXPECTED_CSS_BUNDLE_PATH}", find_css_path_from_index
137
+ assert_equal "/#{EXPECTED_JS_BUNDLE_PATH}", find_js_path_from_index
138
+ end
139
+ end
140
+
141
+ def test_bundles_assets_only_once_upon_startup
142
+ with_site do
143
+ with_env 'JEKYLL_MINIBUNDLE_CMD_JS' => cmd_to_remove_comments_and_count do
144
+ generate_site :production
145
+ end
146
+ assert_equal 1, get_cmd_count
147
+ end
148
+ end
149
+
150
+ def test_do_not_bundle_assets_when_nonsource_files_change
151
+ with_site do
152
+ with_env 'JEKYLL_MINIBUNDLE_CMD_JS' => cmd_to_remove_comments_and_count do
153
+ generate_site :production
154
+ expected_js_path = destination_path EXPECTED_JS_BUNDLE_PATH
155
+ last_mtime = mtime_of expected_js_path
156
+
157
+ assert_equal 1, get_cmd_count
158
+
159
+ ensure_file_mtime_changes { File.write source_path(CSS_BUNDLE_SOURCE_DIR, 'common.css'), 'h1 {}' }
160
+ generate_site :production, clear_cache: false
161
+
162
+ assert_equal last_mtime, mtime_of(expected_js_path)
163
+ assert_equal 1, get_cmd_count
164
+
165
+ ensure_file_mtime_changes { FileUtils.touch 'index.html' }
166
+ generate_site :production, clear_cache: false
167
+
168
+ assert_equal last_mtime, mtime_of(expected_js_path)
169
+ assert_equal 1, get_cmd_count
170
+ end
133
171
  end
134
172
  end
135
173