precompiled_assets 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d8def5c63790a7b16733d25ec1875e7fb294dca734ef389cc26f30b093b41c39
4
+ data.tar.gz: a3db5b16a4dd0fbd55d3e8d0f907de84b3db35269c5c005ee97891cb612d798a
5
+ SHA512:
6
+ metadata.gz: a794f9316533f5422397e3ea950e1529cef6c64326bcec41df60ef122464833ca77e49222fe0c1ce9f7e21a963ddf8ba9007e2a774ffd8f5717187acf37b2a4c
7
+ data.tar.gz: cc1743713e3a27b962f0e4910cc5f67eec72ef00345b6d07da164d08748356382603900d3ee2d058fab02bb9411bcf80b43727408160eab60854142aa741dbd5
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ inherit_gem:
2
+ makandra-rubocop:
3
+ - config/default.yml
4
+ - config/ext/rails.yml
5
+ - config/ext/rspec.yml
6
+
7
+ AllCops:
8
+ TargetRubyVersion: 2.6
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-07-27
4
+
5
+ - First public release.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in precompiled_assets.gemspec
4
+ gemspec
5
+
6
+ gem 'rake'
7
+ gem 'rubocop'
8
+ gem 'makandra-rubocop'
data/Gemfile.lock ADDED
@@ -0,0 +1,104 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ precompiled_assets (0.1.0)
5
+ railties (>= 6.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionpack (7.0.3.1)
11
+ actionview (= 7.0.3.1)
12
+ activesupport (= 7.0.3.1)
13
+ rack (~> 2.0, >= 2.2.0)
14
+ rack-test (>= 0.6.3)
15
+ rails-dom-testing (~> 2.0)
16
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
17
+ actionview (7.0.3.1)
18
+ activesupport (= 7.0.3.1)
19
+ builder (~> 3.1)
20
+ erubi (~> 1.4)
21
+ rails-dom-testing (~> 2.0)
22
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
23
+ activesupport (7.0.3.1)
24
+ concurrent-ruby (~> 1.0, >= 1.0.2)
25
+ i18n (>= 1.6, < 2)
26
+ minitest (>= 5.1)
27
+ tzinfo (~> 2.0)
28
+ ast (2.4.2)
29
+ builder (3.2.4)
30
+ concurrent-ruby (1.1.10)
31
+ crass (1.0.6)
32
+ erubi (1.10.0)
33
+ i18n (1.12.0)
34
+ concurrent-ruby (~> 1.0)
35
+ loofah (2.18.0)
36
+ crass (~> 1.0.2)
37
+ nokogiri (>= 1.5.9)
38
+ makandra-rubocop (9.1.0)
39
+ rubocop (~> 1.25.0)
40
+ rubocop-rails (~> 2.11.3)
41
+ rubocop-rspec (~> 2.4.0)
42
+ method_source (1.0.0)
43
+ minitest (5.16.2)
44
+ nokogiri (1.13.8-x86_64-linux)
45
+ racc (~> 1.4)
46
+ parallel (1.22.1)
47
+ parser (3.1.2.0)
48
+ ast (~> 2.4.1)
49
+ racc (1.6.0)
50
+ rack (2.2.4)
51
+ rack-test (2.0.2)
52
+ rack (>= 1.3)
53
+ rails-dom-testing (2.0.3)
54
+ activesupport (>= 4.2.0)
55
+ nokogiri (>= 1.6)
56
+ rails-html-sanitizer (1.4.3)
57
+ loofah (~> 2.3)
58
+ railties (7.0.3.1)
59
+ actionpack (= 7.0.3.1)
60
+ activesupport (= 7.0.3.1)
61
+ method_source
62
+ rake (>= 12.2)
63
+ thor (~> 1.0)
64
+ zeitwerk (~> 2.5)
65
+ rainbow (3.1.1)
66
+ rake (13.0.6)
67
+ regexp_parser (2.5.0)
68
+ rexml (3.2.5)
69
+ rubocop (1.25.1)
70
+ parallel (~> 1.10)
71
+ parser (>= 3.1.0.0)
72
+ rainbow (>= 2.2.2, < 4.0)
73
+ regexp_parser (>= 1.8, < 3.0)
74
+ rexml
75
+ rubocop-ast (>= 1.15.1, < 2.0)
76
+ ruby-progressbar (~> 1.7)
77
+ unicode-display_width (>= 1.4.0, < 3.0)
78
+ rubocop-ast (1.19.1)
79
+ parser (>= 3.1.1.0)
80
+ rubocop-rails (2.11.3)
81
+ activesupport (>= 4.2.0)
82
+ rack (>= 1.1)
83
+ rubocop (>= 1.7.0, < 2.0)
84
+ rubocop-rspec (2.4.0)
85
+ rubocop (~> 1.0)
86
+ rubocop-ast (>= 1.1.0)
87
+ ruby-progressbar (1.11.0)
88
+ thor (1.2.1)
89
+ tzinfo (2.0.5)
90
+ concurrent-ruby (~> 1.0)
91
+ unicode-display_width (2.2.0)
92
+ zeitwerk (2.6.0)
93
+
94
+ PLATFORMS
95
+ x86_64-linux
96
+
97
+ DEPENDENCIES
98
+ makandra-rubocop
99
+ precompiled_assets!
100
+ rake
101
+ rubocop
102
+
103
+ BUNDLED WITH
104
+ 2.3.18
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Arne Hartherz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # PrecompiledAssets
2
+
3
+ Serve assets without any asset processing in Rails.
4
+
5
+ The gem is intended for applications where assets are digested externally, e.g. via esbuild.
6
+
7
+ Our only requirements are
8
+ - a manifest file which needs to be generated during the build process,
9
+ - that the manifest lives somewhere inside `public/` (you are free to configure an `asset_host`, won't affect the gem),
10
+ - and a matching Rails configuration setting.
11
+
12
+ ## Installation
13
+
14
+ Install the gem and add to the application's Gemfile by executing:
15
+
16
+ $ bundle add precompiled_assets
17
+
18
+ If bundler is not being used to manage dependencies, install the gem by executing:
19
+
20
+ $ gem install precompiled_assets
21
+
22
+ ## Usage
23
+
24
+ In your `config/application.rb`, configure an `asset_path` which should be in your `public/` directory:
25
+
26
+ ```ruby
27
+ config.asset_path = '/assets'
28
+ ```
29
+
30
+ Inside that path, a `manifest.json` is expected to exist which resolves your undigested input names to digested output paths.
31
+ If your manifest has a different filename, you may set `config.asset_manifest_filename`.
32
+
33
+ A manifest file can look like this:
34
+
35
+ ```json
36
+ {
37
+ "application.js": "application-HP2LS2UH.js",
38
+ "application.css": "application-BWAZLURC.css",
39
+ "images/example.png": "images/example-5N2N2WJM.png"
40
+ }
41
+ ```
42
+
43
+ Once that is set up, `javascript_include_tag('application.js')` or `image_path('example.png')` will resolve to their digested filenames and paths.
44
+
45
+ In the `development` Rails environment, the gem detects changes to the manifest and reloads the manifest automatically.
46
+ Hence, your development experience should be similar to other stacks, like with Propshaft or esbuild: You change assets, the manifest changes, Rails will then resolve to the updated asset paths. 🎉
47
+
48
+
49
+
50
+ ## Development
51
+
52
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
53
+
54
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
55
+
56
+
57
+ ## Contributing
58
+
59
+ I am very eager to keep this gem lightweight and on topic.
60
+ If you are unsure whether a change would make it into the gem, please create an issue and discuss first.
61
+
62
+
63
+ ## License
64
+
65
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rubocop/rake_task'
3
+
4
+ RuboCop::RakeTask.new
5
+
6
+ task default: :rubocop
@@ -0,0 +1,3 @@
1
+ module PrecompiledAssets
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,18 @@
1
+ module PrecompiledAssets
2
+ module Helper
3
+
4
+ ASSET_DIRECTORIES = ActionView::Helpers::AssetUrlHelper::ASSET_PUBLIC_DIRECTORIES.except(:javascript, :stylesheet).freeze
5
+
6
+ def asset_resolver
7
+ Thread.current['PrecompiledAssets::Helper#asset_resolver'] ||= Resolver.new
8
+ end
9
+
10
+ def compute_asset_path(path, options = {})
11
+ directory = ASSET_DIRECTORIES[options[:type]]
12
+ path_with_directory = File.join(*directory, path)
13
+
14
+ asset_resolver.resolve(path_with_directory.delete_prefix('/'))
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module PrecompiledAssets
2
+ module LocalPath
3
+
4
+ module_function
5
+
6
+ def pathname
7
+ joinable_asset_path = Rails.configuration.asset_path.delete_prefix('/')
8
+ Rails.public_path.join(joinable_asset_path)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,59 @@
1
+ module PrecompiledAssets
2
+ class Manifest
3
+
4
+ DEFAULT_FILENAME = 'manifest.json'.freeze
5
+
6
+ class Error < PrecompiledAssets::Error; end
7
+ class NotFound < Error; end
8
+ class ParseError < Error; end
9
+
10
+ def resolve(path)
11
+ parsed_manifest[path]
12
+ end
13
+
14
+ def pathname
15
+ @pathname ||= LocalPath.pathname.join(filename)
16
+ end
17
+
18
+ def includes_digested_path?(path_or_pathname)
19
+ digested_paths.include?(path_or_pathname.to_s)
20
+ end
21
+
22
+ def expired?
23
+ # Not intended for use in production environments.
24
+ @mtime && @mtime != fetch_mtime
25
+ end
26
+
27
+ private
28
+
29
+ def filename
30
+ Rails.configuration.try(:asset_manifest_filename) || DEFAULT_FILENAME
31
+ end
32
+
33
+ def parsed_manifest
34
+ @parsed_manifest ||= parse_manifest
35
+ end
36
+
37
+ def parse_manifest
38
+ raise NotFound, "Manifest not found at #{pathname}" unless pathname.exist?
39
+
40
+ @mtime = fetch_mtime
41
+
42
+ json = pathname.read
43
+ JSON.parse(json)
44
+ rescue JSON::ParserError => error
45
+ raise ParseError, "Failed to parse manifest file: #{error.message}"
46
+ end
47
+
48
+ def fetch_mtime
49
+ File.mtime(pathname)
50
+ end
51
+
52
+ def digested_paths
53
+ @digested_paths ||= parsed_manifest
54
+ .values
55
+ .map { |asset_path| LocalPath.pathname.join(asset_path).to_s }
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails'
2
+
3
+ module PrecompiledAssets
4
+ class Railtie < ::Rails::Railtie
5
+
6
+ config.after_initialize do
7
+ ActiveSupport.on_load(:action_view) do
8
+ require 'precompiled_assets/helper'
9
+ include PrecompiledAssets::Helper
10
+ end
11
+ end
12
+
13
+ rake_tasks do
14
+ load 'precompiled_assets/tasks/assets.rake'
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module PrecompiledAssets
2
+ class Resolver
3
+
4
+ class Error < PrecompiledAssets::Error; end
5
+ class UnknownAsset < Error; end
6
+
7
+ def resolve(path)
8
+ reload_manifest if Rails.env.development? && manifest.expired?
9
+
10
+ digested_path = manifest.resolve(path.to_s)
11
+
12
+ if digested_path.present?
13
+ File.join(Rails.configuration.asset_path, digested_path)
14
+ else
15
+ raise UnknownAsset, "Could not find #{path.inspect} in manifest: #{manifest.inspect}"
16
+ end
17
+ end
18
+
19
+ def manifest
20
+ @manifest ||= Manifest.new
21
+ end
22
+
23
+ def reload_manifest
24
+ @manifest = nil
25
+ manifest
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ require 'precompiled_assets/tasks'
2
+
3
+ namespace :assets do
4
+ desc 'Empty task; assets should be compiled externally. Libraries like jsbundling-rails extend this task to do that.'
5
+ task precompile: :environment do # rubocop:disable Lint/EmptyBlock
6
+ end
7
+
8
+ desc 'Remove all assets inside the compiled asset path, but keep the directory itself'
9
+ task clobber: :environment do
10
+ PrecompiledAssets::Tasks.remove_all
11
+ end
12
+
13
+ desc 'Remove all assets not linked in the current manifest file'
14
+ task clean: :environment do
15
+ PrecompiledAssets::Tasks.remove_unused
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ module PrecompiledAssets
2
+ module Tasks
3
+
4
+ module_function
5
+
6
+ def remove_all
7
+ LocalPath.pathname.glob('*').each(&:rmtree)
8
+ end
9
+
10
+ def remove_unused
11
+ manifest = Manifest.new
12
+
13
+ pathnames = LocalPath.pathname.glob('**/*').sort
14
+
15
+ # Remove all files not listed in the manifest; also keep the manifest file itself.
16
+ pathnames.select(&:file?).each do |asset_pathname|
17
+ next if manifest.pathname == asset_pathname
18
+ next if manifest.includes_digested_path?(asset_pathname)
19
+ next if manifest.includes_digested_path?(asset_pathname.to_s.delete_suffix('.map'))
20
+
21
+ puts "Removing unused asset: #{asset_pathname}"
22
+ asset_pathname.delete
23
+ end
24
+
25
+ # After removing all unused files, remove any empty directories that remain.
26
+ # Iterating in reverse to start with most deeply nested directories first, allow to clean up empty branches.
27
+ pathnames.select(&:directory?).reverse_each do |directory|
28
+ next unless directory.empty?
29
+
30
+ puts "Removing empty directory: #{directory}"
31
+ directory.rmdir
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module PrecompiledAssets
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,6 @@
1
+ require_relative 'precompiled_assets/error'
2
+ require_relative 'precompiled_assets/local_path'
3
+ require_relative 'precompiled_assets/manifest'
4
+ require_relative 'precompiled_assets/resolver'
5
+
6
+ require_relative 'precompiled_assets/railtie'
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
2
+ require_relative 'lib/precompiled_assets/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'precompiled_assets'
6
+ spec.version = PrecompiledAssets::VERSION
7
+ spec.authors = ['Arne Hartherz']
8
+ spec.email = ['arne.hartherz@makandra.de']
9
+
10
+ spec.summary = 'Serve assets without any asset processing in Rails.'
11
+ spec.description = spec.summary
12
+ spec.homepage = 'https://github.com/makandra/precompiled_assets'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = '>= 2.6.0'
15
+ spec.metadata = {
16
+ 'rubygems_mfa_required' => 'true',
17
+ 'homepage_uri' => spec.homepage,
18
+ 'source_code_uri' => spec.homepage,
19
+ 'changelog_uri' => "#{spec.homepage}/CHANGELOG.md",
20
+ }
21
+
22
+ spec.files = Dir.chdir(__dir__) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r(\Aexe/)) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ spec.add_dependency 'railties', '>= 6.0.0'
32
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: precompiled_assets
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Arne Hartherz
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-07-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.0
27
+ description: Serve assets without any asset processing in Rails.
28
+ email:
29
+ - arne.hartherz@makandra.de
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".rubocop.yml"
35
+ - ".ruby-version"
36
+ - CHANGELOG.md
37
+ - Gemfile
38
+ - Gemfile.lock
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - lib/precompiled_assets.rb
43
+ - lib/precompiled_assets/error.rb
44
+ - lib/precompiled_assets/helper.rb
45
+ - lib/precompiled_assets/local_path.rb
46
+ - lib/precompiled_assets/manifest.rb
47
+ - lib/precompiled_assets/railtie.rb
48
+ - lib/precompiled_assets/resolver.rb
49
+ - lib/precompiled_assets/tasks.rb
50
+ - lib/precompiled_assets/tasks/assets.rake
51
+ - lib/precompiled_assets/version.rb
52
+ - precompiled-assets.gemspec
53
+ homepage: https://github.com/makandra/precompiled_assets
54
+ licenses:
55
+ - MIT
56
+ metadata:
57
+ rubygems_mfa_required: 'true'
58
+ homepage_uri: https://github.com/makandra/precompiled_assets
59
+ source_code_uri: https://github.com/makandra/precompiled_assets
60
+ changelog_uri: https://github.com/makandra/precompiled_assets/CHANGELOG.md
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 2.6.0
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubygems_version: 3.3.7
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Serve assets without any asset processing in Rails.
80
+ test_files: []