hanami-assets 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +49 -25
- data/bin/hanami-assets +4 -4
- data/hanami-assets.gemspec +7 -7
- data/lib/hanami/assets/bundler/asset.rb +98 -0
- data/lib/hanami/assets/bundler/compressor.rb +61 -0
- data/lib/hanami/assets/bundler/manifest_entry.rb +62 -0
- data/lib/hanami/assets/bundler.rb +35 -62
- data/lib/hanami/assets/cache.rb +58 -15
- data/lib/hanami/assets/compiler.rb +66 -45
- data/lib/hanami/assets/compilers/less.rb +29 -0
- data/lib/hanami/assets/compilers/sass.rb +46 -0
- data/lib/hanami/assets/compressors/sass_stylesheet.rb +2 -2
- data/lib/hanami/assets/config/global_sources.rb +1 -1
- data/lib/hanami/assets/config/manifest.rb +23 -3
- data/lib/hanami/assets/config/sources.rb +8 -3
- data/lib/hanami/assets/configuration.rb +90 -23
- data/lib/hanami/assets/helpers.rb +81 -9
- data/lib/hanami/assets/precompiler.rb +31 -8
- data/lib/hanami/assets/version.rb +1 -1
- data/lib/hanami/assets.rb +12 -10
- metadata +15 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ac62b03f4a875750283f436f0c830152e9decba
|
4
|
+
data.tar.gz: 31d8b0704edd9bf86756ebe3ca481226e12f1c73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 906de34660db373299b9e779a770c67a7192b209927050f4743f4b58510df4e4be9c8f51c13a4db2b3b1a8b6c89628f6fee087526cbd31b51071bfcd78500539
|
7
|
+
data.tar.gz: fd378bbdf3479319c9d8b0a5e1aa53384551eef44b7c9c6ed746397c4114d3efb78c25f14711d5221194ae19bf44c5d450a3a882b8b30ea6516061b44b9d7884
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,20 @@
|
|
1
1
|
# Hanami::Assets
|
2
2
|
Assets management for Ruby web applications
|
3
3
|
|
4
|
+
## v0.3.0 - 2016-07-22
|
5
|
+
### Added
|
6
|
+
- [Matthew Gibbons & Sean Collins] Subresource Integrity (SRI)
|
7
|
+
- [Matthew Gibbons & Sean Collins] Allow `javascript` and `stylesheet` helpers to accept a Hash representing HTML attributes. Eg. `<%= javascript 'application', async: true %>`
|
8
|
+
|
9
|
+
### Fixed
|
10
|
+
- [Alexander Gräfe] Safely precompile assets from directories with a dot in their name.
|
11
|
+
- [Luca Guidi] Detect changes for Sass/SCSS dependencies.
|
12
|
+
- [Maxim Dorofienko & Luca Guidi] Preserve static assets under public directory, by removing only assets directory and manifest at the precompile time.
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
– [Luca Guidi] Drop support for Ruby 2.0 and 2.1. Official support for JRuby 9.0.5.0+.
|
16
|
+
- [Luca Guidi] Don't create digest version of files under public directory, but only for precompiled files.
|
17
|
+
|
4
18
|
## v0.2.1 - 2016-02-05
|
5
19
|
### Changed
|
6
20
|
- [Derk-Jan Karrenbeld] Don't precompile `.map` files
|
data/README.md
CHANGED
@@ -51,9 +51,9 @@ $ gem install hanami-assets
|
|
51
51
|
|
52
52
|
### Helpers
|
53
53
|
|
54
|
-
|
54
|
+
`Hanami::Assets` provides asset-specific helpers to be used in templates.
|
55
55
|
They resolve one or multiple sources into corresponding HTML tags.
|
56
|
-
Those sources can be
|
56
|
+
Those sources can be either a name of a local asset or an absolute URL.
|
57
57
|
|
58
58
|
Given the following template:
|
59
59
|
|
@@ -73,7 +73,7 @@ Given the following template:
|
|
73
73
|
</html>
|
74
74
|
```
|
75
75
|
|
76
|
-
It will output this markup
|
76
|
+
It will output this markup:
|
77
77
|
|
78
78
|
```html
|
79
79
|
<!doctype HTML>
|
@@ -124,7 +124,7 @@ For advanced configurations, please have a look at
|
|
124
124
|
|
125
125
|
### Available Helpers
|
126
126
|
|
127
|
-
This
|
127
|
+
This gem ships with the following helpers:
|
128
128
|
|
129
129
|
* `javascript`
|
130
130
|
* `stylesheet`
|
@@ -191,10 +191,10 @@ that file would be copied into the public directory instead of the one under
|
|
191
191
|
`Hanami::Assets` is able to run assets preprocessors and **lazily compile** them
|
192
192
|
under `public/assets` (by default), before the markup is generated.
|
193
193
|
|
194
|
-
Imagine
|
194
|
+
Imagine you have `main.css.scss` under `app/assets/stylesheets` and `reset.css` under
|
195
195
|
`vendor/stylesheets`.
|
196
196
|
|
197
|
-
**The extensions
|
197
|
+
**The two extensions are important.**
|
198
198
|
The first one is mandatory and it's used to understand which asset type we are
|
199
199
|
handling: `.css` for stylesheets.
|
200
200
|
The second one is optional and it's for a preprocessor: `.scss` for Sass.
|
@@ -213,13 +213,13 @@ Hanami::Assets.configure do
|
|
213
213
|
end
|
214
214
|
```
|
215
215
|
|
216
|
-
|
216
|
+
And in a template you can use the `stylesheet` helper:
|
217
217
|
|
218
218
|
```erb
|
219
219
|
<%= stylesheet 'reset', 'main' %>
|
220
220
|
```
|
221
221
|
|
222
|
-
Your public directory will
|
222
|
+
Your public directory will look like this:
|
223
223
|
|
224
224
|
```shell
|
225
225
|
% tree public
|
@@ -231,23 +231,25 @@ public/
|
|
231
231
|
|
232
232
|
### Preprocessors engines
|
233
233
|
|
234
|
-
`Hanami::Assets` uses [Tilt](https://github.com/rtomayko/tilt) to provide support for the most common preprocessors, such as [Sass](http://sass-lang.com/) (including `sassc-ruby`), [Less](http://lesscss.org/), ES6, [JSX](https://jsx.github.io/), [CoffeScript](http://coffeescript.org), [Opal](http://opalrb.org), [Handlebars](http://handlebarsjs.com), [JBuilder](https://github.com/rails/jbuilder).
|
234
|
+
`Hanami::Assets` uses [Tilt](https://github.com/rtomayko/tilt) to provide support for the most common preprocessors, such as [Sass](http://sass-lang.com/) (including `sassc-ruby`), [Less](http://lesscss.org/), [ES6](https://babeljs.io/), [JSX](https://jsx.github.io/), [CoffeScript](http://coffeescript.org), [Opal](http://opalrb.org), [Handlebars](http://handlebarsjs.com), [JBuilder](https://github.com/rails/jbuilder).
|
235
235
|
|
236
|
-
In order to use one or more of them, be sure to
|
236
|
+
In order to use one or more of them, be sure to add the corresponding gem to your `Gemfile` and require the library.
|
237
237
|
|
238
238
|
#### EcmaScript 6
|
239
239
|
|
240
|
-
We strongly suggest
|
241
|
-
It isn't fully [supported](https://kangax.github.io/compat-table/es6/) yet by
|
240
|
+
We strongly suggest you use [EcmaScript 6](http://es6-features.org/) for your next project.
|
241
|
+
It isn't fully [supported](https://kangax.github.io/compat-table/es6/) yet by browsers, but it's the future of JavaScript.
|
242
242
|
|
243
|
-
As of today, you need to transpile ES6 code into
|
244
|
-
|
243
|
+
As of today, you need to 'transpile' ES6 code into ES5, which current browsers understand.
|
244
|
+
The most popular tool for this is [Babel](https://babeljs.io), which we support.
|
245
245
|
|
246
246
|
### Deployment
|
247
247
|
|
248
248
|
`Hanami::Assets` ships with an executable (`hanami-assets`), which can be used to precompile assets and make them cacheable by browsers (via checksum suffix).
|
249
249
|
|
250
|
-
|
250
|
+
__NOTE__: If you're using `Hanami::Assets` with the full `Hanami` framework, you should use `bundle exec hanami assets precompile` instead of `hanami-assets`.
|
251
|
+
|
252
|
+
Let's say we have an application that has a main file that requires the entire codebase (`config/environment.rb`), a gem that brings in Ember.js code, and the following sources:
|
251
253
|
|
252
254
|
```shell
|
253
255
|
% tree .
|
@@ -321,14 +323,14 @@ public
|
|
321
323
|
|
322
324
|
#### Compressors
|
323
325
|
|
324
|
-
Minification is a process that
|
325
|
-
The goal of this step
|
326
|
+
Minification is a process that shrinks file size in production, by removing unnecessary spaces and characters.
|
327
|
+
The goal of this step is to have lighter assets, which will be served faster to browsers.
|
326
328
|
|
327
|
-
Hanami supports JavaScript and
|
329
|
+
Hanami supports JavaScript and stylesheet minifiers.
|
328
330
|
|
329
331
|
Because this framework relies on external gems for minification, this feature is **turned off by default**.
|
330
332
|
|
331
|
-
To
|
333
|
+
To use minification, we need to specify which gem we want to use and add it to our `Gemfile`.
|
332
334
|
|
333
335
|
##### JavaScript Compressors
|
334
336
|
|
@@ -347,7 +349,7 @@ end
|
|
347
349
|
|
348
350
|
##### Stylesheet Compressors
|
349
351
|
|
350
|
-
Hanami can use the following compressors (aka minifiers) for
|
352
|
+
Hanami can use the following compressors (aka minifiers) for stylesheets.
|
351
353
|
|
352
354
|
* `:builtin` - Ruby based compressor. It doesn't require any external gem. It's fast, but not an efficient compressor.
|
353
355
|
* `:yui` - [YUI Compressor](http://yui.github.io/yuicompressor), it depends on [`yui-compressor`](https://rubygems.org/gems/yui-compressor) gem and it requires Java 1.4+
|
@@ -372,7 +374,9 @@ end
|
|
372
374
|
|
373
375
|
### Digest Mode
|
374
376
|
|
375
|
-
This is a mode that can be activated via
|
377
|
+
This is a mode that can be activated via configuration and it's suitable for production environments.
|
378
|
+
When generating files, it adds a string to the end of each file name, which is a cachesum of its contents.
|
379
|
+
This lets you leverage caching while still ensuring that clients get the most up-to-date assets (this is known as *cache busting*).
|
376
380
|
|
377
381
|
```ruby
|
378
382
|
Hanami::Assets.configure do
|
@@ -390,9 +394,29 @@ Once turned on, it will look at `/public/assets.json`, and helpers such as `java
|
|
390
394
|
<script src="/assets/application-d1829dc353b734e3adc24855693b70f9.js" type="text/javascript"></script>
|
391
395
|
```
|
392
396
|
|
397
|
+
### Subresource Integrity (SRI) Mode
|
398
|
+
|
399
|
+
This is a mode that can be activated via the configuration and it's suitable for production environments.
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
Hanami::Assets.configure do
|
403
|
+
subresource_integrity true
|
404
|
+
end
|
405
|
+
```
|
406
|
+
|
407
|
+
Once turned on, it will look at `/public/assets.json`, and helpers such as `javascript` will include an `integrity` and `crossorigin` attribute.
|
408
|
+
|
409
|
+
```erb
|
410
|
+
<%= javascript 'application' %>
|
411
|
+
```
|
412
|
+
|
413
|
+
```html
|
414
|
+
<script src="/assets/application-d1829dc353b734e3adc24855693b70f9.js" type="text/javascript" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" crossorigin="anonymous"></script>
|
415
|
+
```
|
416
|
+
|
393
417
|
### CDN Mode
|
394
418
|
|
395
|
-
A Hanami project can serve assets via CDN.
|
419
|
+
A Hanami project can serve assets via a Content Delivery Network (CDN).
|
396
420
|
|
397
421
|
```ruby
|
398
422
|
Hanami::Assets.configure do
|
@@ -403,7 +427,7 @@ Hanami::Assets.configure do
|
|
403
427
|
end
|
404
428
|
```
|
405
429
|
|
406
|
-
|
430
|
+
From now on, helpers will return the absolute URL for the asset, hosted on the CDN specified.
|
407
431
|
|
408
432
|
```erb
|
409
433
|
<%= javascript 'application' %>
|
@@ -417,7 +441,7 @@ Since now on, helpers will return the CDN absolute URL for the asset.
|
|
417
441
|
|
418
442
|
Developers can maintain gems that distribute assets for Hanami. For instance `hanami-ember` or `hanami-jquery`.
|
419
443
|
|
420
|
-
|
444
|
+
To do this, inside your gem you have tell `Hanami::Assets` where to look for assets:
|
421
445
|
|
422
446
|
```ruby
|
423
447
|
# lib/hanami/jquery.rb
|
@@ -428,7 +452,7 @@ Hanami::Assets.sources << '/path/to/jquery'
|
|
428
452
|
|
429
453
|
* Make sure you have one of [ExecJS](https://github.com/rails/execjs)
|
430
454
|
supported runtime on your machine.
|
431
|
-
* Java 1.4+
|
455
|
+
* Java 1.4+ (for YUI Compressor and Google Closure Compiler)
|
432
456
|
|
433
457
|
```sh
|
434
458
|
bundle exec rake test
|
data/bin/hanami-assets
CHANGED
@@ -5,16 +5,16 @@ require 'pathname'
|
|
5
5
|
|
6
6
|
options = {}
|
7
7
|
OptionParser.new do |opts|
|
8
|
-
opts.banner =
|
8
|
+
opts.banner = 'Usage: hanami-assets --config=path/to/config.rb'
|
9
9
|
|
10
|
-
opts.on(
|
10
|
+
opts.on('-c', '--config FILE', 'Path to config') do |c|
|
11
11
|
options[:config] = c
|
12
12
|
end
|
13
13
|
end.parse!
|
14
14
|
|
15
|
-
config = options.fetch(:config) { raise ArgumentError.new(
|
15
|
+
config = options.fetch(:config) { raise ArgumentError.new('You must specify a configuration file') }
|
16
16
|
config = Pathname.new(config)
|
17
|
-
config.exist? or raise ArgumentError.new("Cannot find configuration file: #{
|
17
|
+
config.exist? or raise ArgumentError.new("Cannot find configuration file: #{config}") # rubocop:disable Style/AndOr
|
18
18
|
|
19
19
|
require 'hanami/assets'
|
20
20
|
load config
|
data/hanami-assets.gemspec
CHANGED
@@ -8,23 +8,23 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Hanami::Assets::VERSION
|
9
9
|
spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda']
|
10
10
|
spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
11
|
+
spec.summary = 'Assets management'
|
12
|
+
spec.description = 'Assets management for Ruby web applications'
|
13
13
|
spec.homepage = 'http://hanamirb.org'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files = `git ls-files -- lib/* bin/* CHANGELOG.md LICENSE.md README.md hanami-assets.gemspec`.split($/)
|
16
|
+
spec.files = `git ls-files -- lib/* bin/* CHANGELOG.md LICENSE.md README.md hanami-assets.gemspec`.split($/) # rubocop:disable Style/SpecialGlobalVars
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
|
-
spec.required_ruby_version = '>= 2.
|
20
|
+
spec.required_ruby_version = '>= 2.2.0'
|
21
21
|
|
22
|
-
spec.add_runtime_dependency 'hanami-utils', '~> 0.
|
23
|
-
spec.add_runtime_dependency 'hanami-helpers', '~> 0.
|
22
|
+
spec.add_runtime_dependency 'hanami-utils', '~> 0.8'
|
23
|
+
spec.add_runtime_dependency 'hanami-helpers', '~> 0.4'
|
24
24
|
spec.add_runtime_dependency 'tilt', '~> 2.0', '>= 2.0.2'
|
25
25
|
|
26
26
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
27
|
-
spec.add_development_dependency 'rake', '~>
|
27
|
+
spec.add_development_dependency 'rake', '~> 11'
|
28
28
|
spec.add_development_dependency 'minitest', '~> 5'
|
29
29
|
|
30
30
|
spec.add_development_dependency 'yui-compressor', '~> 0.12'
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module Assets
|
5
|
+
# Bundle assets from a single application.
|
6
|
+
#
|
7
|
+
# @since 0.1.0
|
8
|
+
# @api private
|
9
|
+
class Bundler
|
10
|
+
# @since 0.3.0
|
11
|
+
# @api private
|
12
|
+
class Asset
|
13
|
+
# @since 0.3.0
|
14
|
+
# @api private
|
15
|
+
attr_reader :path
|
16
|
+
|
17
|
+
# @since 0.3.0
|
18
|
+
# @api private
|
19
|
+
attr_reader :configuration
|
20
|
+
|
21
|
+
# @since 0.3.0
|
22
|
+
# @api private
|
23
|
+
WILDCARD_EXT = '.*'.freeze
|
24
|
+
|
25
|
+
# Return a new instance
|
26
|
+
#
|
27
|
+
# @since 0.3.0
|
28
|
+
# @api private
|
29
|
+
def initialize(path, configuration)
|
30
|
+
@path = path
|
31
|
+
@configuration = configuration
|
32
|
+
end
|
33
|
+
|
34
|
+
# @since 0.3.0
|
35
|
+
# @api private
|
36
|
+
def expanded_path
|
37
|
+
::File.expand_path(@path)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @since 0.3.0
|
41
|
+
# @api private
|
42
|
+
def fingerprinted_target
|
43
|
+
::File.join(directory, "#{filename}-#{fingerprint}#{extension}")
|
44
|
+
end
|
45
|
+
|
46
|
+
# @since 0.3.0
|
47
|
+
# @api private
|
48
|
+
def expanded_fingerprinted_target
|
49
|
+
::File.expand_path(fingerprinted_target)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @since 0.3.0
|
53
|
+
# @api private
|
54
|
+
def base64_digest(algorithm)
|
55
|
+
raw_digest(algorithm).base64digest
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# @since 0.3.0
|
61
|
+
# @api private
|
62
|
+
def directory
|
63
|
+
::File.dirname(@path)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @since 0.3.0
|
67
|
+
# @api private
|
68
|
+
def filename
|
69
|
+
::File.basename(@path, WILDCARD_EXT)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @since 0.3.0
|
73
|
+
# @api private
|
74
|
+
def extension
|
75
|
+
::File.extname(@path)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @since 0.3.0
|
79
|
+
# @api private
|
80
|
+
def fingerprint
|
81
|
+
raw_digest(:md5).hexdigest
|
82
|
+
end
|
83
|
+
|
84
|
+
# @since 0.3.0
|
85
|
+
# @api private
|
86
|
+
def raw_digest(algorithm)
|
87
|
+
OpenSSL::Digest.new(algorithm.to_s, contents)
|
88
|
+
end
|
89
|
+
|
90
|
+
# @since 0.3.0
|
91
|
+
# @api private
|
92
|
+
def contents
|
93
|
+
::File.read(@path)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Hanami
|
2
|
+
module Assets
|
3
|
+
class Bundler
|
4
|
+
# Compresses a JS or CSS file
|
5
|
+
#
|
6
|
+
# @since 0.3.0
|
7
|
+
# @api private
|
8
|
+
class Compressor
|
9
|
+
# @since 0.3.0
|
10
|
+
# @api private
|
11
|
+
JAVASCRIPT_EXT = '.js'.freeze
|
12
|
+
|
13
|
+
# @since 0.3.0
|
14
|
+
# @api private
|
15
|
+
STYLESHEET_EXT = '.css'.freeze
|
16
|
+
|
17
|
+
# Return a new instance
|
18
|
+
#
|
19
|
+
# @since 0.3.0
|
20
|
+
# @api private
|
21
|
+
def initialize(path, configuration)
|
22
|
+
@path = path
|
23
|
+
@configuration = configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [String, nil] the compressed contents of the file OR nil if it's not compressable
|
27
|
+
#
|
28
|
+
# @since 0.3.0
|
29
|
+
# @api private
|
30
|
+
def compress
|
31
|
+
case File.extname(@path)
|
32
|
+
when JAVASCRIPT_EXT then _compress(compressor(:js))
|
33
|
+
when STYLESHEET_EXT then _compress(compressor(:css))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @since 0.3.0
|
40
|
+
# @api private
|
41
|
+
def compressor(type)
|
42
|
+
@configuration.__send__(:"#{ type }_compressor")
|
43
|
+
end
|
44
|
+
|
45
|
+
# @since 0.3.0
|
46
|
+
# @api private
|
47
|
+
def _compress(compressor)
|
48
|
+
compressor.compress(@path)
|
49
|
+
rescue => e
|
50
|
+
warn(
|
51
|
+
[
|
52
|
+
"Skipping compression of: `#{@path}'",
|
53
|
+
"Reason: #{e}\n",
|
54
|
+
"\t#{e.backtrace.join("\n\t")}\n\n"
|
55
|
+
].join("\n")
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Hanami
|
2
|
+
module Assets
|
3
|
+
class Bundler
|
4
|
+
# Constructs a hash for a single asset's manifest file entry
|
5
|
+
#
|
6
|
+
# @since 0.3.0
|
7
|
+
# @api private
|
8
|
+
class ManifestEntry
|
9
|
+
# @since 0.3.0
|
10
|
+
# @api private
|
11
|
+
SUBRESOURCE_INTEGRITY_SEPARATOR = '-'.freeze
|
12
|
+
|
13
|
+
# Return a new instance
|
14
|
+
#
|
15
|
+
# @since 0.3.0
|
16
|
+
# @api private
|
17
|
+
def initialize(asset)
|
18
|
+
@asset = asset
|
19
|
+
end
|
20
|
+
|
21
|
+
# A single entry for this asset, to go into manifest file
|
22
|
+
# @since 0.3.0
|
23
|
+
# @api private
|
24
|
+
def entry
|
25
|
+
{ name => values }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# @since 0.3.0
|
31
|
+
# @api private
|
32
|
+
def name
|
33
|
+
_convert_to_url(@asset.expanded_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @since 0.3.0
|
37
|
+
# @api private
|
38
|
+
def values
|
39
|
+
Hash[
|
40
|
+
target: _convert_to_url(@asset.expanded_fingerprinted_target),
|
41
|
+
sri: subresource_integrity_values
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
# @since 0.3.0
|
46
|
+
# @api private
|
47
|
+
def subresource_integrity_values
|
48
|
+
@asset.configuration.subresource_integrity_algorithms.map do |algorithm|
|
49
|
+
[algorithm, @asset.base64_digest(algorithm)].join(SUBRESOURCE_INTEGRITY_SEPARATOR)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @since 0.3.0
|
54
|
+
# @api private
|
55
|
+
def _convert_to_url(path)
|
56
|
+
path.sub(@asset.configuration.public_directory.to_s, URL_REPLACEMENT)
|
57
|
+
.gsub(File::SEPARATOR, URL_SEPARATOR)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,7 +1,10 @@
|
|
1
|
-
require 'digest'
|
2
1
|
require 'fileutils'
|
3
2
|
require 'json'
|
4
3
|
|
4
|
+
require 'hanami/assets/bundler/compressor'
|
5
|
+
require 'hanami/assets/bundler/asset'
|
6
|
+
require 'hanami/assets/bundler/manifest_entry'
|
7
|
+
|
5
8
|
module Hanami
|
6
9
|
module Assets
|
7
10
|
# Bundle assets from a single application.
|
@@ -11,19 +14,7 @@ module Hanami
|
|
11
14
|
class Bundler
|
12
15
|
# @since 0.1.0
|
13
16
|
# @api private
|
14
|
-
DEFAULT_PERMISSIONS =
|
15
|
-
|
16
|
-
# @since 0.1.0
|
17
|
-
# @api private
|
18
|
-
JAVASCRIPT_EXT = '.js'.freeze
|
19
|
-
|
20
|
-
# @since 0.1.0
|
21
|
-
# @api private
|
22
|
-
STYLESHEET_EXT = '.css'.freeze
|
23
|
-
|
24
|
-
# @since 0.1.0
|
25
|
-
# @api private
|
26
|
-
WILDCARD_EXT = '.*'.freeze
|
17
|
+
DEFAULT_PERMISSIONS = 0o644
|
27
18
|
|
28
19
|
# @since 0.1.0
|
29
20
|
# @api private
|
@@ -45,7 +36,7 @@ module Hanami
|
|
45
36
|
# @since 0.1.0
|
46
37
|
# @api private
|
47
38
|
def initialize(configuration, duplicates)
|
48
|
-
@manifest = Hash
|
39
|
+
@manifest = Hash[]
|
49
40
|
@configuration = configuration
|
50
41
|
@configurations = if duplicates.empty?
|
51
42
|
[@configuration]
|
@@ -60,6 +51,7 @@ module Hanami
|
|
60
51
|
#
|
61
52
|
# * Compress
|
62
53
|
# * Create a checksum version
|
54
|
+
# * Generate an integrity digest
|
63
55
|
#
|
64
56
|
# At the end it will generate a digest manifest
|
65
57
|
#
|
@@ -67,78 +59,57 @@ module Hanami
|
|
67
59
|
# @see Hanami::Assets::Configuration#manifest
|
68
60
|
# @see Hanami::Assets::Configuration#manifest_path
|
69
61
|
def run
|
70
|
-
assets.each do |
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
62
|
+
assets.each do |path|
|
63
|
+
unless File.directory?(path)
|
64
|
+
configuration = _configuration_for(path)
|
65
|
+
process(Asset.new(path, configuration))
|
66
|
+
end
|
75
67
|
end
|
76
68
|
|
77
|
-
|
69
|
+
write_manifest_file
|
78
70
|
end
|
79
71
|
|
80
72
|
private
|
81
73
|
|
82
|
-
# @since 0.
|
74
|
+
# @since 0.3.0
|
83
75
|
# @api private
|
84
|
-
def
|
85
|
-
|
76
|
+
def process(asset)
|
77
|
+
compress_in_place!(asset)
|
78
|
+
copy_to_fingerprinted_location!(asset)
|
79
|
+
@manifest.merge!(ManifestEntry.new(asset).entry)
|
86
80
|
end
|
87
81
|
|
88
|
-
# @since 0.
|
82
|
+
# @since 0.3.0
|
89
83
|
# @api private
|
90
|
-
def
|
91
|
-
|
92
|
-
|
93
|
-
when STYLESHEET_EXT then _compress(compressor(:css, asset), asset)
|
94
|
-
end
|
84
|
+
def copy_to_fingerprinted_location!(asset)
|
85
|
+
FileUtils.cp(asset.path, asset.fingerprinted_target)
|
86
|
+
_set_permissions(asset.fingerprinted_target)
|
95
87
|
end
|
96
88
|
|
97
|
-
# @since 0.
|
89
|
+
# @since 0.3.0
|
98
90
|
# @api private
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
directory = ::File.dirname(asset)
|
103
|
-
target = [directory, "#{ filename }-#{ digest }#{ ext }"].join(::File::SEPARATOR)
|
104
|
-
|
105
|
-
FileUtils.cp(asset, target)
|
106
|
-
_set_permissions(target)
|
107
|
-
|
108
|
-
store_manifest(asset, target)
|
91
|
+
def compress_in_place!(asset)
|
92
|
+
compressed = Compressor.new(asset.path, asset.configuration).compress
|
93
|
+
_write(asset.path, compressed) unless compressed.nil?
|
109
94
|
end
|
110
95
|
|
111
|
-
# @since 0.
|
96
|
+
# @since 0.3.0
|
112
97
|
# @api private
|
113
|
-
def
|
98
|
+
def write_manifest_file
|
114
99
|
_write(@configuration.manifest_path, JSON.dump(@manifest))
|
115
100
|
end
|
116
101
|
|
117
102
|
# @since 0.1.0
|
118
103
|
# @api private
|
119
|
-
def
|
120
|
-
@
|
121
|
-
end
|
122
|
-
|
123
|
-
# @since 0.1.0
|
124
|
-
# @api private
|
125
|
-
def compressor(type, asset)
|
126
|
-
_configuration_for(asset).__send__(:"#{ type }_compressor")
|
127
|
-
end
|
128
|
-
|
129
|
-
# @since 0.1.0
|
130
|
-
# @api private
|
131
|
-
def _compress(compressor, asset)
|
132
|
-
_write(asset, compressor.compress(asset))
|
133
|
-
rescue => e
|
134
|
-
warn "Skipping compression of: `#{ asset }'\nReason: #{ e }\n\t#{ e.backtrace.join("\n\t") }\n\n"
|
104
|
+
def assets
|
105
|
+
Dir.glob("#{@configuration.destination_directory}#{::File::SEPARATOR}**#{::File::SEPARATOR}*")
|
135
106
|
end
|
136
107
|
|
137
108
|
# @since 0.1.0
|
138
109
|
# @api private
|
139
110
|
def _convert_to_url(path)
|
140
|
-
path.sub(public_directory.to_s, URL_REPLACEMENT)
|
141
|
-
|
111
|
+
path.sub(public_directory.to_s, URL_REPLACEMENT)
|
112
|
+
.gsub(File::SEPARATOR, URL_SEPARATOR)
|
142
113
|
end
|
143
114
|
|
144
115
|
# @since 0.1.0
|
@@ -156,10 +127,12 @@ module Hanami
|
|
156
127
|
::File.chmod(DEFAULT_PERMISSIONS, path)
|
157
128
|
end
|
158
129
|
|
130
|
+
# @since 0.3.0
|
131
|
+
# @api private
|
159
132
|
def _configuration_for(asset)
|
160
133
|
url = _convert_to_url(asset)
|
161
134
|
|
162
|
-
@configurations.find {|config| url.start_with?(config.prefix) } ||
|
135
|
+
@configurations.find { |config| url.start_with?(config.prefix) } ||
|
163
136
|
@configuration
|
164
137
|
end
|
165
138
|
|