jekyll-minibundle 1.0.0 → 1.1.0

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