propshaft 0.1.6 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/lib/propshaft/assembly.rb +8 -2
- data/lib/propshaft/asset.rb +15 -2
- data/lib/propshaft/compilers/css_asset_urls.rb +26 -4
- data/lib/propshaft/compilers.rb +1 -1
- data/lib/propshaft/errors.rb +18 -0
- data/lib/propshaft/load_path.rb +19 -1
- data/lib/propshaft/processor.rb +5 -0
- data/lib/propshaft/railtie.rb +24 -3
- data/lib/propshaft/resolver/dynamic.rb +2 -0
- data/lib/propshaft/resolver/static.rb +2 -0
- data/lib/propshaft/server.rb +8 -7
- data/lib/propshaft/version.rb +1 -1
- data/lib/propshaft.rb +5 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b51bb39ebb02eaa2eb0491eb6af28b1d6034d8b6dbed1da92161cc9d6ece2a7
|
4
|
+
data.tar.gz: 80426c2f10fe259bb7aec42c86f15581686680a4bb017117c677df2cc95150e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35ca25b6bc30528fa870343793320c59265819b1154ac14d0f2fefa4288a66c686e63b87ddc264d3ab3a467df1be0399b8bb37a0e3d157f5c5240eb6756db7d8
|
7
|
+
data.tar.gz: 9d3abd851135146a1bacf62c73c755679d4bc76f920b89f969a4f4b34700c09a3334025a15531f85196a420a63c2d5f77f7f7bacdff91ab4c8537117982c0948
|
data/README.md
CHANGED
@@ -24,6 +24,11 @@ These assets can be referenced through their logical path using the normal helpe
|
|
24
24
|
Additionally, Propshaft ships with a CSS function called `asset-path("image.svg")` that'll be compiled into `url("/assets/image-f2e1ec14d6856e1958083094170ca6119c529a73.svg")` when doing `assets:precompile`. This function is applied to all `.css` files.
|
25
25
|
|
26
26
|
|
27
|
+
## Bypassing the digest step
|
28
|
+
|
29
|
+
If you need to put multiple files that refer to each other through Propshaft, like a JavaScript file and its source map, you have to digest these files in advance to retain stable file names. Propshaft looks for the specific pattern of `-[digest].digested.js` as the postfix to any asset file as an indication that the file has already been digested.
|
30
|
+
|
31
|
+
|
27
32
|
## Migrating from Sprockets
|
28
33
|
|
29
34
|
Propshaft does a lot less than Sprockets, by design, so it might well be a fair bit of work to migrate if it's even desirable. This is particularly true if you rely on Sprockets to provide any form of transpiling, like CoffeeScript or Sass, or if you rely on any gems that do. You'll need to either stop transpiling or use a Node-based transpiler, like those in `jsbundling-rails` and `cssbundling-rails`.
|
data/lib/propshaft/assembly.rb
CHANGED
@@ -14,11 +14,11 @@ class Propshaft::Assembly
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def load_path
|
17
|
-
Propshaft::LoadPath.new(config.paths)
|
17
|
+
@load_path ||= Propshaft::LoadPath.new(config.paths)
|
18
18
|
end
|
19
19
|
|
20
20
|
def resolver
|
21
|
-
if manifest_path.exist?
|
21
|
+
@resolver ||= if manifest_path.exist?
|
22
22
|
Propshaft::Resolver::Static.new manifest_path: manifest_path, prefix: config.prefix
|
23
23
|
else
|
24
24
|
Propshaft::Resolver::Dynamic.new load_path: load_path, prefix: config.prefix
|
@@ -43,6 +43,12 @@ class Propshaft::Assembly
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
def reveal
|
47
|
+
load_path.assets.each do |asset|
|
48
|
+
Propshaft.logger.info asset.logical_path
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
46
52
|
private
|
47
53
|
def manifest_path
|
48
54
|
config.output_path.join(Propshaft::Processor::MANIFEST_FILENAME)
|
data/lib/propshaft/asset.rb
CHANGED
@@ -21,14 +21,27 @@ class Propshaft::Asset
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def digest
|
24
|
-
Digest::SHA1.hexdigest(content)
|
24
|
+
@digest ||= Digest::SHA1.hexdigest(content)
|
25
25
|
end
|
26
26
|
|
27
27
|
def digested_path
|
28
|
-
|
28
|
+
if already_digested?
|
29
|
+
logical_path
|
30
|
+
else
|
31
|
+
logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def fresh?(digest)
|
36
|
+
self.digest == digest || already_digested?
|
29
37
|
end
|
30
38
|
|
31
39
|
def ==(other_asset)
|
32
40
|
logical_path.hash == other_asset.logical_path.hash
|
33
41
|
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def already_digested?
|
45
|
+
logical_path.to_s =~ /-([0-9a-f]{7,128})\.digested/
|
46
|
+
end
|
34
47
|
end
|
@@ -1,13 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "propshaft/errors"
|
3
|
+
|
1
4
|
class Propshaft::Compilers::CssAssetUrls
|
2
5
|
attr_reader :assembly
|
3
6
|
|
7
|
+
ASSET_URL_PATTERN = /url\(\s*["']?(?!(?:\#|data|http))([^"'\s)]+)\s*["']?\)/
|
8
|
+
|
4
9
|
def initialize(assembly)
|
5
10
|
@assembly = assembly
|
6
11
|
end
|
7
12
|
|
8
|
-
def compile(input)
|
9
|
-
input.gsub(
|
10
|
-
%[url("#{assembly.config.prefix}/#{assembly.load_path.find($1).digested_path}")]
|
11
|
-
end
|
13
|
+
def compile(logical_path, input)
|
14
|
+
input.gsub(ASSET_URL_PATTERN) { asset_url resolve_path(logical_path.dirname, $1) }
|
12
15
|
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def resolve_path(directory, filename)
|
19
|
+
if filename.start_with?("../")
|
20
|
+
Pathname.new(directory + filename).relative_path_from("").to_s
|
21
|
+
elsif filename.start_with?("/")
|
22
|
+
filename.delete_prefix("/").to_s
|
23
|
+
else
|
24
|
+
(directory + filename.delete_prefix("./")).to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def asset_url(resolved_path)
|
29
|
+
if asset = assembly.load_path.find(resolved_path)
|
30
|
+
%[url("#{assembly.config.prefix}/#{asset.digested_path}")]
|
31
|
+
else
|
32
|
+
raise Propshaft::MissingAssetError.new(resolved_path)
|
33
|
+
end
|
34
|
+
end
|
13
35
|
end
|
data/lib/propshaft/compilers.rb
CHANGED
@@ -23,7 +23,7 @@ class Propshaft::Compilers
|
|
23
23
|
if relevant_registrations = registrations[asset.content_type.to_s]
|
24
24
|
asset.content.dup.tap do |input|
|
25
25
|
relevant_registrations.each do |compiler|
|
26
|
-
input.replace compiler.new(assembly).compile(input)
|
26
|
+
input.replace compiler.new(assembly).compile(asset.logical_path, input)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
else
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Propshaft
|
4
|
+
# Generic base class for all Propshaft exceptions.
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
# Raised when LoadPath cannot find the requested asset
|
8
|
+
class MissingAssetError < Error
|
9
|
+
def initialize(path)
|
10
|
+
super
|
11
|
+
@path = path
|
12
|
+
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
"The asset '#{@path}' was not found in the load path."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/propshaft/load_path.rb
CHANGED
@@ -27,9 +27,23 @@ class Propshaft::LoadPath
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
# Returns a ActiveSupport::FileUpdateChecker object configured to clear the cache of the load_path
|
31
|
+
# when the directories passed during its initialization have changes. This is used in development
|
32
|
+
# and test to ensure the map caches are reset when javascript files are changed.
|
33
|
+
def cache_sweeper
|
34
|
+
@cache_sweeper ||= begin
|
35
|
+
exts_to_watch = Mime::EXTENSION_LOOKUP.map(&:first)
|
36
|
+
files_to_watch = Array(paths).collect { |dir| [ dir.to_s, exts_to_watch ] }.to_h
|
37
|
+
|
38
|
+
Rails.application.config.file_watcher.new([], files_to_watch) do
|
39
|
+
clear_cache
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
30
44
|
private
|
31
45
|
def assets_by_path
|
32
|
-
Hash.new.tap do |mapped|
|
46
|
+
@cached_assets_by_path ||= Hash.new.tap do |mapped|
|
33
47
|
paths.each do |path|
|
34
48
|
all_files_from_tree(path).each do |file|
|
35
49
|
logical_path = file.relative_path_from(path)
|
@@ -42,4 +56,8 @@ class Propshaft::LoadPath
|
|
42
56
|
def all_files_from_tree(path)
|
43
57
|
path.children.flat_map { |child| child.directory? ? all_files_from_tree(child) : child }
|
44
58
|
end
|
59
|
+
|
60
|
+
def clear_cache
|
61
|
+
@cached_assets_by_path = nil
|
62
|
+
end
|
45
63
|
end
|
data/lib/propshaft/processor.rb
CHANGED
@@ -16,6 +16,10 @@ class Propshaft::Processor
|
|
16
16
|
compress_assets
|
17
17
|
end
|
18
18
|
|
19
|
+
def clean
|
20
|
+
FileUtils.rm_r(output_path) if File.exist?(output_path)
|
21
|
+
end
|
22
|
+
|
19
23
|
private
|
20
24
|
def ensure_output_path_exists
|
21
25
|
FileUtils.mkdir_p output_path
|
@@ -32,6 +36,7 @@ class Propshaft::Processor
|
|
32
36
|
def output_assets
|
33
37
|
load_path.assets.each do |asset|
|
34
38
|
unless output_path.join(asset.digested_path).exist?
|
39
|
+
Propshaft.logger.info "Writing #{asset.digested_path}"
|
35
40
|
FileUtils.mkdir_p output_path.join(asset.digested_path.parent)
|
36
41
|
output_asset(asset)
|
37
42
|
end
|
data/lib/propshaft/railtie.rb
CHANGED
@@ -14,9 +14,10 @@ end
|
|
14
14
|
module Propshaft
|
15
15
|
class Railtie < ::Rails::Railtie
|
16
16
|
config.assets = ActiveSupport::OrderedOptions.new
|
17
|
-
config.assets.paths
|
18
|
-
config.assets.prefix
|
19
|
-
config.assets.compilers
|
17
|
+
config.assets.paths = []
|
18
|
+
config.assets.prefix = "/assets"
|
19
|
+
config.assets.compilers = [ [ "text/css", Propshaft::Compilers::CssAssetUrls ] ]
|
20
|
+
config.assets.sweep_cache = Rails.env.development?
|
20
21
|
|
21
22
|
config.after_initialize do |app|
|
22
23
|
config.assets.output_path ||=
|
@@ -31,6 +32,16 @@ module Propshaft
|
|
31
32
|
ActiveSupport.on_load(:action_view) do
|
32
33
|
include Propshaft::Helper
|
33
34
|
end
|
35
|
+
|
36
|
+
if config.assets.sweep_cache
|
37
|
+
ActiveSupport.on_load(:action_controller_base) do
|
38
|
+
before_action { Rails.application.assets.load_path.cache_sweeper.execute_if_updated }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
initializer "propshaft.logger" do
|
44
|
+
Propshaft.logger = config.assets.logger || Rails.logger
|
34
45
|
end
|
35
46
|
|
36
47
|
rake_tasks do |app|
|
@@ -39,6 +50,16 @@ module Propshaft
|
|
39
50
|
task precompile: :environment do
|
40
51
|
Rails.application.assets.processor.process
|
41
52
|
end
|
53
|
+
|
54
|
+
desc "Remove config.assets.output_path"
|
55
|
+
task clean: :environment do
|
56
|
+
Rails.application.assets.processor.clean
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "Print all the assets available in config.assets.paths"
|
60
|
+
task reveal: :environment do
|
61
|
+
Rails.application.assets.reveal
|
62
|
+
end
|
42
63
|
end
|
43
64
|
end
|
44
65
|
|
data/lib/propshaft/server.rb
CHANGED
@@ -8,16 +8,17 @@ class Propshaft::Server
|
|
8
8
|
def call(env)
|
9
9
|
path, digest = extract_path_and_digest(env)
|
10
10
|
|
11
|
-
if (asset = @assembly.load_path.find(path)) && asset.digest
|
11
|
+
if (asset = @assembly.load_path.find(path)) && asset.fresh?(digest)
|
12
12
|
compiled_content = @assembly.compilers.compile(asset)
|
13
13
|
|
14
14
|
[
|
15
15
|
200,
|
16
16
|
{
|
17
|
-
"Content-Length"
|
18
|
-
"Content-Type"
|
19
|
-
"
|
20
|
-
"
|
17
|
+
"Content-Length" => compiled_content.length.to_s,
|
18
|
+
"Content-Type" => asset.content_type.to_s,
|
19
|
+
"Accept-Encoding" => "Vary",
|
20
|
+
"ETag" => asset.digest,
|
21
|
+
"Cache-Control" => "public, max-age=31536000, immutable"
|
21
22
|
},
|
22
23
|
[ compiled_content ]
|
23
24
|
]
|
@@ -29,9 +30,9 @@ class Propshaft::Server
|
|
29
30
|
private
|
30
31
|
def extract_path_and_digest(env)
|
31
32
|
full_path = Rack::Utils.unescape(env["PATH_INFO"].to_s.sub(/^\//, ""))
|
32
|
-
digest = full_path[/-([0-9a-f]{7,128})\.[^.]+\z/, 1]
|
33
|
+
digest = full_path[/-([0-9a-f]{7,128})\.(?!digested)[^.]+\z/, 1]
|
33
34
|
path = digest ? full_path.sub("-#{digest}", "") : full_path
|
34
|
-
|
35
|
+
|
35
36
|
[ path, digest ]
|
36
37
|
end
|
37
38
|
end
|
data/lib/propshaft/version.rb
CHANGED
data/lib/propshaft.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: propshaft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 7.0.0.alpha2
|
27
|
-
description:
|
27
|
+
description:
|
28
28
|
email: dhh@hey.com
|
29
29
|
executables: []
|
30
30
|
extensions: []
|
@@ -38,6 +38,7 @@ files:
|
|
38
38
|
- lib/propshaft/asset.rb
|
39
39
|
- lib/propshaft/compilers.rb
|
40
40
|
- lib/propshaft/compilers/css_asset_urls.rb
|
41
|
+
- lib/propshaft/errors.rb
|
41
42
|
- lib/propshaft/helper.rb
|
42
43
|
- lib/propshaft/load_path.rb
|
43
44
|
- lib/propshaft/processor.rb
|
@@ -50,7 +51,7 @@ homepage: https://github.com/rails/propshaft
|
|
50
51
|
licenses:
|
51
52
|
- MIT
|
52
53
|
metadata: {}
|
53
|
-
post_install_message:
|
54
|
+
post_install_message:
|
54
55
|
rdoc_options: []
|
55
56
|
require_paths:
|
56
57
|
- lib
|
@@ -65,8 +66,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
66
|
- !ruby/object:Gem::Version
|
66
67
|
version: '0'
|
67
68
|
requirements: []
|
68
|
-
rubygems_version: 3.
|
69
|
-
signing_key:
|
69
|
+
rubygems_version: 3.2.22
|
70
|
+
signing_key:
|
70
71
|
specification_version: 4
|
71
72
|
summary: Deliver assets for Rails.
|
72
73
|
test_files: []
|