├── .devcontainer └── devcontainer.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CONTRIBUTING.md ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── gemfiles ├── Gemfile.rails-6.1-sprockets-3 ├── Gemfile.rails-6.1-sprockets-4 ├── Gemfile.rails-7.0-sprockets-3 ├── Gemfile.rails-7.0-sprockets-4 ├── Gemfile.rails-7.1-sprockets-3 ├── Gemfile.rails-7.1-sprockets-4 ├── Gemfile.rails-7.2-sprockets-3 ├── Gemfile.rails-7.2-sprockets-4 ├── Gemfile.rails-8.0-sprockets-3 └── Gemfile.rails-8.0-sprockets-4 ├── lib └── sprockets │ ├── rails.rb │ ├── rails │ ├── asset_url_processor.rb │ ├── context.rb │ ├── deprecator.rb │ ├── helper.rb │ ├── quiet_assets.rb │ ├── route_wrapper.rb │ ├── sourcemapping_url_processor.rb │ ├── task.rb │ ├── utils.rb │ └── version.rb │ └── railtie.rb ├── sprockets-rails.gemspec └── test ├── fixtures ├── bar.css ├── bar.js ├── bundle │ └── index.js ├── dependency.css ├── dependency.js ├── error │ ├── dependency.js.erb │ └── missing.css.erb ├── file1.css ├── file1.js ├── file2.css ├── file2.js ├── foo.css ├── foo.js ├── jquery │ ├── bower.json │ └── jquery.js ├── logo.png ├── manifest.js ├── not_precompiled.css ├── not_precompiled.js ├── url.css.erb └── url.js.erb ├── test_asset_url_processor.rb ├── test_helper.rb ├── test_quiet_assets.rb ├── test_railtie.rb ├── test_sourcemapping_url_processor.rb └── test_task.rb /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/ruby 3 | { 4 | "name": "sprockets-rails", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "ghcr.io/rails/devcontainer/images/ruby:3.2.5", 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | "features": { 10 | "ghcr.io/devcontainers/features/github-cli:1": { 11 | "version": "latest" 12 | } 13 | } 14 | 15 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 16 | // "forwardPorts": [], 17 | 18 | // Use 'postCreateCommand' to run commands after the container is created. 19 | // "postCreateCommand": "ruby --version", 20 | 21 | // Configure tool-specific properties. 22 | // "customizations": {}, 23 | 24 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 25 | // "remoteUser": "root" 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | tests: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | include: 10 | - ruby: 2.5 11 | gemfile: "gemfiles/Gemfile.rails-6.1-sprockets-3" 12 | - ruby: 2.5 13 | gemfile: "gemfiles/Gemfile.rails-6.1-sprockets-4" 14 | 15 | - ruby: 2.7 16 | gemfile: "gemfiles/Gemfile.rails-7.0-sprockets-3" 17 | - ruby: 2.7 18 | gemfile: "gemfiles/Gemfile.rails-7.0-sprockets-4" 19 | 20 | - ruby: 2.7 21 | gemfile: "gemfiles/Gemfile.rails-7.1-sprockets-3" 22 | - ruby: 2.7 23 | gemfile: "gemfiles/Gemfile.rails-7.1-sprockets-4" 24 | 25 | - ruby: 3.1 26 | gemfile: "gemfiles/Gemfile.rails-7.2-sprockets-3" 27 | - ruby: 3.1 28 | gemfile: "gemfiles/Gemfile.rails-7.2-sprockets-4" 29 | 30 | - ruby: 3.2 31 | gemfile: "gemfiles/Gemfile.rails-8.0-sprockets-3" 32 | - ruby: 3.2 33 | gemfile: "gemfiles/Gemfile.rails-8.0-sprockets-4" 34 | 35 | - ruby: 3.2 36 | gemfile: Gemfile 37 | - ruby: 3.3 38 | gemfile: Gemfile 39 | - ruby: head 40 | gemfile: Gemfile 41 | 42 | env: 43 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 44 | 45 | steps: 46 | - uses: actions/checkout@v3 47 | - name: Set up Ruby 48 | uses: ruby/setup-ruby@v1 49 | with: 50 | ruby-version: ${{ matrix.ruby }} 51 | bundler-cache: true 52 | - name: Run tests 53 | run: bundle exec rake 54 | continue-on-error: ${{ matrix.gemfile == 'Gemfile' }} 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile*.lock 2 | tmp/ 3 | *.gem 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to Sprockets Rails 2 | ===================== 3 | 4 | Sprockets Rails is work of [many contributors](https://github.com/rails/sprockets-rails/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/rails/sprockets-rails/pulls), [propose features and discuss issues](https://github.com/rails/sprockets-rails/issues). 5 | 6 | #### Fork the Project 7 | 8 | Fork the [project on GitHub](https://github.com/rails/sprockets-rails) and clone your fork. 9 | Use your GitHub username instead of `YOUR-USERNAME`. 10 | 11 | ``` 12 | git clone https://github.com/YOUR-USERNAME/sprockets-rails.git 13 | cd sprockets-rails 14 | git remote add upstream https://github.com/rails/sprockets-rails.git 15 | ``` 16 | 17 | #### Create a Topic Branch 18 | 19 | Make sure your fork is up-to-date and create a topic branch for your feature or bug fix. 20 | 21 | ``` 22 | git checkout master 23 | git pull upstream master 24 | git checkout -b my-feature-branch 25 | ``` 26 | 27 | #### Bundle Install and Test 28 | 29 | Ensure that you can build the project and run tests. 30 | 31 | ``` 32 | bundle install 33 | bundle exec rake test 34 | ``` 35 | 36 | #### Write Tests 37 | 38 | Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [test](test). 39 | 40 | We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix. 41 | 42 | #### Write Code 43 | 44 | Implement your feature or bug fix. 45 | 46 | Make sure that `bundle exec rake test` completes without errors. 47 | 48 | #### Write Documentation 49 | 50 | Document any external behavior in the [README](README.md). 51 | 52 | #### Commit Changes 53 | 54 | Make sure git knows your name and email address: 55 | 56 | ``` 57 | git config --global user.name "Your Name" 58 | git config --global user.email "contributor@example.com" 59 | ``` 60 | 61 | Writing good commit logs is important. A commit log should describe what changed and why. 62 | 63 | ``` 64 | git add ... 65 | git commit 66 | ``` 67 | 68 | #### Push 69 | 70 | ``` 71 | git push origin my-feature-branch 72 | ``` 73 | 74 | #### Make a Pull Request 75 | 76 | Go to https://github.com/contributor/sprockets-rails and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days. 77 | 78 | #### Rebase 79 | 80 | If you've been working on a change for a while, rebase with upstream/master. 81 | 82 | ``` 83 | git fetch upstream 84 | git rebase upstream/master 85 | git push origin my-feature-branch -f 86 | ``` 87 | 88 | #### Check on Your Pull Request 89 | 90 | Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above. 91 | 92 | #### Be Patient 93 | 94 | It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there! 95 | 96 | #### Thank You 97 | 98 | Please do know that we really appreciate and value your time and work. We love you, really. 99 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | 4 | gem 'actionpack', github: 'rails/rails', branch: 'main' 5 | gem 'railties', github: 'rails/rails', branch: 'main' 6 | gem 'rack', '~> 2.2' 7 | gem 'sprockets', github: 'rails/sprockets', branch: '3.x' 8 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 Joshua Peek 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sprockets Rails 2 | 3 | Provides [Sprockets](https://github.com/rails/sprockets) implementation for Rails 4.x (and beyond) Asset Pipeline. 4 | 5 | 6 | ## Installation 7 | 8 | ``` ruby 9 | gem 'sprockets-rails', :require => 'sprockets/railtie' 10 | ``` 11 | 12 | Or alternatively `require 'sprockets/railtie'` in your `config/application.rb` if you have Bundler auto-require disabled. 13 | 14 | 15 | ## Usage 16 | 17 | 18 | ### Rake task 19 | 20 | **`rake assets:precompile`** 21 | 22 | Deployment task that compiles any assets listed in `config.assets.precompile` to `public/assets`. 23 | 24 | **`rake assets:clean`** 25 | 26 | Only removes old assets (keeps the most recent 3 copies) from `public/assets`. Useful when doing rolling deploys that may still be serving old assets while the new ones are being compiled. 27 | 28 | **`rake assets:clobber`** 29 | 30 | Nuke `public/assets`. 31 | 32 | #### Customize 33 | 34 | If the basic tasks don't do all that you need, it's straight forward to redefine them and replace them with something more specific to your app. 35 | 36 | You can also redefine the task with the built in task generator. 37 | 38 | ``` ruby 39 | require 'sprockets/rails/task' 40 | Sprockets::Rails::Task.new(Rails.application) do |t| 41 | t.environment = lambda { Rails.application.assets } 42 | t.assets = %w( application.js application.css ) 43 | t.keep = 5 44 | end 45 | ``` 46 | 47 | Each asset task will invoke `assets:environment` first. By default this loads the Rails environment. You can override this task to add or remove dependencies for your specific compilation environment. 48 | 49 | Also see [Sprockets::Rails::Task](https://github.com/rails/sprockets-rails/blob/master/lib/sprockets/rails/task.rb) and [Rake::SprocketsTask](https://github.com/rails/sprockets/blob/master/lib/rake/sprocketstask.rb). 50 | 51 | ### Initializer options 52 | 53 | **`config.assets.unknown_asset_fallback`** 54 | 55 | When set to a truthy value, a result will be returned even if the requested asset is not found in the asset pipeline. When set to a falsey value it will raise an error when no asset is found in the pipeline. Defaults to `true`. 56 | 57 | **`config.assets.precompile`** 58 | 59 | Add additional assets to compile on deploy. Defaults to `application.js`, `application.css` and any other non-js/css file under `app/assets`. 60 | 61 | **`config.assets.paths`** 62 | 63 | Add additional load paths to this Array. Rails includes `app/assets`, `lib/assets` and `vendor/assets` for you already. Plugins might want to add their custom paths to this. 64 | 65 | **`config.assets.quiet`** 66 | 67 | Suppresses logger output for asset requests. Uses the `config.assets.prefix` path to match asset requests. Defaults to `false`. 68 | 69 | **`config.assets.version`** 70 | 71 | Set a custom cache buster string. Changing it will cause all assets to recompile on the next build. 72 | 73 | ``` ruby 74 | config.assets.version = 'v1' 75 | # after installing a new plugin, change loads paths 76 | config.assets.version = 'v2' 77 | ``` 78 | 79 | **`config.assets.prefix`** 80 | 81 | Defaults to `/assets`. Changes the directory to compile assets to. 82 | 83 | **`config.assets.digest`** 84 | 85 | When enabled, fingerprints will be added to asset filenames. 86 | 87 | **`config.assets.debug`** 88 | 89 | Enable asset debugging mode. A source map will be included with each asset when this is true. 90 | 91 | **`config.assets.compile`** 92 | 93 | Enables Sprockets compile environment. If disabled, `Rails.application.assets` will be `nil` to prevent inadvertent compilation calls. View helpers will depend on assets being precompiled to `public/assets` in order to link to them. Initializers expecting `Rails.application.assets` during boot should be accessing the environment in a `config.assets.configure` block. See below. 94 | 95 | **`config.assets.configure`** 96 | 97 | Invokes block with environment when the environment is initialized. Allows direct access to the environment instance and lets you lazily load libraries only needed for asset compiling. 98 | 99 | ``` ruby 100 | config.assets.configure do |env| 101 | env.js_compressor = :uglifier # or :closure, :yui 102 | env.css_compressor = :sass # or :yui 103 | 104 | require 'my_processor' 105 | env.register_preprocessor 'application/javascript', MyProcessor 106 | 107 | env.logger = Rails.logger 108 | end 109 | ``` 110 | 111 | **`config.assets.resolve_assets_in_css_urls`** 112 | 113 | When this option is enabled, sprockets-rails will register a CSS postprocessor to resolve assets referenced in [`url()`](https://developer.mozilla.org/en-US/docs/Web/CSS/url()) function calls and replace them with the digested paths. Defaults to `true`. 114 | 115 | **`config.assets.resolve_with`** 116 | 117 | A list of `:environment` and `:manifest` symbols that defines the order that 118 | we try to find assets: manifest first, environment second? Manifest only? 119 | 120 | By default, we check the manifest first if asset digests are enabled and debug 121 | is not enabled, then we check the environment if compiling is enabled: 122 | ``` 123 | # Dev where debug is true, or digests are disabled 124 | %i[ environment ] 125 | 126 | # Dev default, or production with compile enabled. 127 | %i[ manifest environment ] 128 | 129 | # Production default. 130 | %i[ manifest ] 131 | ``` 132 | If the resolver list is empty (e.g. if debug is true and compile is false), the standard rails public path resolution will be used. 133 | 134 | **`config.assets.check_precompiled_asset`** 135 | 136 | When enabled, an exception is raised for missing assets. This option is enabled by default. 137 | 138 | ## Complementary plugins 139 | 140 | The following plugins provide some extras for the Sprockets Asset Pipeline. 141 | 142 | * [coffee-rails](https://github.com/rails/coffee-rails) 143 | * [sass-rails](https://github.com/rails/sass-rails) 144 | 145 | **NOTE** That these plugins are optional. The core coffee-script, sass, less, uglify, (and many more) features are built into Sprockets itself. Many of these plugins only provide generators and extra helpers. You can probably get by without them. 146 | 147 | 148 | ## Changes from Rails 3.x 149 | 150 | * Only compiles digest filenames. Static non-digest assets should simply live in public/. 151 | * Unmanaged asset paths and urls fallback to linking to public/. This should make it easier to work with both compiled assets and simple static assets. As a side effect, there will never be any "asset not precompiled errors" when linking to missing assets. They will just link to a public file which may or may not exist. 152 | * JS and CSS compressors must be explicitly set. Magic detection has been removed to avoid loading compressors in environments where you want to avoid loading any of the asset libraries. Assign `config.assets.js_compressor = :uglifier` or `config.assets.css_compressor = :sass` for the standard compressors. 153 | * The manifest file is now in a JSON format. Since it lives in public/ by default, the initial filename is also randomized to obfuscate public access to the resource. 154 | * `config.assets.manifest` (if used) must now include the manifest filename, e.g. `Rails.root.join('config/manifest.json')`. It cannot be a directory. 155 | * Two cleanup tasks: `rake assets:clean` is now a safe cleanup that only removes older assets that are no longer used, while `rake assets:clobber` nukes the entire `public/assets` directory. The clean task allows for rolling deploys that may still be linking to an old asset while the new assets are being built. 156 | 157 | ### But what if I want sprockets to generate non-digest assets? 158 | 159 | You have several options: 160 | 161 | * Use the [non-digest-assets gem](https://github.com/mvz/non-digest-assets). 162 | * Use the [sprockets-redirect gem](https://github.com/sikachu/sprockets-redirect). 163 | * Use the [smart_assets gem](https://github.com/zarqman/smart_assets). 164 | * Create [a rake task](https://github.com/rails/sprockets-rails/issues/49#issuecomment-20535134) to pre-generate a non-digest version in `public/`. 165 | 166 | ## Experimental 167 | 168 | ### [SRI](http://www.w3.org/TR/SRI/) support 169 | 170 | Sprockets 3.x adds experimental support for subresource integrity checks. The spec is still evolving and the API may change in backwards incompatible ways. 171 | 172 | ``` ruby 173 | javascript_include_tag :application, integrity: true 174 | # => "" 175 | ``` 176 | 177 | Note that sprockets-rails only adds integrity hashes to assets when served in a secure context (over an HTTPS connection or localhost). 178 | 179 | 180 | ## Contributing to Sprockets Rails 181 | 182 | Sprockets Rails is work of many contributors. You're encouraged to submit pull requests, propose 183 | features and discuss issues. 184 | 185 | See [CONTRIBUTING](CONTRIBUTING.md). 186 | 187 | ## Releases 188 | 189 | sprockets-rails 3.x will primarily target sprockets 3.x. And future versions will target the corresponding sprockets release line. 190 | 191 | The minor and patch version will be updated according to [semver](http://semver.org/). 192 | 193 | * Any new APIs or config options that don't break compatibility will be in a minor release 194 | * Any time the sprockets dependency is bumped, there will be a new minor release 195 | * Simple bug fixes will be patch releases 196 | 197 | ## License 198 | 199 | Sprockets Rails is released under the [MIT License](MIT-LICENSE). 200 | 201 | ## Code Status 202 | 203 | * [![Gem Version](https://badge.fury.io/rb/sprockets-rails.svg)](http://badge.fury.io/rb/sprockets-rails) 204 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | require 'bundler/gem_tasks' 3 | 4 | task :default => :test 5 | 6 | Rake::TestTask.new(:test) do |t| 7 | t.libs << 'lib' 8 | t.pattern = 'test/test_*.rb' 9 | t.warning = true 10 | t.verbose = true 11 | end 12 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-6.1-sprockets-3: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 6.1.0' 5 | gem 'railties', '~> 6.1.0' 6 | gem 'sprockets', '~> 3.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-6.1-sprockets-4: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 6.1.0' 5 | gem 'railties', '~> 6.1.0' 6 | gem 'sprockets', '~> 4.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-7.0-sprockets-3: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 7.0.0' 5 | gem 'railties', '~> 7.0.0' 6 | gem 'sprockets', '~> 3.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-7.0-sprockets-4: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 7.0.0' 5 | gem 'railties', '~> 7.0.0' 6 | gem 'sprockets', '~> 4.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-7.1-sprockets-3: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 7.1.0' 5 | gem 'railties', '~> 7.1.0' 6 | gem 'sprockets', '~> 3.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-7.1-sprockets-4: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 7.1.0' 5 | gem 'railties', '~> 7.1.0' 6 | gem 'sprockets', '~> 4.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-7.2-sprockets-3: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 7.2.0' 5 | gem 'railties', '~> 7.2.0' 6 | gem 'sprockets', '~> 3.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-7.2-sprockets-4: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 7.2.0' 5 | gem 'railties', '~> 7.2.0' 6 | gem 'sprockets', '~> 4.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-8.0-sprockets-3: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 8.0.0.rc1' 5 | gem 'railties', '~> 8.0.0.rc1' 6 | gem 'sprockets', '~> 3.0' 7 | -------------------------------------------------------------------------------- /gemfiles/Gemfile.rails-8.0-sprockets-4: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec path: '..' 3 | 4 | gem 'actionpack', '~> 8.0.0.rc1' 5 | gem 'railties', '~> 8.0.0.rc1' 6 | gem 'sprockets', '~> 4.0' 7 | -------------------------------------------------------------------------------- /lib/sprockets/rails.rb: -------------------------------------------------------------------------------- 1 | require 'sprockets/rails/version' 2 | if defined? Rails::Railtie 3 | require 'sprockets/railtie' 4 | end 5 | require 'sprockets/rails/deprecator' 6 | -------------------------------------------------------------------------------- /lib/sprockets/rails/asset_url_processor.rb: -------------------------------------------------------------------------------- 1 | module Sprockets 2 | module Rails 3 | # Resolve assets referenced in CSS `url()` calls and replace them with the digested paths 4 | class AssetUrlProcessor 5 | REGEX = /url\(\s*["']?(?!(?:\#|data|http))(?\.\/)?(?[^"'\s)]+)\s*["']?\)/ 6 | def self.call(input) 7 | context = input[:environment].context_class.new(input) 8 | data = input[:data].gsub(REGEX) do |_match| 9 | path = Regexp.last_match[:path] 10 | "url(#{context.asset_path(path)})" 11 | end 12 | 13 | context.metadata.merge(data: data) 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/sprockets/rails/context.rb: -------------------------------------------------------------------------------- 1 | require 'action_view/helpers' 2 | require 'sprockets' 3 | 4 | module Sprockets 5 | module Rails 6 | module Context 7 | include ActionView::Helpers::AssetUrlHelper 8 | include ActionView::Helpers::AssetTagHelper 9 | 10 | def self.included(klass) 11 | klass.class_eval do 12 | class_attribute :config, :assets_prefix, :digest_assets 13 | end 14 | end 15 | 16 | def compute_asset_path(path, options = {}) 17 | @dependencies << 'actioncontroller-asset-url-config' 18 | 19 | begin 20 | asset_uri = resolve(path) 21 | rescue FileNotFound 22 | # TODO: eh, we should be able to use a form of locate that returns 23 | # nil instead of raising an exception. 24 | end 25 | 26 | if asset_uri 27 | asset = link_asset(path) 28 | digest_path = asset.digest_path 29 | path = digest_path if digest_assets 30 | File.join(assets_prefix || "/", path) 31 | else 32 | super 33 | end 34 | end 35 | end 36 | end 37 | 38 | register_dependency_resolver 'actioncontroller-asset-url-config' do |env| 39 | config = env.context_class.config 40 | [config.relative_url_root, 41 | (config.asset_host unless config.asset_host.respond_to?(:call))] 42 | end 43 | 44 | # fallback to the default pipeline when using Sprockets 3.x 45 | unless config[:pipelines].include? :debug 46 | register_pipeline :debug, config[:pipelines][:default] 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/sprockets/rails/deprecator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_support" 4 | 5 | module Sprockets 6 | module Rails 7 | def self.deprecator 8 | @deprecator ||= ActiveSupport::Deprecation.new("4.0", "Sprockets::Rails") 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/sprockets/rails/helper.rb: -------------------------------------------------------------------------------- 1 | require 'action_view' 2 | require 'sprockets' 3 | require 'active_support/core_ext/class/attribute' 4 | require 'sprockets/rails/utils' 5 | 6 | module Sprockets 7 | module Rails 8 | module Helper 9 | class AssetNotFound < StandardError; end 10 | class AssetNotPrecompiled < StandardError; end 11 | 12 | class AssetNotPrecompiledError < AssetNotPrecompiled 13 | include Sprockets::Rails::Utils 14 | def initialize(source) 15 | msg = 16 | if using_sprockets4? 17 | "Asset `#{ source }` was not declared to be precompiled in production.\n" + 18 | "Declare links to your assets in `app/assets/config/manifest.js`.\n\n" + 19 | " //= link #{ source }\n\n" + 20 | "and restart your server" 21 | else 22 | "Asset was not declared to be precompiled in production.\n" + 23 | "Add `Rails.application.config.assets.precompile += " + 24 | "%w( #{source} )` to `config/initializers/assets.rb` and " + 25 | "restart your server" 26 | end 27 | super(msg) 28 | end 29 | end 30 | 31 | include ActionView::Helpers::AssetUrlHelper 32 | include ActionView::Helpers::AssetTagHelper 33 | include Sprockets::Rails::Utils 34 | 35 | VIEW_ACCESSORS = [ 36 | :assets_environment, :assets_manifest, 37 | :assets_precompile, :precompiled_asset_checker, 38 | :assets_prefix, :digest_assets, :debug_assets, 39 | :resolve_assets_with, :check_precompiled_asset, 40 | :unknown_asset_fallback 41 | ] 42 | 43 | def self.included(klass) 44 | klass.class_attribute(*VIEW_ACCESSORS) 45 | 46 | klass.class_eval do 47 | remove_method :assets_environment 48 | def assets_environment 49 | if instance_variable_defined?(:@assets_environment) 50 | @assets_environment = @assets_environment.cached 51 | elsif env = self.class.assets_environment 52 | @assets_environment = env.cached 53 | else 54 | nil 55 | end 56 | end 57 | end 58 | end 59 | 60 | def self.extended(obj) 61 | obj.singleton_class.class_eval do 62 | attr_accessor(*VIEW_ACCESSORS) 63 | 64 | remove_method :assets_environment 65 | def assets_environment 66 | if env = @assets_environment 67 | @assets_environment = env.cached 68 | else 69 | nil 70 | end 71 | end 72 | end 73 | end 74 | 75 | # Writes over the built in ActionView::Helpers::AssetUrlHelper#compute_asset_path 76 | # to use the asset pipeline. 77 | def compute_asset_path(path, options = {}) 78 | debug = options[:debug] 79 | 80 | if asset_path = resolve_asset_path(path, debug) 81 | File.join(assets_prefix || "/", legacy_debug_path(asset_path, debug)) 82 | else 83 | message = "The asset #{ path.inspect } is not present in the asset pipeline.\n" 84 | raise AssetNotFound, message unless unknown_asset_fallback 85 | 86 | if respond_to?(:public_compute_asset_path) 87 | message << "Falling back to an asset that may be in the public folder.\n" 88 | message << "This behavior is deprecated and will be removed.\n" 89 | message << "To bypass the asset pipeline and preserve this behavior,\n" 90 | message << "use the `skip_pipeline: true` option.\n" 91 | 92 | Sprockets::Rails.deprecator.warn(message, caller_locations) 93 | end 94 | super 95 | end 96 | end 97 | 98 | # Resolve the asset path against the Sprockets manifest or environment. 99 | # Returns nil if it's an asset we don't know about. 100 | def resolve_asset_path(path, allow_non_precompiled = false) #:nodoc: 101 | resolve_asset do |resolver| 102 | resolver.asset_path path, digest_assets, allow_non_precompiled 103 | end 104 | end 105 | 106 | # Expand asset path to digested form. 107 | # 108 | # path - String path 109 | # options - Hash options 110 | # 111 | # Returns String path or nil if no asset was found. 112 | def asset_digest_path(path, options = {}) 113 | resolve_asset do |resolver| 114 | resolver.digest_path path, options[:debug] 115 | end 116 | end 117 | 118 | # Experimental: Get integrity for asset path. 119 | # 120 | # path - String path 121 | # options - Hash options 122 | # 123 | # Returns String integrity attribute or nil if no asset was found. 124 | def asset_integrity(path, options = {}) 125 | path = path_with_extname(path, options) 126 | 127 | resolve_asset do |resolver| 128 | resolver.integrity path 129 | end 130 | end 131 | 132 | # Override javascript tag helper to provide debugging support. 133 | # 134 | # Eventually will be deprecated and replaced by source maps. 135 | def javascript_include_tag(*sources) 136 | options = sources.extract_options!.stringify_keys 137 | integrity = compute_integrity?(options) 138 | 139 | if options["debug"] != false && request_debug_assets? 140 | sources.map { |source| 141 | if asset = lookup_debug_asset(source, type: :javascript) 142 | if asset.respond_to?(:to_a) 143 | asset.to_a.map do |a| 144 | super(path_to_javascript(a.logical_path, debug: true), options) 145 | end 146 | else 147 | super(path_to_javascript(asset.logical_path, debug: true), options) 148 | end 149 | else 150 | super(source, options) 151 | end 152 | }.flatten.uniq.join("\n").html_safe 153 | else 154 | sources.map { |source| 155 | options = options.merge('integrity' => asset_integrity(source, type: :javascript)) if integrity 156 | super source, options 157 | }.join("\n").html_safe 158 | end 159 | end 160 | 161 | # Override stylesheet tag helper to provide debugging support. 162 | # 163 | # Eventually will be deprecated and replaced by source maps. 164 | def stylesheet_link_tag(*sources) 165 | options = sources.extract_options!.stringify_keys 166 | integrity = compute_integrity?(options) 167 | 168 | if options["debug"] != false && request_debug_assets? 169 | sources.map { |source| 170 | if asset = lookup_debug_asset(source, type: :stylesheet) 171 | if asset.respond_to?(:to_a) 172 | asset.to_a.map do |a| 173 | super(path_to_stylesheet(a.logical_path, debug: true), options) 174 | end 175 | else 176 | super(path_to_stylesheet(asset.logical_path, debug: true), options) 177 | end 178 | else 179 | super(source, options) 180 | end 181 | }.flatten.uniq.join("\n").html_safe 182 | else 183 | sources.map { |source| 184 | options = options.merge('integrity' => asset_integrity(source, type: :stylesheet)) if integrity 185 | super source, options 186 | }.join("\n").html_safe 187 | end 188 | end 189 | 190 | protected 191 | # This is awkward: `integrity` is a boolean option indicating whether 192 | # we want to include or omit the subresource integrity hash, but the 193 | # options hash is also passed through as literal tag attributes. 194 | # That means we have to delete the shortcut boolean option so it 195 | # doesn't bleed into the tag attributes, but also check its value if 196 | # it's boolean-ish. 197 | def compute_integrity?(options) 198 | if secure_subresource_integrity_context? 199 | case options['integrity'] 200 | when nil, false, true 201 | options.delete('integrity') == true 202 | end 203 | else 204 | options.delete 'integrity' 205 | false 206 | end 207 | end 208 | 209 | # Only serve integrity metadata for HTTPS requests: 210 | # http://www.w3.org/TR/SRI/#non-secure-contexts-remain-non-secure 211 | def secure_subresource_integrity_context? 212 | respond_to?(:request) && self.request && (self.request.local? || self.request.ssl?) 213 | end 214 | 215 | # Enable split asset debugging. Eventually will be deprecated 216 | # and replaced by source maps in Sprockets 3.x. 217 | def request_debug_assets? 218 | debug_assets || (defined?(controller) && controller && params[:debug_assets]) 219 | rescue # FIXME: what exactly are we rescuing? 220 | false 221 | end 222 | 223 | # Internal method to support multifile debugging. Will 224 | # eventually be removed w/ Sprockets 3.x. 225 | def lookup_debug_asset(path, options = {}) 226 | path = path_with_extname(path, options) 227 | 228 | resolve_asset do |resolver| 229 | resolver.find_debug_asset path 230 | end 231 | end 232 | 233 | # compute_asset_extname is in AV::Helpers::AssetUrlHelper 234 | def path_with_extname(path, options) 235 | path = path.to_s 236 | "#{path}#{compute_asset_extname(path, options)}" 237 | end 238 | 239 | # Try each asset resolver and return the first non-nil result. 240 | def resolve_asset 241 | asset_resolver_strategies.detect do |resolver| 242 | if result = yield(resolver) 243 | break result 244 | end 245 | end 246 | end 247 | 248 | # List of resolvers in `config.assets.resolve_with` order. 249 | def asset_resolver_strategies 250 | @asset_resolver_strategies ||= 251 | Array(resolve_assets_with).map do |name| 252 | HelperAssetResolvers[name].new(self) 253 | end 254 | end 255 | 256 | # Append ?body=1 if debug is on and we're on old Sprockets. 257 | def legacy_debug_path(path, debug) 258 | if debug && !using_sprockets4? 259 | "#{path}?body=1" 260 | else 261 | path 262 | end 263 | end 264 | end 265 | 266 | # Use a separate module since Helper is mixed in and we needn't pollute 267 | # the class namespace with our internals. 268 | module HelperAssetResolvers #:nodoc: 269 | def self.[](name) 270 | case name 271 | when :manifest 272 | Manifest 273 | when :environment 274 | Environment 275 | else 276 | raise ArgumentError, "Unrecognized asset resolver: #{name.inspect}. Expected :manifest or :environment" 277 | end 278 | end 279 | 280 | class Manifest #:nodoc: 281 | def initialize(view) 282 | @manifest = view.assets_manifest 283 | raise ArgumentError, 'config.assets.resolve_with includes :manifest, but app.assets_manifest is nil' unless @manifest 284 | end 285 | 286 | def asset_path(path, digest, allow_non_precompiled = false) 287 | if digest 288 | digest_path path, allow_non_precompiled 289 | end 290 | end 291 | 292 | def digest_path(path, allow_non_precompiled = false) 293 | @manifest.assets[path] 294 | end 295 | 296 | def integrity(path) 297 | if meta = metadata(path) 298 | meta["integrity"] 299 | end 300 | end 301 | 302 | def find_debug_asset(path) 303 | nil 304 | end 305 | 306 | private 307 | def metadata(path) 308 | if digest_path = digest_path(path) 309 | @manifest.files[digest_path] 310 | end 311 | end 312 | end 313 | 314 | class Environment #:nodoc: 315 | def initialize(view) 316 | raise ArgumentError, 'config.assets.resolve_with includes :environment, but app.assets is nil' unless view.assets_environment 317 | @env = view.assets_environment 318 | @precompiled_asset_checker = view.precompiled_asset_checker 319 | @check_precompiled_asset = view.check_precompiled_asset 320 | end 321 | 322 | def asset_path(path, digest, allow_non_precompiled = false) 323 | # Digests enabled? Do the work to calculate the full asset path. 324 | if digest 325 | digest_path path, allow_non_precompiled 326 | 327 | # Otherwise, ask the Sprockets environment whether the asset exists 328 | # and check whether it's also precompiled for production deploys. 329 | elsif asset = find_asset(path) 330 | raise_unless_precompiled_asset asset.logical_path unless allow_non_precompiled 331 | path 332 | end 333 | end 334 | 335 | def digest_path(path, allow_non_precompiled = false) 336 | if asset = find_asset(path) 337 | raise_unless_precompiled_asset asset.logical_path unless allow_non_precompiled 338 | asset.digest_path 339 | end 340 | end 341 | 342 | def integrity(path) 343 | find_asset(path).try :integrity 344 | end 345 | 346 | def find_debug_asset(path) 347 | if asset = find_asset(path, pipeline: :debug) 348 | raise_unless_precompiled_asset asset.logical_path.sub('.debug', '') 349 | asset 350 | end 351 | end 352 | 353 | private 354 | if RUBY_VERSION >= "2.7" 355 | class_eval <<-RUBY, __FILE__, __LINE__ + 1 356 | def find_asset(path, options = {}) 357 | @env[path, **options] 358 | end 359 | RUBY 360 | else 361 | def find_asset(path, options = {}) 362 | @env[path, options] 363 | end 364 | end 365 | 366 | def precompiled?(path) 367 | @precompiled_asset_checker.call path 368 | end 369 | 370 | def raise_unless_precompiled_asset(path) 371 | raise Helper::AssetNotPrecompiledError.new(path) if @check_precompiled_asset && !precompiled?(path) 372 | end 373 | end 374 | end 375 | end 376 | end 377 | -------------------------------------------------------------------------------- /lib/sprockets/rails/quiet_assets.rb: -------------------------------------------------------------------------------- 1 | module Sprockets 2 | module Rails 3 | class LoggerSilenceError < StandardError; end 4 | 5 | class QuietAssets 6 | def initialize(app) 7 | @app = app 8 | @assets_regex = %r(\A/{0,2}#{::Rails.application.config.assets.prefix}) 9 | end 10 | 11 | def call(env) 12 | if env['PATH_INFO'] =~ @assets_regex 13 | raise_logger_silence_error unless ::Rails.logger.respond_to?(:silence) 14 | 15 | ::Rails.logger.silence { @app.call(env) } 16 | else 17 | @app.call(env) 18 | end 19 | end 20 | 21 | private 22 | def raise_logger_silence_error 23 | error = <<~ERROR 24 | You have enabled `config.assets.quiet`, but your `Rails.logger` 25 | does not use the `LoggerSilence` module. 26 | 27 | Please use a compatible logger such as `ActiveSupport::Logger` 28 | to take advantage of quiet asset logging. 29 | 30 | ERROR 31 | 32 | raise LoggerSilenceError, error 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/sprockets/rails/route_wrapper.rb: -------------------------------------------------------------------------------- 1 | module Sprockets 2 | module Rails 3 | module RouteWrapper 4 | def internal_assets_path? 5 | path =~ %r{\A#{self.class.assets_prefix}\z} 6 | end 7 | 8 | def internal? 9 | super || internal_assets_path? 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/sprockets/rails/sourcemapping_url_processor.rb: -------------------------------------------------------------------------------- 1 | module Sprockets 2 | module Rails 3 | # Rewrites source mapping urls with the digested paths and protect against semicolon appending with a dummy comment line 4 | class SourcemappingUrlProcessor 5 | REGEX = /\/\/# sourceMappingURL=(.*\.map)/ 6 | 7 | class << self 8 | def call(input) 9 | env = input[:environment] 10 | context = env.context_class.new(input) 11 | data = input[:data].gsub(REGEX) do |_match| 12 | sourcemap_logical_path = combine_sourcemap_logical_path(sourcefile: input[:name], sourcemap: $1) 13 | 14 | begin 15 | resolved_sourcemap_comment(sourcemap_logical_path, context: context) 16 | rescue Sprockets::FileNotFound 17 | removed_sourcemap_comment(sourcemap_logical_path, filename: input[:filename], env: env) 18 | end 19 | end 20 | 21 | { data: data } 22 | end 23 | 24 | private 25 | def combine_sourcemap_logical_path(sourcefile:, sourcemap:) 26 | if (parts = sourcefile.split("/")).many? 27 | parts[0..-2].append(sourcemap).join("/") 28 | else 29 | sourcemap 30 | end 31 | end 32 | 33 | def resolved_sourcemap_comment(sourcemap_logical_path, context:) 34 | "//# sourceMappingURL=#{sourcemap_asset_path(sourcemap_logical_path, context: context)}\n//!\n" 35 | end 36 | 37 | def sourcemap_asset_path(sourcemap_logical_path, context:) 38 | # FIXME: Work-around for bug where if the sourcemap is nested two levels deep, it'll resolve as the source file 39 | # that's being mapped, rather than the map itself. So context.resolve("a/b/c.js.map") will return "c.js?" 40 | if context.resolve(sourcemap_logical_path) =~ /\.map/ 41 | context.asset_path(sourcemap_logical_path) 42 | else 43 | raise Sprockets::FileNotFound, "Failed to resolve source map asset due to nesting depth" 44 | end 45 | end 46 | 47 | def removed_sourcemap_comment(sourcemap_logical_path, filename:, env:) 48 | env.logger.warn "Removed sourceMappingURL comment for missing asset '#{sourcemap_logical_path}' from #{filename}" 49 | nil 50 | end 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/sprockets/rails/task.rb: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/sprocketstask' 3 | require 'sprockets' 4 | require 'action_view' 5 | 6 | module Sprockets 7 | module Rails 8 | class Task < Rake::SprocketsTask 9 | attr_accessor :app 10 | 11 | def initialize(app = nil) 12 | self.app = app 13 | super() 14 | end 15 | 16 | def environment 17 | if app 18 | # Use initialized app.assets or force build an environment if 19 | # config.assets.compile is disabled 20 | app.assets || Sprockets::Railtie.build_environment(app) 21 | else 22 | super 23 | end 24 | end 25 | 26 | def output 27 | if app 28 | config = app.config 29 | File.join(config.paths['public'].first, config.assets.prefix) 30 | else 31 | super 32 | end 33 | end 34 | 35 | def assets 36 | if app 37 | app.config.assets.precompile 38 | else 39 | super 40 | end 41 | end 42 | 43 | def manifest 44 | if app 45 | Sprockets::Manifest.new(index, output, app.config.assets.manifest) 46 | else 47 | super 48 | end 49 | end 50 | 51 | def define 52 | namespace :assets do 53 | %w( environment precompile clean clobber ).each do |task| 54 | Rake::Task[task].clear if Rake::Task.task_defined?(task) 55 | end 56 | 57 | # Override this task change the loaded dependencies 58 | desc "Load asset compile environment" 59 | task :environment do 60 | # Load full Rails environment by default 61 | Rake::Task['environment'].invoke 62 | end 63 | 64 | desc "Compile all the assets named in config.assets.precompile" 65 | task :precompile => :environment do 66 | with_logger do 67 | manifest.compile(assets) 68 | end 69 | end 70 | 71 | desc "Remove old compiled assets" 72 | task :clean, [:keep] => :environment do |t, args| 73 | with_logger do 74 | manifest.clean(Integer(args.keep || self.keep)) 75 | end 76 | end 77 | 78 | desc "Remove compiled assets" 79 | task :clobber => :environment do 80 | with_logger do 81 | manifest.clobber 82 | end 83 | end 84 | end 85 | end 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/sprockets/rails/utils.rb: -------------------------------------------------------------------------------- 1 | require 'sprockets' 2 | 3 | module Sprockets 4 | module Rails 5 | module Utils 6 | def using_sprockets4? 7 | Gem::Version.new(Sprockets::VERSION) >= Gem::Version.new('4.x') 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/sprockets/rails/version.rb: -------------------------------------------------------------------------------- 1 | module Sprockets 2 | module Rails 3 | VERSION = "3.5.2" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/sprockets/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'rails/railtie' 3 | require 'action_controller/railtie' 4 | require 'active_support/core_ext/module/remove_method' 5 | require 'active_support/core_ext/numeric/bytes' 6 | require 'sprockets' 7 | 8 | require 'sprockets/rails/asset_url_processor' 9 | require 'sprockets/rails/deprecator' 10 | require 'sprockets/rails/sourcemapping_url_processor' 11 | require 'sprockets/rails/context' 12 | require 'sprockets/rails/helper' 13 | require 'sprockets/rails/quiet_assets' 14 | require 'sprockets/rails/route_wrapper' 15 | require 'sprockets/rails/version' 16 | require 'set' 17 | 18 | module Rails 19 | class Application 20 | # Hack: We need to remove Rails' built in config.assets so we can 21 | # do our own thing. 22 | class Configuration 23 | remove_possible_method :assets 24 | end 25 | 26 | # Undefine Rails' assets method before redefining it, to avoid warnings. 27 | remove_possible_method :assets 28 | remove_possible_method :assets= 29 | 30 | # Returns Sprockets::Environment for app config. 31 | attr_accessor :assets 32 | 33 | # Returns Sprockets::Manifest for app config. 34 | attr_accessor :assets_manifest 35 | 36 | # Called from asset helpers to alert you if you reference an asset URL that 37 | # isn't precompiled and hence won't be available in production. 38 | def asset_precompiled?(logical_path) 39 | if precompiled_assets.include?(logical_path) 40 | true 41 | elsif !config.cache_classes 42 | # Check to see if precompile list has been updated 43 | precompiled_assets(true).include?(logical_path) 44 | else 45 | false 46 | end 47 | end 48 | 49 | # Lazy-load the precompile list so we don't cause asset compilation at app 50 | # boot time, but ensure we cache the list so we don't recompute it for each 51 | # request or test case. 52 | def precompiled_assets(clear_cache = false) 53 | @precompiled_assets = nil if clear_cache 54 | @precompiled_assets ||= assets_manifest.find(config.assets.precompile).map(&:logical_path).to_set 55 | end 56 | end 57 | end 58 | 59 | module Sprockets 60 | class Railtie < ::Rails::Railtie 61 | include Sprockets::Rails::Utils 62 | 63 | class ManifestNeededError < StandardError 64 | def initialize 65 | msg = "Expected to find a manifest file in `app/assets/config/manifest.js`\n" + 66 | "But did not, please create this file and use it to link any assets that need\n" + 67 | "to be rendered by your app:\n\n" + 68 | "Example:\n" + 69 | " //= link_tree ../images\n" + 70 | " //= link_directory ../javascripts .js\n" + 71 | " //= link_directory ../stylesheets .css\n" + 72 | "and restart your server\n\n" + 73 | "For more information see: https://github.com/rails/sprockets/blob/070fc01947c111d35bb4c836e9bb71962a8e0595/UPGRADING.md#manifestjs" 74 | super msg 75 | end 76 | end 77 | 78 | LOOSE_APP_ASSETS = lambda do |logical_path, filename| 79 | filename.start_with?(::Rails.root.join("app/assets").to_s) && 80 | !['.js', '.css', ''].include?(File.extname(logical_path)) 81 | end 82 | 83 | class OrderedOptions < ActiveSupport::OrderedOptions 84 | def configure(&block) 85 | self._blocks << block 86 | end 87 | end 88 | 89 | ::Rails::Engine.initializer :append_assets_path, :group => :all do |app| 90 | app.config.assets.paths.unshift(*paths["vendor/assets"].existent_directories) 91 | app.config.assets.paths.unshift(*paths["lib/assets"].existent_directories) 92 | app.config.assets.paths.unshift(*paths["app/assets"].existent_directories) 93 | end 94 | 95 | config.assets = OrderedOptions.new 96 | config.assets._blocks = [] 97 | config.assets.paths = [] 98 | config.assets.precompile = [] 99 | config.assets.prefix = "/assets" 100 | config.assets.manifest = nil 101 | config.assets.quiet = false 102 | config.assets.resolve_assets_in_css_urls = true 103 | 104 | initializer :set_default_precompile do |app| 105 | if using_sprockets4? 106 | raise ManifestNeededError unless ::Rails.root.join("app/assets/config/manifest.js").exist? 107 | app.config.assets.precompile += %w( manifest.js ) 108 | else 109 | app.config.assets.precompile += [LOOSE_APP_ASSETS, /(?:\/|\\|\A)application\.(css|js)$/] 110 | end 111 | end 112 | 113 | initializer :quiet_assets do |app| 114 | if app.config.assets.quiet 115 | app.middleware.insert_before ::Rails::Rack::Logger, ::Sprockets::Rails::QuietAssets 116 | end 117 | end 118 | 119 | initializer :asset_url_processor do |app| 120 | if app.config.assets.resolve_assets_in_css_urls 121 | Sprockets.register_postprocessor "text/css", ::Sprockets::Rails::AssetUrlProcessor 122 | end 123 | end 124 | 125 | initializer :asset_sourcemap_url_processor do |app| 126 | Sprockets.register_postprocessor "application/javascript", ::Sprockets::Rails::SourcemappingUrlProcessor 127 | end 128 | 129 | initializer "sprockets-rails.deprecator" do |app| 130 | app.deprecators[:sprockets_rails] = Sprockets::Rails.deprecator if app.respond_to?(:deprecators) 131 | end 132 | 133 | config.assets.version = "" 134 | config.assets.debug = false 135 | config.assets.compile = true 136 | config.assets.digest = true 137 | config.assets.cache_limit = 50.megabytes 138 | config.assets.gzip = true 139 | config.assets.check_precompiled_asset = true 140 | config.assets.unknown_asset_fallback = true 141 | 142 | config.assets.configure do |env| 143 | config.assets.paths.each { |path| env.append_path(path) } 144 | end 145 | 146 | config.assets.configure do |env| 147 | env.context_class.send :include, ::Sprockets::Rails::Context 148 | env.context_class.assets_prefix = config.assets.prefix 149 | env.context_class.digest_assets = config.assets.digest 150 | env.context_class.config = config.action_controller 151 | end 152 | 153 | config.assets.configure do |env| 154 | env.cache = Sprockets::Cache::FileStore.new( 155 | "#{env.root}/tmp/cache/assets", 156 | config.assets.cache_limit, 157 | env.logger 158 | ) 159 | end 160 | 161 | Sprockets.register_dependency_resolver 'rails-env' do 162 | ::Rails.env.to_s 163 | end 164 | 165 | config.assets.configure do |env| 166 | env.depend_on 'rails-env' 167 | end 168 | 169 | config.assets.configure do |env| 170 | env.version = config.assets.version 171 | end 172 | 173 | config.assets.configure do |env| 174 | env.gzip = config.assets.gzip if env.respond_to?(:gzip=) 175 | end 176 | 177 | rake_tasks do |app| 178 | require 'sprockets/rails/task' 179 | Sprockets::Rails::Task.new(app) 180 | end 181 | 182 | def build_environment(app, initialized = nil) 183 | initialized = app.initialized? if initialized.nil? 184 | unless initialized 185 | ::Rails.logger.warn "Application uninitialized: Try calling YourApp::Application.initialize!" 186 | end 187 | 188 | env = Sprockets::Environment.new(app.root.to_s) 189 | 190 | config = app.config 191 | 192 | # Run app.assets.configure blocks 193 | config.assets._blocks.each do |block| 194 | block.call(env) 195 | end 196 | 197 | # Set compressors after the configure blocks since they can 198 | # define new compressors and we only accept existent compressors. 199 | env.js_compressor = config.assets.js_compressor 200 | env.css_compressor = config.assets.css_compressor 201 | 202 | # No more configuration changes at this point. 203 | # With cache classes on, Sprockets won't check the FS when files 204 | # change. Preferable in production when the FS only changes on 205 | # deploys when the app restarts. 206 | if config.cache_classes 207 | env = env.cached 208 | end 209 | 210 | env 211 | end 212 | 213 | def self.build_manifest(app) 214 | config = app.config 215 | path = File.join(config.paths['public'].first, config.assets.prefix) 216 | Sprockets::Manifest.new(app.assets, path, config.assets.manifest) 217 | end 218 | 219 | config.after_initialize do |app| 220 | config = app.config 221 | 222 | if config.assets.compile 223 | app.assets = self.build_environment(app, true) 224 | app.routes.prepend do 225 | mount app.assets, at: config.assets.prefix 226 | end 227 | end 228 | 229 | app.assets_manifest = build_manifest(app) 230 | 231 | if config.assets.resolve_with.nil? 232 | config.assets.resolve_with = [] 233 | config.assets.resolve_with << :manifest if config.assets.digest && !config.assets.debug 234 | config.assets.resolve_with << :environment if config.assets.compile 235 | end 236 | 237 | ActionDispatch::Routing::RouteWrapper.class_eval do 238 | class_attribute :assets_prefix 239 | 240 | prepend Sprockets::Rails::RouteWrapper 241 | 242 | self.assets_prefix = config.assets.prefix 243 | end 244 | 245 | ActiveSupport.on_load(:action_view) do 246 | include Sprockets::Rails::Helper 247 | 248 | # Copy relevant config to AV context 249 | self.debug_assets = config.assets.debug 250 | self.digest_assets = config.assets.digest 251 | self.assets_prefix = config.assets.prefix 252 | self.assets_precompile = config.assets.precompile 253 | 254 | self.assets_environment = app.assets 255 | self.assets_manifest = app.assets_manifest 256 | 257 | self.resolve_assets_with = config.assets.resolve_with 258 | 259 | self.check_precompiled_asset = config.assets.check_precompiled_asset 260 | self.unknown_asset_fallback = config.assets.unknown_asset_fallback 261 | # Expose the app precompiled asset check to the view 262 | self.precompiled_asset_checker = -> logical_path { app.asset_precompiled? logical_path } 263 | end 264 | end 265 | end 266 | end 267 | -------------------------------------------------------------------------------- /sprockets-rails.gemspec: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("../lib", __FILE__) 2 | require "sprockets/rails/version" 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "sprockets-rails" 6 | s.version = Sprockets::Rails::VERSION 7 | 8 | s.homepage = "https://github.com/rails/sprockets-rails" 9 | s.summary = "Sprockets Rails integration" 10 | s.license = "MIT" 11 | 12 | s.metadata = { 13 | "changelog_uri" => "#{s.homepage}/releases/tag/v#{s.version}" 14 | } 15 | 16 | s.files = Dir["README.md", "lib/**/*.rb", "MIT-LICENSE"] 17 | 18 | s.required_ruby_version = '>= 2.5' 19 | 20 | s.add_dependency "sprockets", ">= 3.0.0" 21 | s.add_dependency "actionpack", ">= 6.1" 22 | s.add_dependency "activesupport", ">= 6.1" 23 | s.add_development_dependency "railties", ">= 6.1" 24 | s.add_development_dependency "rake" 25 | s.add_development_dependency "sass" 26 | s.add_development_dependency "uglifier" 27 | 28 | s.author = "Joshua Peek" 29 | s.email = "josh@joshpeek.com" 30 | end 31 | -------------------------------------------------------------------------------- /test/fixtures/bar.css: -------------------------------------------------------------------------------- 1 | /*= require foo */ 2 | -------------------------------------------------------------------------------- /test/fixtures/bar.js: -------------------------------------------------------------------------------- 1 | //= require foo 2 | var Bar; 3 | -------------------------------------------------------------------------------- /test/fixtures/bundle/index.js: -------------------------------------------------------------------------------- 1 | var jQuery; 2 | -------------------------------------------------------------------------------- /test/fixtures/dependency.css: -------------------------------------------------------------------------------- 1 | /* dependency */ 2 | -------------------------------------------------------------------------------- /test/fixtures/dependency.js: -------------------------------------------------------------------------------- 1 | // dependency 2 | -------------------------------------------------------------------------------- /test/fixtures/error/dependency.js.erb: -------------------------------------------------------------------------------- 1 | <%= asset_path("bar.js") %> 2 | -------------------------------------------------------------------------------- /test/fixtures/error/missing.css.erb: -------------------------------------------------------------------------------- 1 | p { background: url(<%= image_path "does_not_exist.png" %>); } 2 | -------------------------------------------------------------------------------- /test/fixtures/file1.css: -------------------------------------------------------------------------------- 1 | /*= require dependency 2 | */ 3 | -------------------------------------------------------------------------------- /test/fixtures/file1.js: -------------------------------------------------------------------------------- 1 | //= require dependency 2 | -------------------------------------------------------------------------------- /test/fixtures/file2.css: -------------------------------------------------------------------------------- 1 | /*= require dependency 2 | */ 3 | -------------------------------------------------------------------------------- /test/fixtures/file2.js: -------------------------------------------------------------------------------- 1 | //= require dependency 2 | -------------------------------------------------------------------------------- /test/fixtures/foo.css: -------------------------------------------------------------------------------- 1 | .foo {} 2 | -------------------------------------------------------------------------------- /test/fixtures/foo.js: -------------------------------------------------------------------------------- 1 | var Foo; -------------------------------------------------------------------------------- /test/fixtures/jquery/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery", 3 | "main": "./jquery.js" 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/jquery/jquery.js: -------------------------------------------------------------------------------- 1 | var jQuery; 2 | -------------------------------------------------------------------------------- /test/fixtures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rails/sprockets-rails/266ec49f3c7c96018dd75f9dc4f9b62fe3f7eecf/test/fixtures/logo.png -------------------------------------------------------------------------------- /test/fixtures/manifest.js: -------------------------------------------------------------------------------- 1 | // manifest.js 2 | //= link foo.css 3 | //= link foo.js 4 | //= link bar.css 5 | //= link bar.js 6 | //= link file1.css 7 | //= link file1.js 8 | //= link file2.css 9 | //= link file2.js 10 | //= link bundle.js 11 | -------------------------------------------------------------------------------- /test/fixtures/not_precompiled.css: -------------------------------------------------------------------------------- 1 | .not-precompiled {} 2 | -------------------------------------------------------------------------------- /test/fixtures/not_precompiled.js: -------------------------------------------------------------------------------- 1 | var not_precompiled; 2 | -------------------------------------------------------------------------------- /test/fixtures/url.css.erb: -------------------------------------------------------------------------------- 1 | p { background: url(<%= image_path "logo.png" %>); } 2 | -------------------------------------------------------------------------------- /test/fixtures/url.js.erb: -------------------------------------------------------------------------------- 1 | var url = '<%= javascript_path :foo %>'; 2 | -------------------------------------------------------------------------------- /test/test_asset_url_processor.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'sprockets/railtie' 3 | 4 | 5 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 6 | class TestAssetUrlProcessor < Minitest::Test 7 | FIXTURES_PATH = File.expand_path("../fixtures", __FILE__) 8 | 9 | def setup 10 | @env = Sprockets::Environment.new 11 | @env.append_path FIXTURES_PATH 12 | @env.context_class.class_eval do 13 | include ::Sprockets::Rails::Context 14 | end 15 | @env.context_class.digest_assets = true 16 | 17 | @logo_digest = @env["logo.png"].etag 18 | @logo_uri = @env["logo.png"].uri 19 | end 20 | 21 | def test_basic 22 | input = { environment: @env, data: 'background: url(logo.png);', filename: 'url2.css', metadata: {} } 23 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 24 | assert_equal("background: url(/logo-#{@logo_digest}.png);", output[:data]) 25 | end 26 | 27 | def test_spaces 28 | input = { environment: @env, data: 'background: url( logo.png );', filename: 'url2.css', metadata: {} } 29 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 30 | assert_equal("background: url(/logo-#{@logo_digest}.png);", output[:data]) 31 | end 32 | 33 | def test_single_quote 34 | input = { environment: @env, data: "background: url('logo.png');", filename: 'url2.css', metadata: {} } 35 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 36 | assert_equal("background: url(/logo-#{@logo_digest}.png);", output[:data]) 37 | end 38 | 39 | def test_double_quote 40 | input = { environment: @env, data: 'background: url("logo.png");', filename: 'url2.css', metadata: {} } 41 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 42 | assert_equal("background: url(/logo-#{@logo_digest}.png);", output[:data]) 43 | end 44 | 45 | def test_dependencies_are_tracked 46 | input = { environment: @env, data: 'background: url(logo.png);', filename: 'url2.css', metadata: {} } 47 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 48 | assert_equal(1, output[:links].size) 49 | assert_equal(@logo_uri, output[:links].first) 50 | end 51 | 52 | def test_relative 53 | input = { environment: @env, data: 'background: url(./logo.png);', filename: 'url2.css', metadata: {} } 54 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 55 | assert_equal("background: url(/logo-#{@logo_digest}.png);", output[:data]) 56 | end 57 | 58 | def test_subdirectory 59 | input = { environment: @env, data: "background: url('jquery/jquery.js');", filename: 'url2.css', metadata: {} } 60 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 61 | jquery_digest = 'c6910e1db4a5ed4905be728ab786471e81565f4a9d544734b199f3790de9f9a3' 62 | assert_equal("background: url(/jquery/jquery-#{jquery_digest}.js);", output[:data]) 63 | end 64 | 65 | def test_protocol_relative_paths 66 | input = { environment: @env, data: "background: url(//assets.example.com/assets/fontawesome-webfont-82ff0fe46a6f60e0ab3c4a9891a0ae0a1f7b7e84c625f55358379177a2dcb202.eot);", filename: 'url2.css', metadata: {} } 67 | output = Sprockets::Rails::AssetUrlProcessor.call(input) 68 | assert_equal("background: url(//assets.example.com/assets/fontawesome-webfont-82ff0fe46a6f60e0ab3c4a9891a0ae0a1f7b7e84c625f55358379177a2dcb202.eot);", output[:data]) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require 'action_view' 4 | require 'sprockets' 5 | # Apps generated before Rails 7.0 without rails/all require sprockets/railtie, not sprockets/rails 6 | # Same with other reverse dependencies like sassc-rails 7 | require 'sprockets/railtie' 8 | require 'sprockets/rails/context' 9 | require 'sprockets/rails/helper' 10 | require 'rails/version' 11 | 12 | ActiveSupport::TestCase.test_order = :random if ActiveSupport::TestCase.respond_to?(:test_order=) 13 | 14 | def append_media_attribute 15 | if ::Rails::VERSION::MAJOR < 7 16 | "media=\"screen\"" 17 | end 18 | end 19 | 20 | class HelperTest < ActionView::TestCase 21 | FIXTURES_PATH = File.expand_path("../fixtures", __FILE__) 22 | 23 | def setup 24 | @assets = Sprockets::Environment.new 25 | @assets.append_path FIXTURES_PATH 26 | @assets.context_class.class_eval do 27 | include ::Sprockets::Rails::Context 28 | end 29 | tmp = File.expand_path("../../tmp", __FILE__) 30 | @manifest = Sprockets::Manifest.new(@assets, tmp) 31 | 32 | @view = ActionView::Base.new(ActionView::LookupContext.new([]), {}, nil) 33 | @view.extend ::Sprockets::Rails::Helper 34 | @view.assets_environment = @assets 35 | @view.assets_manifest = @manifest 36 | @view.resolve_assets_with = [ :manifest, :environment ] 37 | @view.assets_prefix = "/assets" 38 | @view.assets_precompile = %w( manifest.js ) 39 | precompiled_assets = @manifest.find(@view.assets_precompile).map(&:logical_path) 40 | @view.check_precompiled_asset = true 41 | @view.unknown_asset_fallback = true 42 | @view.precompiled_asset_checker = -> logical_path { precompiled_assets.include? logical_path } 43 | @view.request = ActionDispatch::Request.new({ 44 | "rack.url_scheme" => "https" 45 | }) 46 | 47 | @assets.context_class.assets_prefix = @view.assets_prefix 48 | @assets.context_class.config = @view.config 49 | 50 | @foo_js_integrity = @assets['foo.js'].integrity 51 | @foo_css_integrity = @assets['foo.css'].integrity 52 | @bar_js_integrity = @assets['bar.js'].integrity 53 | 54 | @foo_js_digest = @assets['foo.js'].etag 55 | @foo_css_digest = @assets['foo.css'].etag 56 | @bar_js_digest = @assets['bar.js'].etag 57 | @bar_css_digest = @assets['bar.css'].etag 58 | @logo_digest = @assets['logo.png'].etag 59 | 60 | @foo_self_js_digest = @assets['foo.self.js'].etag 61 | @foo_self_css_digest = @assets['foo.self.css'].etag 62 | @bar_self_js_digest = @assets['bar.self.js'].etag 63 | @bar_self_css_digest = @assets['bar.self.css'].etag 64 | 65 | @foo_debug_js_digest = @assets['foo.debug.js'].etag 66 | @foo_debug_css_digest = @assets['foo.debug.css'].etag 67 | @bar_debug_js_digest = @assets['bar.debug.js'].etag 68 | @bar_debug_css_digest = @assets['bar.debug.css'].etag 69 | 70 | @dependency_js_digest = @assets['dependency.js'].etag 71 | @dependency_css_digest = @assets['dependency.css'].etag 72 | @file1_js_digest = @assets['file1.js'].etag 73 | @file1_css_digest = @assets['file1.css'].etag 74 | @file2_js_digest = @assets['file2.js'].etag 75 | @file2_css_digest = @assets['file2.css'].etag 76 | 77 | @dependency_self_js_digest = @assets['dependency.self.js'].etag 78 | @dependency_self_css_digest = @assets['dependency.self.css'].etag 79 | @file1_self_js_digest = @assets['file1.self.js'].etag 80 | @file1_self_css_digest = @assets['file1.self.css'].etag 81 | @file2_self_js_digest = @assets['file2.self.js'].etag 82 | @file2_self_css_digest = @assets['file2.self.css'].etag 83 | 84 | @dependency_debug_js_digest = @assets['dependency.debug.js'].etag 85 | @dependency_debug_css_digest = @assets['dependency.debug.css'].etag 86 | @file1_debug_js_digest = @assets['file1.debug.js'].etag 87 | @file1_debug_css_digest = @assets['file1.debug.css'].etag 88 | @file2_debug_js_digest = @assets['file2.debug.js'].etag 89 | @file2_debug_css_digest = @assets['file2.debug.css'].etag 90 | end 91 | 92 | def using_sprockets4? 93 | Gem::Version.new(Sprockets::VERSION) >= Gem::Version.new('4.x') 94 | end 95 | 96 | def test_foo_and_bar_different_digests 97 | refute_equal @foo_js_digest, @bar_js_digest 98 | refute_equal @foo_css_digest, @bar_css_digest 99 | end 100 | 101 | def assert_servable_asset_url(url) 102 | path, query = url.split("?", 2) 103 | path = path.sub(@view.assets_prefix, "") 104 | 105 | status = @assets.call({ 106 | 'REQUEST_METHOD' => 'GET', 107 | 'PATH_INFO' => path, 108 | 'QUERY_STRING' => query 109 | })[0] 110 | assert_equal 200, status, "#{url} responded with #{status}" 111 | end 112 | end 113 | 114 | class NoHostHelperTest < HelperTest 115 | def test_javascript_include_tag 116 | Sprockets::Rails.deprecator.silence do 117 | assert_dom_equal %(), 118 | @view.javascript_include_tag("static") 119 | assert_dom_equal %(), 120 | @view.javascript_include_tag("static.js") 121 | assert_dom_equal %(), 122 | @view.javascript_include_tag(:static) 123 | 124 | assert_dom_equal %(), 125 | @view.javascript_include_tag("/elsewhere.js") 126 | assert_dom_equal %(\n), 127 | @view.javascript_include_tag("/script1.js", "script2.js") 128 | 129 | assert_dom_equal %(), 130 | @view.javascript_include_tag("http://example.com/script") 131 | assert_dom_equal %(), 132 | @view.javascript_include_tag("http://example.com/script.js") 133 | assert_dom_equal %(), 134 | @view.javascript_include_tag("//example.com/script.js") 135 | 136 | assert_dom_equal %(), 137 | @view.javascript_include_tag("static", :defer => "defer") 138 | assert_dom_equal %(), 139 | @view.javascript_include_tag("static", :async => "async") 140 | end 141 | end 142 | 143 | def test_stylesheet_link_tag 144 | Sprockets::Rails.deprecator.silence do 145 | assert_dom_equal %(), 146 | @view.stylesheet_link_tag("static") 147 | assert_dom_equal %(), 148 | @view.stylesheet_link_tag("static.css") 149 | assert_dom_equal %(), 150 | @view.stylesheet_link_tag(:static) 151 | 152 | assert_dom_equal %(), 153 | @view.stylesheet_link_tag("/elsewhere.css") 154 | assert_dom_equal %(\n), 155 | @view.stylesheet_link_tag("/style1.css", "style2.css") 156 | 157 | assert_dom_equal %(), 158 | @view.stylesheet_link_tag("http://www.example.com/styles/style") 159 | assert_dom_equal %(), 160 | @view.stylesheet_link_tag("http://www.example.com/styles/style.css") 161 | assert_dom_equal %(), 162 | @view.stylesheet_link_tag("//www.example.com/styles/style.css") 163 | 164 | assert_dom_equal %(), 165 | @view.stylesheet_link_tag("print", :media => "print") 166 | assert_dom_equal %(), 167 | @view.stylesheet_link_tag("print", :media => "") 168 | end 169 | end 170 | 171 | def test_javascript_include_tag_integrity 172 | Sprockets::Rails.deprecator.silence do 173 | assert_dom_equal %(), 174 | @view.javascript_include_tag("static", integrity: "sha-256-TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs=") 175 | 176 | assert_dom_equal %(), 177 | @view.javascript_include_tag("static", integrity: true) 178 | assert_dom_equal %(), 179 | @view.javascript_include_tag("static", integrity: false) 180 | assert_dom_equal %(), 181 | @view.javascript_include_tag("static", integrity: nil) 182 | end 183 | end 184 | 185 | def test_stylesheet_link_tag_integrity 186 | Sprockets::Rails.deprecator.silence do 187 | assert_dom_equal %(), 188 | @view.stylesheet_link_tag("static", integrity: "sha-256-5YzTQPuOJz/EpeXfN/+v1sxsjAj/dw8q26abiHZM3A4=") 189 | 190 | assert_dom_equal %(), 191 | @view.stylesheet_link_tag("static", integrity: true) 192 | assert_dom_equal %(), 193 | @view.stylesheet_link_tag("static", integrity: false) 194 | end 195 | end 196 | 197 | def test_javascript_path 198 | Sprockets::Rails.deprecator.silence do 199 | assert_equal "/javascripts/xmlhr.js", @view.javascript_path("xmlhr") 200 | assert_equal "/javascripts/xmlhr.js", @view.javascript_path("xmlhr.js") 201 | assert_equal "/javascripts/super/xmlhr.js", @view.javascript_path("super/xmlhr") 202 | assert_equal "/super/xmlhr.js", @view.javascript_path("/super/xmlhr") 203 | 204 | assert_equal "/javascripts/xmlhr.js?foo=1", @view.javascript_path("xmlhr.js?foo=1") 205 | assert_equal "/javascripts/xmlhr.js?foo=1", @view.javascript_path("xmlhr?foo=1") 206 | assert_equal "/javascripts/xmlhr.js#hash", @view.javascript_path("xmlhr.js#hash") 207 | assert_equal "/javascripts/xmlhr.js#hash", @view.javascript_path("xmlhr#hash") 208 | assert_equal "/javascripts/xmlhr.js?foo=1#hash", @view.javascript_path("xmlhr.js?foo=1#hash") 209 | end 210 | end 211 | 212 | def test_stylesheet_path 213 | Sprockets::Rails.deprecator.silence do 214 | assert_equal "/stylesheets/bank.css", @view.stylesheet_path("bank") 215 | assert_equal "/stylesheets/bank.css", @view.stylesheet_path("bank.css") 216 | assert_equal "/stylesheets/subdir/subdir.css", @view.stylesheet_path("subdir/subdir") 217 | assert_equal "/subdir/subdir.css", @view.stylesheet_path("/subdir/subdir.css") 218 | 219 | assert_equal "/stylesheets/bank.css?foo=1", @view.stylesheet_path("bank.css?foo=1") 220 | assert_equal "/stylesheets/bank.css?foo=1", @view.stylesheet_path("bank?foo=1") 221 | assert_equal "/stylesheets/bank.css#hash", @view.stylesheet_path("bank.css#hash") 222 | assert_equal "/stylesheets/bank.css#hash", @view.stylesheet_path("bank#hash") 223 | assert_equal "/stylesheets/bank.css?foo=1#hash", @view.stylesheet_path("bank.css?foo=1#hash") 224 | end 225 | end 226 | end 227 | 228 | class NoSSLHelperTest < NoHostHelperTest 229 | def setup 230 | super 231 | 232 | @view.request = nil 233 | end 234 | 235 | def test_javascript_include_tag_integrity 236 | Sprockets::Rails.deprecator.silence do 237 | assert_dom_equal %(), 238 | @view.javascript_include_tag("static", integrity: true) 239 | assert_dom_equal %(), 240 | @view.javascript_include_tag("static", integrity: false) 241 | assert_dom_equal %(), 242 | @view.javascript_include_tag("static", integrity: nil) 243 | 244 | assert_dom_equal %(), 245 | @view.javascript_include_tag("static", integrity: "sha-256-TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs=") 246 | end 247 | 248 | assert_dom_equal %(), 249 | @view.javascript_include_tag("foo", integrity: true) 250 | end 251 | 252 | def test_stylesheet_link_tag_integrity 253 | Sprockets::Rails.deprecator.silence do 254 | assert_dom_equal %(), 255 | @view.stylesheet_link_tag("static", integrity: true) 256 | assert_dom_equal %(), 257 | @view.stylesheet_link_tag("static", integrity: false) 258 | assert_dom_equal %(), 259 | @view.stylesheet_link_tag("static", integrity: nil) 260 | 261 | assert_dom_equal %(), 262 | @view.stylesheet_link_tag("static", integrity: "sha-256-5YzTQPuOJz/EpeXfN/+v1sxsjAj/dw8q26abiHZM3A4=") 263 | end 264 | 265 | assert_dom_equal %(), 266 | @view.stylesheet_link_tag("foo", integrity: true) 267 | end 268 | end 269 | 270 | class LocalhostHelperTest < NoHostHelperTest 271 | def setup 272 | super 273 | 274 | @view.request = ActionDispatch::Request.new({ 275 | "rack.url_scheme" => "http", 276 | "REMOTE_ADDR" => "127.0.0.1" 277 | }) 278 | end 279 | 280 | def test_javascript_include_tag_integrity 281 | super 282 | 283 | assert_dom_equal %(), 284 | @view.javascript_include_tag("foo", integrity: false) 285 | assert_dom_equal %(), 286 | @view.javascript_include_tag("foo", integrity: nil) 287 | 288 | assert_dom_equal %(), 289 | @view.javascript_include_tag("foo", integrity: true) 290 | assert_dom_equal %(), 291 | @view.javascript_include_tag("foo.js", integrity: true) 292 | assert_dom_equal %(), 293 | @view.javascript_include_tag(:foo, integrity: true) 294 | 295 | assert_dom_equal %(\n), 296 | @view.javascript_include_tag(:foo, :bar, integrity: true) 297 | end 298 | 299 | def test_stylesheet_link_tag_integrity 300 | super 301 | 302 | assert_dom_equal %(), 303 | @view.stylesheet_link_tag("foo", integrity: false) 304 | assert_dom_equal %(), 305 | @view.stylesheet_link_tag("foo", integrity: nil) 306 | 307 | assert_dom_equal %(), 308 | @view.stylesheet_link_tag("foo", integrity: true) 309 | assert_dom_equal %(), 310 | @view.stylesheet_link_tag("foo.css", integrity: true) 311 | assert_dom_equal %(), 312 | @view.stylesheet_link_tag(:foo, integrity: true) 313 | 314 | assert_dom_equal %(\n), 315 | @view.stylesheet_link_tag(:foo, :bar, integrity: true) 316 | end 317 | end 318 | 319 | class RelativeHostHelperTest < HelperTest 320 | def setup 321 | super 322 | 323 | @view.config.asset_host = "assets.example.com" 324 | end 325 | 326 | def test_javascript_path 327 | Sprockets::Rails.deprecator.silence do 328 | assert_equal "https://assets.example.com/javascripts/xmlhr.js", @view.javascript_path("xmlhr") 329 | assert_equal "https://assets.example.com/javascripts/xmlhr.js", @view.javascript_path("xmlhr.js") 330 | assert_equal "https://assets.example.com/javascripts/super/xmlhr.js", @view.javascript_path("super/xmlhr") 331 | assert_equal "https://assets.example.com/super/xmlhr.js", @view.javascript_path("/super/xmlhr") 332 | 333 | assert_equal "https://assets.example.com/javascripts/xmlhr.js?foo=1", @view.javascript_path("xmlhr.js?foo=1") 334 | assert_equal "https://assets.example.com/javascripts/xmlhr.js?foo=1", @view.javascript_path("xmlhr?foo=1") 335 | assert_equal "https://assets.example.com/javascripts/xmlhr.js#hash", @view.javascript_path("xmlhr.js#hash") 336 | assert_equal "https://assets.example.com/javascripts/xmlhr.js#hash", @view.javascript_path("xmlhr#hash") 337 | assert_equal "https://assets.example.com/javascripts/xmlhr.js?foo=1#hash", @view.javascript_path("xmlhr.js?foo=1#hash") 338 | end 339 | 340 | assert_dom_equal %(), 341 | @view.javascript_include_tag("foo") 342 | assert_dom_equal %(), 343 | @view.javascript_include_tag("foo.js") 344 | assert_dom_equal %(), 345 | @view.javascript_include_tag(:foo) 346 | end 347 | 348 | def test_stylesheet_path 349 | Sprockets::Rails.deprecator.silence do 350 | assert_equal "https://assets.example.com/stylesheets/bank.css", @view.stylesheet_path("bank") 351 | assert_equal "https://assets.example.com/stylesheets/bank.css", @view.stylesheet_path("bank.css") 352 | assert_equal "https://assets.example.com/stylesheets/subdir/subdir.css", @view.stylesheet_path("subdir/subdir") 353 | assert_equal "https://assets.example.com/subdir/subdir.css", @view.stylesheet_path("/subdir/subdir.css") 354 | 355 | assert_equal "https://assets.example.com/stylesheets/bank.css?foo=1", @view.stylesheet_path("bank.css?foo=1") 356 | assert_equal "https://assets.example.com/stylesheets/bank.css?foo=1", @view.stylesheet_path("bank?foo=1") 357 | assert_equal "https://assets.example.com/stylesheets/bank.css#hash", @view.stylesheet_path("bank.css#hash") 358 | assert_equal "https://assets.example.com/stylesheets/bank.css#hash", @view.stylesheet_path("bank#hash") 359 | assert_equal "https://assets.example.com/stylesheets/bank.css?foo=1#hash", @view.stylesheet_path("bank.css?foo=1#hash") 360 | end 361 | 362 | assert_dom_equal %(), 363 | @view.stylesheet_link_tag("foo") 364 | assert_dom_equal %(), 365 | @view.stylesheet_link_tag("foo.css") 366 | assert_dom_equal %(), 367 | @view.stylesheet_link_tag(:foo) 368 | end 369 | 370 | def test_asset_url 371 | assert_equal "var url = '//assets.example.com/assets/foo.js';\n", @assets["url.js"].to_s 372 | assert_equal "p { background: url(//assets.example.com/assets/logo.png); }\n", @assets["url.css"].to_s 373 | end 374 | end 375 | 376 | class NoDigestHelperTest < NoHostHelperTest 377 | def setup 378 | super 379 | @view.digest_assets = false 380 | @assets.context_class.digest_assets = false 381 | end 382 | 383 | def test_javascript_include_tag 384 | super 385 | 386 | assert_dom_equal %(), 387 | @view.javascript_include_tag("foo") 388 | assert_dom_equal %(), 389 | @view.javascript_include_tag("foo.js") 390 | assert_dom_equal %(), 391 | @view.javascript_include_tag(:foo) 392 | 393 | assert_servable_asset_url "/assets/foo.js" 394 | end 395 | 396 | def test_stylesheet_link_tag 397 | super 398 | 399 | assert_dom_equal %(), 400 | @view.stylesheet_link_tag("foo") 401 | assert_dom_equal %(), 402 | @view.stylesheet_link_tag("foo.css") 403 | assert_dom_equal %(), 404 | @view.stylesheet_link_tag(:foo) 405 | 406 | assert_servable_asset_url "/assets/foo.css" 407 | end 408 | 409 | def test_javascript_path 410 | super 411 | 412 | assert_equal "/assets/foo.js", @view.javascript_path("foo") 413 | assert_servable_asset_url "/assets/foo.js" 414 | end 415 | 416 | def test_stylesheet_path 417 | super 418 | 419 | assert_equal "/assets/foo.css", @view.stylesheet_path("foo") 420 | assert_servable_asset_url "/assets/foo.css" 421 | end 422 | 423 | def test_asset_url 424 | assert_equal "var url = '/assets/foo.js';\n", @assets["url.js"].to_s 425 | assert_equal "p { background: url(/assets/logo.png); }\n", @assets["url.css"].to_s 426 | end 427 | end 428 | 429 | class DigestHelperTest < NoHostHelperTest 430 | def setup 431 | super 432 | @view.digest_assets = true 433 | @assets.context_class.digest_assets = true 434 | end 435 | 436 | def test_javascript_include_tag 437 | super 438 | 439 | assert_dom_equal %(), 440 | @view.javascript_include_tag("foo") 441 | assert_dom_equal %(), 442 | @view.javascript_include_tag("foo.js") 443 | assert_dom_equal %(), 444 | @view.javascript_include_tag(:foo) 445 | 446 | assert_dom_equal %(\n), 447 | @view.javascript_include_tag(:foo, :bar) 448 | 449 | assert_servable_asset_url "/assets/foo-#{@foo_js_digest}.js" 450 | end 451 | 452 | def test_stylesheet_link_tag 453 | super 454 | 455 | assert_dom_equal %(), 456 | @view.stylesheet_link_tag("foo") 457 | assert_dom_equal %(), 458 | @view.stylesheet_link_tag("foo.css") 459 | assert_dom_equal %(), 460 | @view.stylesheet_link_tag(:foo) 461 | 462 | assert_dom_equal %(\n), 463 | @view.stylesheet_link_tag(:foo, :bar) 464 | 465 | assert_servable_asset_url "/assets/foo-#{@foo_css_digest}.css" 466 | end 467 | 468 | def test_javascript_include_tag_integrity 469 | super 470 | 471 | assert_dom_equal %(), 472 | @view.javascript_include_tag("foo", integrity: false) 473 | assert_dom_equal %(), 474 | @view.javascript_include_tag("foo", integrity: nil) 475 | 476 | assert_dom_equal %(), 477 | @view.javascript_include_tag("foo", integrity: true) 478 | assert_dom_equal %(), 479 | @view.javascript_include_tag("foo.js", integrity: true) 480 | assert_dom_equal %(), 481 | @view.javascript_include_tag(:foo, integrity: true) 482 | 483 | assert_dom_equal %(\n), 484 | @view.javascript_include_tag(:foo, :bar, integrity: true) 485 | end 486 | 487 | def test_stylesheet_link_tag_integrity 488 | super 489 | 490 | assert_dom_equal %(), 491 | @view.stylesheet_link_tag("foo", integrity: false) 492 | assert_dom_equal %(), 493 | @view.stylesheet_link_tag("foo", integrity: nil) 494 | 495 | assert_dom_equal %(), 496 | @view.stylesheet_link_tag("foo", integrity: true) 497 | assert_dom_equal %(), 498 | @view.stylesheet_link_tag("foo.css", integrity: true) 499 | assert_dom_equal %(), 500 | @view.stylesheet_link_tag(:foo, integrity: true) 501 | 502 | assert_dom_equal %(\n), 503 | @view.stylesheet_link_tag(:foo, :bar, integrity: true) 504 | end 505 | 506 | def test_javascript_path 507 | super 508 | 509 | assert_equal "/assets/foo-#{@foo_js_digest}.js", @view.javascript_path("foo") 510 | assert_servable_asset_url "/assets/foo-#{@foo_js_digest}.js" 511 | end 512 | 513 | def test_stylesheet_path 514 | super 515 | 516 | assert_equal "/assets/foo-#{@foo_css_digest}.css", @view.stylesheet_path("foo") 517 | assert_servable_asset_url "/assets/foo-#{@foo_css_digest}.css" 518 | end 519 | 520 | def test_asset_digest_path 521 | assert_equal "foo-#{@foo_js_digest}.js", @view.asset_digest_path("foo.js") 522 | assert_equal "foo-#{@foo_css_digest}.css", @view.asset_digest_path("foo.css") 523 | end 524 | 525 | def test_asset_url 526 | assert_equal "var url = '/assets/foo-#{@foo_js_digest}.js';\n", @assets["url.js"].to_s 527 | assert_equal "p { background: url(/assets/logo-#{@logo_digest}.png); }\n", @assets["url.css"].to_s 528 | end 529 | end 530 | 531 | class DebugHelperTest < NoHostHelperTest 532 | def setup 533 | super 534 | @view.debug_assets = true 535 | end 536 | 537 | def test_javascript_include_tag 538 | super 539 | 540 | if using_sprockets4? 541 | assert_dom_equal %(), 542 | @view.javascript_include_tag(:foo) 543 | assert_dom_equal %(), 544 | @view.javascript_include_tag(:bar) 545 | assert_dom_equal %(\n), 546 | @view.javascript_include_tag(:file1, :file2) 547 | 548 | assert_servable_asset_url "/assets/foo.debug.js" 549 | assert_servable_asset_url "/assets/bar.debug.js" 550 | assert_servable_asset_url "/assets/dependency.debug.js" 551 | assert_servable_asset_url "/assets/file1.debug.js" 552 | assert_servable_asset_url "/assets/file2.debug.js" 553 | else 554 | assert_dom_equal %(), 555 | @view.javascript_include_tag(:foo) 556 | assert_dom_equal %(\n), 557 | @view.javascript_include_tag(:bar) 558 | assert_dom_equal %(\n\n), 559 | @view.javascript_include_tag(:file1, :file2) 560 | 561 | assert_servable_asset_url "/assets/foo.self.js?body=1" 562 | assert_servable_asset_url "/assets/bar.self.js?body=1" 563 | assert_servable_asset_url "/assets/dependency.self.js?body=1" 564 | assert_servable_asset_url "/assets/file1.self.js?body=1" 565 | assert_servable_asset_url "/assets/file2.self.js?body=1" 566 | end 567 | end 568 | 569 | def test_stylesheet_link_tag 570 | super 571 | 572 | if using_sprockets4? 573 | assert_dom_equal %(), 574 | @view.stylesheet_link_tag(:foo) 575 | assert_dom_equal %(), 576 | @view.stylesheet_link_tag(:bar) 577 | assert_dom_equal %(\n), 578 | @view.stylesheet_link_tag(:file1, :file2) 579 | 580 | assert_servable_asset_url "/assets/foo.self.css" 581 | assert_servable_asset_url "/assets/bar.self.css" 582 | assert_servable_asset_url "/assets/dependency.self.css" 583 | assert_servable_asset_url "/assets/file1.self.css" 584 | assert_servable_asset_url "/assets/file2.self.css" 585 | else 586 | assert_dom_equal %(), 587 | @view.stylesheet_link_tag(:foo) 588 | assert_dom_equal %(\n), 589 | @view.stylesheet_link_tag(:bar) 590 | assert_dom_equal %(\n\n), 591 | @view.stylesheet_link_tag(:file1, :file2) 592 | 593 | assert_servable_asset_url "/assets/foo.self.css?body=1" 594 | assert_servable_asset_url "/assets/bar.self.css?body=1" 595 | assert_servable_asset_url "/assets/dependency.self.css?body=1" 596 | assert_servable_asset_url "/assets/file1.self.css?body=1" 597 | assert_servable_asset_url "/assets/file2.self.css?body=1" 598 | end 599 | end 600 | 601 | def test_javascript_path 602 | super 603 | 604 | assert_equal "/assets/foo.js", @view.javascript_path("foo") 605 | assert_servable_asset_url "/assets/foo.js" 606 | end 607 | 608 | def test_stylesheet_path 609 | super 610 | 611 | assert_equal "/assets/foo.css", @view.stylesheet_path("foo") 612 | assert_servable_asset_url "/assets/foo.css" 613 | end 614 | end 615 | 616 | class DebugDigestHelperTest < NoHostHelperTest 617 | def setup 618 | super 619 | @view.debug_assets = true 620 | @view.digest_assets = true 621 | @assets.context_class.digest_assets = true 622 | end 623 | 624 | def test_javascript_include_tag 625 | super 626 | 627 | if using_sprockets4? 628 | assert_dom_equal %(), 629 | @view.javascript_include_tag(:foo) 630 | assert_dom_equal %(), 631 | @view.javascript_include_tag(:bar) 632 | assert_dom_equal %(\n), 633 | @view.javascript_include_tag(:file1, :file2) 634 | 635 | assert_servable_asset_url "/assets/foo.debug-#{@foo_debug_js_digest}.js" 636 | assert_servable_asset_url "/assets/bar.debug-#{@bar_debug_js_digest}.js" 637 | assert_servable_asset_url "/assets/dependency.debug-#{@dependency_debug_js_digest}.js" 638 | assert_servable_asset_url "/assets/file1.debug-#{@file1_debug_js_digest}.js" 639 | assert_servable_asset_url "/assets/file2.debug-#{@file2_debug_js_digest}.js" 640 | else 641 | assert_dom_equal %(), 642 | @view.javascript_include_tag(:foo) 643 | assert_dom_equal %(\n), 644 | @view.javascript_include_tag(:bar) 645 | assert_dom_equal %(\n\n), 646 | @view.javascript_include_tag(:file1, :file2) 647 | 648 | assert_servable_asset_url "/assets/foo.self-#{@foo_self_js_digest}.js?body=1" 649 | assert_servable_asset_url "/assets/bar.self-#{@bar_self_js_digest}.js?body=1" 650 | assert_servable_asset_url "/assets/dependency.self-#{@dependency_self_js_digest}.js?body=1" 651 | assert_servable_asset_url "/assets/file1.self-#{@file1_self_js_digest}.js?body=1" 652 | assert_servable_asset_url "/assets/file2.self-#{@file2_self_js_digest}.js?body=1" 653 | end 654 | end 655 | 656 | def test_stylesheet_link_tag 657 | super 658 | 659 | if using_sprockets4? 660 | assert_dom_equal %(), 661 | @view.stylesheet_link_tag(:foo) 662 | assert_dom_equal %(), 663 | @view.stylesheet_link_tag(:bar) 664 | assert_dom_equal %(\n), 665 | @view.stylesheet_link_tag(:file1, :file2) 666 | 667 | assert_servable_asset_url "/assets/foo.self-#{@foo_self_css_digest}.css" 668 | assert_servable_asset_url "/assets/bar.self-#{@bar_self_css_digest}.css" 669 | assert_servable_asset_url "/assets/dependency.self-#{@dependency_self_css_digest}.css" 670 | assert_servable_asset_url "/assets/file1.self-#{@file1_self_css_digest}.css" 671 | assert_servable_asset_url "/assets/file2.self-#{@file2_self_css_digest}.css" 672 | else 673 | assert_dom_equal %(), 674 | @view.stylesheet_link_tag(:foo) 675 | assert_dom_equal %(\n), 676 | @view.stylesheet_link_tag(:bar) 677 | assert_dom_equal %(\n\n), 678 | @view.stylesheet_link_tag(:file1, :file2) 679 | 680 | assert_servable_asset_url "/assets/foo.self-#{@foo_self_css_digest}.css?body=1" 681 | assert_servable_asset_url "/assets/bar.self-#{@bar_self_css_digest}.css?body=1" 682 | assert_servable_asset_url "/assets/dependency.self-#{@dependency_self_css_digest}.css?body=1" 683 | assert_servable_asset_url "/assets/file1.self-#{@file1_self_css_digest}.css?body=1" 684 | assert_servable_asset_url "/assets/file2.self-#{@file2_self_css_digest}.css?body=1" 685 | end 686 | end 687 | 688 | def test_javascript_path 689 | super 690 | 691 | assert_equal "/assets/foo-#{@foo_js_digest}.js", @view.javascript_path("foo") 692 | assert_servable_asset_url "/assets/foo-#{@foo_js_digest}.js" 693 | end 694 | 695 | def test_stylesheet_path 696 | super 697 | 698 | assert_equal "/assets/foo-#{@foo_css_digest}.css", @view.stylesheet_path("foo") 699 | assert_servable_asset_url "/assets/foo-#{@foo_css_digest}.css" 700 | end 701 | 702 | def test_asset_digest_path 703 | assert_equal "foo-#{@foo_js_digest}.js", @view.asset_digest_path("foo.js") 704 | assert_equal "foo-#{@foo_css_digest}.css", @view.asset_digest_path("foo.css") 705 | end 706 | 707 | def test_asset_url 708 | assert_equal "var url = '/assets/foo-#{@foo_js_digest}.js';\n", @assets["url.js"].to_s 709 | assert_equal "p { background: url(/assets/logo-#{@logo_digest}.png); }\n", @assets["url.css"].to_s 710 | end 711 | end 712 | 713 | class ManifestHelperTest < NoHostHelperTest 714 | def setup 715 | super 716 | 717 | @manifest = Sprockets::Manifest.new(@assets, FIXTURES_PATH) 718 | @manifest.assets["foo.js"] = "foo-#{@foo_js_digest}.js" 719 | @manifest.assets["foo.css"] = "foo-#{@foo_css_digest}.css" 720 | 721 | @manifest.files["foo-#{@foo_js_digest}.js"] = { "integrity" => @foo_js_integrity } 722 | @manifest.files["foo-#{@foo_css_digest}.css"] = { "integrity" => @foo_css_integrity } 723 | 724 | @view.digest_assets = true 725 | @view.assets_environment = nil 726 | @view.assets_manifest = @manifest 727 | @view.resolve_assets_with = [ :manifest ] 728 | end 729 | 730 | def test_javascript_include_tag 731 | super 732 | 733 | assert_dom_equal %(), 734 | @view.javascript_include_tag("foo") 735 | assert_dom_equal %(), 736 | @view.javascript_include_tag("foo.js") 737 | assert_dom_equal %(), 738 | @view.javascript_include_tag(:foo) 739 | end 740 | 741 | def test_stylesheet_link_tag 742 | super 743 | 744 | assert_dom_equal %(), 745 | @view.stylesheet_link_tag("foo") 746 | assert_dom_equal %(), 747 | @view.stylesheet_link_tag("foo.css") 748 | assert_dom_equal %(), 749 | @view.stylesheet_link_tag(:foo) 750 | end 751 | 752 | def test_javascript_include_tag_integrity 753 | super 754 | 755 | assert_dom_equal %(), 756 | @view.javascript_include_tag("foo", integrity: true) 757 | assert_dom_equal %(), 758 | @view.javascript_include_tag("foo.js", integrity: true) 759 | assert_dom_equal %(), 760 | @view.javascript_include_tag(:foo, integrity: true) 761 | end 762 | 763 | def test_stylesheet_link_tag_integrity 764 | super 765 | 766 | assert_dom_equal %(), 767 | @view.stylesheet_link_tag("foo", integrity: true) 768 | assert_dom_equal %(), 769 | @view.stylesheet_link_tag("foo.css", integrity: true) 770 | assert_dom_equal %(), 771 | @view.stylesheet_link_tag(:foo, integrity: true) 772 | end 773 | 774 | def test_javascript_path 775 | super 776 | 777 | assert_equal "/assets/foo-#{@foo_js_digest}.js", @view.javascript_path("foo") 778 | end 779 | 780 | def test_stylesheet_path 781 | super 782 | 783 | assert_equal "/assets/foo-#{@foo_css_digest}.css", @view.stylesheet_path("foo") 784 | end 785 | 786 | def test_asset_digest_path 787 | assert_equal "foo-#{@foo_js_digest}.js", @view.asset_digest_path("foo.js") 788 | assert_equal "foo-#{@foo_css_digest}.css", @view.asset_digest_path("foo.css") 789 | end 790 | 791 | def test_assets_environment_unavailable 792 | refute @view.assets_environment 793 | end 794 | end 795 | 796 | class DebugManifestHelperTest < ManifestHelperTest 797 | def setup 798 | super 799 | 800 | @view.debug_assets = true 801 | end 802 | 803 | def test_javascript_include_tag_integrity 804 | pass 805 | end 806 | 807 | def test_stylesheet_link_tag_integrity 808 | pass 809 | end 810 | end 811 | 812 | class AssetResolverOrderingTest < HelperTest 813 | def setup 814 | super 815 | 816 | @view.digest_assets = true 817 | 818 | @view.assets_manifest = Sprockets::Manifest.new(@assets, FIXTURES_PATH).tap do |stale| 819 | stale.assets["foo.js"] = "foo-stale.js" 820 | stale.files["foo-stale.js"] = { "integrity" => "stale-manifest" } 821 | 822 | stale.assets["foo.css"] = "foo-stale.css" 823 | stale.files["foo-stale.css"] = { "integrity" => "stale-manifest" } 824 | end 825 | end 826 | 827 | def test_digest_prefers_asset_environment_over_manifest 828 | @view.resolve_assets_with = [ :environment, :manifest ] 829 | 830 | assert_equal "foo-#{@foo_js_digest}.js", @view.asset_digest_path("foo.js") 831 | assert_equal "foo-#{@foo_css_digest}.css", @view.asset_digest_path("foo.css") 832 | 833 | assert_equal @foo_js_integrity, @view.asset_integrity("foo.js") 834 | assert_equal @foo_css_integrity, @view.asset_integrity("foo.css") 835 | end 836 | 837 | def test_try_resolvers_until_first_result 838 | @view.resolve_assets_with = [ :manifest, :environment ] 839 | 840 | assert_equal 'foo-stale.js', @view.asset_digest_path('foo.js') 841 | assert_equal "bar-#{@bar_js_digest}.js", @view.asset_digest_path('bar.js') 842 | assert_nil @view.asset_digest_path('nonexistent') 843 | 844 | assert_equal 'stale-manifest', @view.asset_integrity('foo.js') 845 | assert_equal @bar_js_integrity, @view.asset_integrity('bar.js') 846 | assert_nil @view.asset_integrity('nonexistent') 847 | end 848 | 849 | def test_obeys_asset_resolver_order 850 | @view.resolve_assets_with = [] 851 | assert_nil @view.asset_digest_path('foo.js') 852 | assert_nil @view.asset_integrity('foo.js') 853 | end 854 | end 855 | 856 | class AssetUrlHelperLinksTarget < HelperTest 857 | def test_precompile_allows_links 858 | @view.assets_precompile = ["url.css"] 859 | precompiled_assets = @manifest.find(@view.assets_precompile).map(&:logical_path) 860 | @view.precompiled_asset_checker = -> logical_path { precompiled_assets.include? logical_path } 861 | assert @view.asset_path("url.css") 862 | assert @view.asset_path("logo.png") 863 | 864 | assert_raises(Sprockets::Rails::Helper::AssetNotPrecompiled) do 865 | @view.asset_path("foo.css") 866 | end 867 | end 868 | 869 | def test_links_image_target 870 | assert_match "logo.png", @assets['url.css'].links.to_a[0] 871 | end 872 | 873 | def test_doesnt_track_public_assets 874 | refute_match "does_not_exist.png", @assets['error/missing.css'].links.to_a[0] 875 | end 876 | 877 | def test_asset_environment_reference_is_cached 878 | env = @view.assets_environment 879 | assert_kind_of Sprockets::CachedEnvironment, env 880 | assert @view.assets_environment.equal?(env), "view didn't return the same cached instance" 881 | end 882 | end 883 | 884 | class PrecompiledAssetHelperTest < HelperTest 885 | def setup 886 | super 887 | @bundle_js_name = '/assets/bundle.js' 888 | end 889 | 890 | # both subclass and more specific error are supported due to 891 | # https://github.com/rails/sprockets-rails/pull/414/commits/760a805a9f56d3df0d4b83bd4a5a6476eb3aeb29 892 | def test_javascript_precompile 893 | assert_raises(Sprockets::Rails::Helper::AssetNotPrecompiled) do 894 | @view.javascript_include_tag("not_precompiled") 895 | end 896 | end 897 | 898 | def test_javascript_precompile_throws_the_descriptive_error 899 | assert_raises(Sprockets::Rails::Helper::AssetNotPrecompiledError) do 900 | @view.javascript_include_tag("not_precompiled") 901 | end 902 | end 903 | 904 | def test_stylesheet_precompile 905 | assert_raises(Sprockets::Rails::Helper::AssetNotPrecompiled) do 906 | @view.stylesheet_link_tag("not_precompiled") 907 | end 908 | end 909 | 910 | def test_index_files 911 | assert_dom_equal %(), 912 | @view.javascript_include_tag("bundle") 913 | end 914 | end 915 | 916 | class DeprecationTest < HelperTest 917 | def test_deprecations_for_asset_path 918 | @view.send(:define_singleton_method, :public_compute_asset_path, -> {}) 919 | assert_deprecated("use the `skip_pipeline: true` option", Sprockets::Rails.deprecator) do 920 | @view.asset_path("does_not_exist.noextension") 921 | end 922 | ensure 923 | @view.instance_eval('undef :public_compute_asset_path') 924 | end 925 | 926 | def test_deprecations_for_asset_url 927 | @view.send(:define_singleton_method, :public_compute_asset_path, -> {}) 928 | 929 | assert_deprecated("use the `skip_pipeline: true` option", Sprockets::Rails.deprecator) do 930 | @view.asset_url("does_not_exist.noextension") 931 | end 932 | ensure 933 | @view.instance_eval('undef :public_compute_asset_path') 934 | end 935 | 936 | def test_deprecations_for_image_tag 937 | @view.send(:define_singleton_method, :public_compute_asset_path, -> {}) 938 | 939 | assert_deprecated("use the `skip_pipeline: true` option", Sprockets::Rails.deprecator) do 940 | @view.image_tag("does_not_exist.noextension") 941 | end 942 | ensure 943 | @view.instance_eval('undef :public_compute_asset_path') 944 | end 945 | end 946 | 947 | class RaiseUnlessPrecompiledAssetDisabledTest < HelperTest 948 | def test_check_precompiled_asset_enabled 949 | @view.check_precompiled_asset = true 950 | assert_raises(Sprockets::Rails::Helper::AssetNotPrecompiled) do 951 | @view.asset_path("not_precompiled.css") 952 | end 953 | end 954 | 955 | def test_check_precompiled_asset_disabled 956 | @view.check_precompiled_asset = false 957 | assert @view.asset_path("not_precompiled.css") 958 | end 959 | end 960 | 961 | class PrecompiledDebugAssetHelperTest < PrecompiledAssetHelperTest 962 | 963 | # Re-run all PrecompiledAssetHelperTest with a different setup 964 | def setup 965 | super 966 | @view.debug_assets = true 967 | if using_sprockets4? 968 | @bundle_js_name = '/assets/bundle.debug.js' 969 | else 970 | @bundle_js_name = '/assets/bundle/index.self.js?body=1' 971 | end 972 | end 973 | end 974 | -------------------------------------------------------------------------------- /test/test_quiet_assets.rb: -------------------------------------------------------------------------------- 1 | require 'active_support' 2 | require 'active_support/testing/isolation' 3 | require 'active_support/log_subscriber/test_helper' 4 | require 'minitest/autorun' 5 | 6 | require 'sprockets/railtie' 7 | require 'rails' 8 | 9 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 10 | 11 | class TestQuietAssets < Minitest::Test 12 | include ActiveSupport::Testing::Isolation 13 | 14 | ROOT_PATH = Pathname.new(File.expand_path("../../tmp/app", __FILE__)) 15 | ASSET_PATH = ROOT_PATH.join("app","assets", "config") 16 | 17 | def setup 18 | FileUtils.mkdir_p(ROOT_PATH) 19 | Dir.chdir(ROOT_PATH) 20 | 21 | @app = Class.new(Rails::Application) 22 | @app.config.eager_load = false 23 | @app.config.logger = ActiveSupport::Logger.new("/dev/null") 24 | @app.config.active_support.to_time_preserves_timezone = :zone 25 | 26 | FileUtils.mkdir_p(ASSET_PATH) 27 | File.open(ASSET_PATH.join("manifest.js"), "w") { |f| f << "" } 28 | 29 | @app.initialize! 30 | 31 | Rails.logger.level = Logger::DEBUG 32 | end 33 | 34 | def test_silences_with_default_prefix 35 | assert_equal Logger::ERROR, middleware.call("PATH_INFO" => "/assets/stylesheets/application.css") 36 | end 37 | 38 | def test_silences_with_custom_prefix 39 | Rails.application.config.assets.prefix = "path/to" 40 | assert_equal Logger::ERROR, middleware.call("PATH_INFO" => "/path/to/thing") 41 | end 42 | 43 | def test_does_not_silence_without_match 44 | assert_equal Logger::DEBUG, middleware.call("PATH_INFO" => "/path/to/thing") 45 | end 46 | 47 | def test_logger_does_not_respond_to_silence 48 | ::Rails.logger.stub :respond_to?, false do 49 | assert_raises(Sprockets::Rails::LoggerSilenceError) { middleware.call("PATH_INFO" => "/assets/stylesheets/application.css") } 50 | end 51 | end 52 | 53 | private 54 | 55 | def middleware 56 | @middleware ||= Sprockets::Rails::QuietAssets.new(->(env) { Rails.logger.level }) 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /test/test_railtie.rb: -------------------------------------------------------------------------------- 1 | require 'active_support' 2 | require 'active_support/testing/isolation' 3 | require 'minitest/autorun' 4 | 5 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 6 | 7 | def silence_stderr 8 | orig_stderr = $stderr.clone 9 | $stderr.reopen File.new('/dev/null', 'w') 10 | yield 11 | ensure 12 | $stderr.reopen orig_stderr 13 | end 14 | 15 | class TestBoot < Minitest::Test 16 | include ActiveSupport::Testing::Isolation 17 | 18 | ROOT = File.expand_path("../../tmp/app", __FILE__) 19 | FIXTURES_PATH = File.expand_path("../fixtures", __FILE__) 20 | 21 | attr_reader :app 22 | 23 | def setup 24 | require 'rails' 25 | # Can't seem to get initialize to run w/o this 26 | require 'action_controller/railtie' 27 | require 'active_support/dependencies' 28 | require 'tzinfo' 29 | 30 | ENV['RAILS_ENV'] = 'test' 31 | 32 | FileUtils.mkdir_p ROOT 33 | Dir.chdir ROOT 34 | 35 | @app = Class.new(Rails::Application) 36 | @app.config.eager_load = false 37 | @app.config.time_zone = 'UTC' 38 | @app.config.middleware ||= Rails::Configuration::MiddlewareStackProxy.new 39 | @app.config.active_support.deprecation = :notify 40 | 41 | Dir.chdir(app.root) do 42 | dir = "app/assets/config" 43 | FileUtils.mkdir_p(dir) 44 | File.open("#{ dir }/manifest.js", "w") do |f| 45 | f << "" 46 | end 47 | end 48 | end 49 | 50 | def test_initialize 51 | app.initialize! 52 | end 53 | end 54 | 55 | class TestRailtie < TestBoot 56 | def setup 57 | require 'sprockets/railtie' 58 | super 59 | 60 | # sprockets-4.0.0.beta8 does not like 'rake assets:clobber' when this directory does not exist 61 | Dir.chdir(app.root) do 62 | dir = "tmp/cache/assets/sprockets" 63 | FileUtils.mkdir_p(dir) 64 | end 65 | end 66 | 67 | def test_defaults_to_compile_assets_with_env_and_manifest_available 68 | assert_equal true, app.config.assets.compile 69 | 70 | app.initialize! 71 | 72 | # Env is available 73 | refute_nil env = app.assets 74 | assert_kind_of Sprockets::Environment, env 75 | 76 | # Manifest is always available 77 | assert manifest = app.assets_manifest 78 | assert_equal app.assets, manifest.environment 79 | assert_equal File.join(ROOT, "public/assets"), manifest.dir 80 | 81 | # Resolves against manifest then environment by default 82 | assert_equal [ :manifest, :environment ], app.config.assets.resolve_with 83 | 84 | # Sprockets config 85 | assert_equal ROOT, env.root 86 | assert_equal "", env.version 87 | assert env.cache 88 | assert_includes(env.paths, "#{ROOT}/app/assets/config") 89 | 90 | assert_nil env.js_compressor 91 | assert_nil env.css_compressor 92 | end 93 | 94 | def test_disabling_compile_has_manifest_but_no_env 95 | app.configure do 96 | config.assets.compile = false 97 | end 98 | 99 | assert_equal false, app.config.assets.compile 100 | 101 | app.initialize! 102 | 103 | # No env when compile is disabled 104 | assert_nil app.assets 105 | 106 | # Manifest is always available 107 | refute_nil manifest = app.assets_manifest 108 | assert_nil manifest.environment 109 | assert_equal File.join(ROOT, "public/assets"), manifest.dir 110 | 111 | # Resolves against manifest only 112 | assert_equal [ :manifest ], app.config.assets.resolve_with 113 | end 114 | 115 | def test_enabling_debug_resolves_with_env_only 116 | app.configure do 117 | config.assets.debug = true 118 | end 119 | 120 | assert_equal true, app.config.assets.debug 121 | assert_equal true, app.config.assets.compile 122 | 123 | app.initialize! 124 | 125 | # Resolves against environment only 126 | assert_equal [ :environment ], app.config.assets.resolve_with 127 | end 128 | 129 | def test_copies_paths 130 | app.configure do 131 | config.assets.paths << "javascripts" 132 | config.assets.paths << "stylesheets" 133 | end 134 | app.initialize! 135 | 136 | assert env = app.assets 137 | assert_includes(env.paths, "#{ROOT}/javascripts") 138 | assert_includes(env.paths, "#{ROOT}/stylesheets") 139 | assert_includes(env.paths, "#{ROOT}/app/assets/config") 140 | end 141 | 142 | def test_compressors 143 | app.configure do 144 | config.assets.js_compressor = :uglifier 145 | config.assets.css_compressor = :sass 146 | end 147 | app.initialize! 148 | 149 | assert env = app.assets 150 | assert_equal Sprockets::UglifierCompressor.name, env.js_compressor.name 151 | 152 | silence_warnings do 153 | require 'sprockets/sass_compressor' 154 | end 155 | assert_equal Sprockets::SassCompressor.name, env.css_compressor.name 156 | end 157 | 158 | def test_custom_compressors 159 | compressor = Class.new do 160 | def self.call(input) 161 | { data: input[:data] } 162 | end 163 | end 164 | 165 | app.configure do 166 | config.assets.configure do |env| 167 | env.register_compressor "application/javascript", :test_js, compressor 168 | env.register_compressor "text/css", :test_css, compressor 169 | end 170 | config.assets.js_compressor = :test_js 171 | config.assets.css_compressor = :test_css 172 | end 173 | app.initialize! 174 | 175 | assert env = app.assets 176 | assert_equal compressor, env.js_compressor 177 | assert_equal compressor, env.css_compressor 178 | end 179 | 180 | def test_default_gzip_config 181 | app.initialize! 182 | 183 | assert env = app.assets 184 | assert_equal true, env.gzip? 185 | end 186 | 187 | def test_gzip_config 188 | app.configure do 189 | config.assets.gzip = false 190 | end 191 | app.initialize! 192 | 193 | assert env = app.assets 194 | assert_equal false, env.gzip? 195 | end 196 | 197 | def test_default_check_precompiled_assets 198 | assert app.config.assets.check_precompiled_asset 199 | app.initialize! 200 | @view = action_view 201 | assert @view.check_precompiled_asset 202 | end 203 | 204 | def test_configure_check_precompiled_assets 205 | app.configure do 206 | config.assets.check_precompiled_asset = false 207 | end 208 | app.initialize! 209 | @view = action_view 210 | refute @view.check_precompiled_asset 211 | end 212 | 213 | def test_version 214 | app.configure do 215 | config.assets.version = 'v2' 216 | end 217 | app.initialize! 218 | 219 | assert env = app.assets 220 | assert_equal "v2", env.version 221 | end 222 | 223 | def test_configure 224 | app.configure do 225 | config.assets.configure do |env| 226 | env.append_path "javascripts" 227 | end 228 | config.assets.configure do |env| 229 | env.append_path "stylesheets" 230 | end 231 | end 232 | app.initialize! 233 | 234 | assert env = app.assets 235 | 236 | assert_includes(env.paths, "#{ROOT}/javascripts") 237 | assert_includes(env.paths, "#{ROOT}/stylesheets") 238 | assert_includes(env.paths, "#{ROOT}/app/assets/config") 239 | end 240 | 241 | def test_environment_is_frozen_if_caching_classes 242 | app.configure do 243 | config.cache_classes = true 244 | end 245 | app.initialize! 246 | 247 | assert env = app.assets 248 | assert_kind_of Sprockets::CachedEnvironment, env 249 | end 250 | 251 | def test_action_view_helper 252 | app.configure do 253 | config.assets.paths << FIXTURES_PATH 254 | config.assets.precompile += ["foo.js"] 255 | end 256 | app.initialize! 257 | 258 | assert app.assets.paths.include?(FIXTURES_PATH) 259 | 260 | assert_equal false, ActionView::Base.debug_assets 261 | assert_equal true, ActionView::Base.digest_assets 262 | assert_equal "/assets", ActionView::Base.assets_prefix 263 | assert_equal app.assets, ActionView::Base.assets_environment 264 | assert_equal app.assets_manifest, ActionView::Base.assets_manifest 265 | assert_kind_of Sprockets::Environment, ActionView::Base.assets_environment 266 | 267 | @view = action_view 268 | assert_equal "/javascripts/xmlhr.js", @view.javascript_path("xmlhr") 269 | assert_equal "/assets/foo-4ef5541f349f7ed5a0d6b71f2fa4c82745ca106ae02f212aea5129726ac6f6ab.js", @view.javascript_path("foo") 270 | 271 | env = @view.assets_environment 272 | assert_kind_of Sprockets::CachedEnvironment, env 273 | assert @view.assets_environment.equal?(env), "view didn't return the same cached instance" 274 | end 275 | 276 | def test_action_view_helper_when_no_compile 277 | app.configure do 278 | config.assets.compile = false 279 | end 280 | 281 | assert_equal false, app.config.assets.compile 282 | 283 | app.initialize! 284 | 285 | refute ActionView::Base.assets_environment 286 | assert_equal app.assets_manifest, ActionView::Base.assets_manifest 287 | 288 | @view = action_view 289 | refute @view.assets_environment 290 | assert_equal app.assets_manifest, @view.assets_manifest 291 | end 292 | 293 | def test_sprockets_context_helper 294 | app.initialize! 295 | 296 | assert env = app.assets 297 | assert_equal "/assets", env.context_class.assets_prefix 298 | assert_equal true, env.context_class.digest_assets 299 | assert_nil env.context_class.config.asset_host 300 | end 301 | 302 | def test_manifest_path 303 | app.configure do 304 | config.assets.manifest = Rails.root.join('config','foo','bar.json') 305 | end 306 | app.initialize! 307 | 308 | assert manifest = app.assets_manifest 309 | assert_match %r{config/foo/bar\.json$}, manifest.path 310 | assert_match %r{public/assets$}, manifest.dir 311 | end 312 | 313 | def test_manifest_path_respects_rails_public_path 314 | app.configure do 315 | config.paths['public'] = 'test_public' 316 | end 317 | app.initialize! 318 | 319 | assert manifest = app.assets_manifest 320 | assert_match %r{test_public/assets/\.sprockets-manifest-.*\.json$}, manifest.path 321 | assert_match %r{test_public/assets$}, manifest.dir 322 | end 323 | 324 | def test_load_tasks 325 | app.initialize! 326 | app.load_tasks 327 | 328 | assert Rake.application['assets:environment'] 329 | assert Rake.application['assets:precompile'] 330 | assert Rake.application['assets:clean'] 331 | assert Rake.application['assets:clobber'] 332 | end 333 | 334 | def test_task_precompile 335 | app.configure do 336 | config.assets.paths << FIXTURES_PATH 337 | config.assets.precompile += ["foo.js", "url.css"] 338 | end 339 | app.initialize! 340 | app.load_tasks 341 | 342 | path = "#{app.assets_manifest.dir}/foo-#{Rails.application.assets['foo.js'].etag}.js" 343 | 344 | silence_stderr do 345 | Rake.application['assets:clobber'].execute 346 | end 347 | refute File.exist?(path) 348 | 349 | silence_stderr do 350 | Rake.application['assets:precompile'].execute 351 | end 352 | assert File.exist?(path) 353 | url_css_path = File.join(app.assets_manifest.dir, Rails.application.assets['url.css'].digest_path) 354 | assert_match(%r{/assets/logo-#{Rails.application.assets['logo.png'].etag}.png}, File.read(url_css_path)) 355 | 356 | silence_stderr do 357 | Rake.application['assets:clobber'].execute 358 | end 359 | refute File.exist?(path) 360 | end 361 | 362 | def test_task_precompile_compile_false 363 | app.configure do 364 | config.assets.compile = false 365 | config.assets.paths << FIXTURES_PATH 366 | config.assets.precompile += ["foo.js"] 367 | end 368 | app.initialize! 369 | app.load_tasks 370 | 371 | path = "#{app.assets_manifest.dir}/foo-4ef5541f349f7ed5a0d6b71f2fa4c82745ca106ae02f212aea5129726ac6f6ab.js" 372 | 373 | silence_stderr do 374 | Rake.application['assets:clobber'].execute 375 | end 376 | refute File.exist?(path) 377 | 378 | silence_stderr do 379 | Rake.application['assets:precompile'].execute 380 | end 381 | assert File.exist?(path) 382 | 383 | silence_stderr do 384 | Rake.application['assets:clobber'].execute 385 | end 386 | refute File.exist?(path) 387 | end 388 | 389 | def test_direct_build_environment_call 390 | app.configure do 391 | config.assets.paths << "javascripts" 392 | config.assets.paths << "stylesheets" 393 | end 394 | app.initialize! 395 | 396 | assert env = Sprockets::Railtie.build_environment(app) 397 | assert_kind_of Sprockets::Environment, env 398 | 399 | assert_equal ROOT, env.root 400 | assert_includes(env.paths, "#{ROOT}/javascripts") 401 | assert_includes(env.paths, "#{ROOT}/stylesheets") 402 | assert_includes(env.paths, "#{ROOT}/app/assets/config") 403 | end 404 | 405 | def test_quiet_assets_defaults_to_off 406 | app.initialize! 407 | app.load_tasks 408 | 409 | assert_equal false, app.config.assets.quiet 410 | refute app.config.middleware.include?(Sprockets::Rails::QuietAssets) 411 | end 412 | 413 | def test_quiet_assets_inserts_middleware 414 | app.configure do 415 | config.assets.quiet = true 416 | end 417 | app.initialize! 418 | app.load_tasks 419 | middleware = app.config.middleware 420 | 421 | assert_equal true, app.config.assets.quiet 422 | assert middleware.include?(Sprockets::Rails::QuietAssets) 423 | assert middleware.each_cons(2).include?([Sprockets::Rails::QuietAssets, Rails::Rack::Logger]) 424 | end 425 | 426 | def test_resolve_assets_in_css_urls_defaults_to_true 427 | app.initialize! 428 | 429 | assert_equal true, app.config.assets.resolve_assets_in_css_urls 430 | assert_includes Sprockets.postprocessors['text/css'], Sprockets::Rails::AssetUrlProcessor 431 | end 432 | 433 | def test_resolve_assets_in_css_urls_when_false_avoids_registering_postprocessor 434 | app.configure do 435 | config.assets.resolve_assets_in_css_urls = false 436 | end 437 | app.initialize! 438 | 439 | assert_equal false, app.config.assets.resolve_assets_in_css_urls 440 | refute_includes Sprockets.postprocessors['text/css'], Sprockets::Rails::AssetUrlProcessor 441 | end 442 | 443 | private 444 | def action_view 445 | ActionView::Base.new(ActionView::LookupContext.new([]), {}, nil) 446 | end 447 | end 448 | -------------------------------------------------------------------------------- /test/test_sourcemapping_url_processor.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'sprockets/railtie' 3 | 4 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 5 | class TestSourceMappingUrlProcessor < Minitest::Test 6 | def setup 7 | @env = Sprockets::Environment.new 8 | end 9 | 10 | def test_successful 11 | @env.context_class.class_eval do 12 | def resolve(path, **kargs) 13 | "/assets/mapped.js.map" 14 | end 15 | 16 | def asset_path(path, options = {}) 17 | "/assets/mapped-HEXGOESHERE.js.map" 18 | end 19 | end 20 | 21 | input = { environment: @env, data: "var mapped;\n//# sourceMappingURL=mapped.js.map", name: 'mapped', filename: 'mapped.js', metadata: {} } 22 | output = Sprockets::Rails::SourcemappingUrlProcessor.call(input) 23 | assert_equal({ data: "var mapped;\n//# sourceMappingURL=/assets/mapped-HEXGOESHERE.js.map\n//!\n" }, output) 24 | end 25 | 26 | def test_resolving_erroneously_without_map_extension 27 | @env.context_class.class_eval do 28 | def resolve(path, **kargs) 29 | "/assets/mapped.js" 30 | end 31 | end 32 | 33 | input = { environment: @env, data: "var mapped;\n//# sourceMappingURL=mapped.js.map", name: 'mapped', filename: 'mapped.js', metadata: {} } 34 | output = Sprockets::Rails::SourcemappingUrlProcessor.call(input) 35 | assert_equal({ data: "var mapped;\n" }, output) 36 | end 37 | 38 | def test_missing 39 | @env.context_class.class_eval do 40 | def resolve(path, **kargs) 41 | raise Sprockets::FileNotFound 42 | end 43 | end 44 | 45 | input = { environment: @env, data: "var mapped;\n//# sourceMappingURL=mappedNOT.js.map", name: 'mapped', filename: 'mapped.js', metadata: {} } 46 | output = Sprockets::Rails::SourcemappingUrlProcessor.call(input) 47 | assert_equal({ data: "var mapped;\n" }, output) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/test_task.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'tmpdir' 3 | 4 | require 'sprockets' 5 | require 'sprockets/rails/task' 6 | 7 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 8 | 9 | class TestTask < Minitest::Test 10 | FIXTURES_PATH = File.expand_path("../fixtures", __FILE__) 11 | 12 | def setup 13 | @rake = Rake::Application.new 14 | Rake.application = @rake 15 | 16 | @assets = Sprockets::Environment.new 17 | @assets.append_path FIXTURES_PATH 18 | 19 | @dir = File.join(Dir::tmpdir, 'rails', 'task') 20 | 21 | @manifest_file = File.join(Dir::tmpdir, 'rails', 'manifest', 'custom-manifest.json') 22 | FileUtils.mkdir_p File.dirname(@manifest_file) 23 | @manifest = Sprockets::Manifest.new(@assets, @dir, @manifest_file) 24 | 25 | Sprockets::Rails::Task.new do |t| 26 | t.environment = @assets 27 | t.manifest = @manifest 28 | t.assets = ['foo.js', 'foo-modified.js'] 29 | t.log_level = :fatal 30 | end 31 | 32 | @environment_ran = false 33 | # Stub Rails environment task 34 | @rake.define_task Rake::Task, :environment do 35 | @environment_ran = true 36 | end 37 | end 38 | 39 | def teardown 40 | Rake.application = nil 41 | 42 | FileUtils.rm_rf(@dir) 43 | assert Dir["#{@dir}/*"].empty? 44 | 45 | manifest_dir = File.dirname(@manifest_file) 46 | FileUtils.rm_rf(manifest_dir) 47 | assert Dir["#{manifest_dir}/*"].empty? 48 | end 49 | 50 | def test_precompile 51 | assert !@environment_ran 52 | 53 | digest_path = @assets['foo.js'].digest_path 54 | assert !File.exist?("#{@dir}/#{digest_path}") 55 | 56 | @rake['assets:precompile'].invoke 57 | 58 | assert @environment_ran 59 | assert File.exist?(@manifest_file) 60 | assert File.exist?("#{@dir}/#{digest_path}") 61 | end 62 | 63 | def test_precompile_without_manifest 64 | Sprockets::Rails::Task.new do |t| 65 | t.environment = @assets 66 | t.manifest = Sprockets::Manifest.new(@assets, @dir, nil) 67 | t.assets = ['foo.js', 'foo-modified.js'] 68 | t.log_level = :fatal 69 | end 70 | 71 | assert !@environment_ran 72 | 73 | digest_path = @assets['foo.js'].digest_path 74 | assert !File.exist?("#{@dir}/#{digest_path}") 75 | 76 | @rake['assets:precompile'].invoke 77 | 78 | assert @environment_ran 79 | assert Dir["#{@dir}/.sprockets-manifest-*.json"].first 80 | assert File.exist?("#{@dir}/#{digest_path}") 81 | end 82 | 83 | def test_clobber 84 | assert !@environment_ran 85 | digest_path = @assets['foo.js'].digest_path 86 | 87 | @rake['assets:precompile'].invoke 88 | assert File.exist?("#{@dir}/#{digest_path}") 89 | 90 | assert @environment_ran 91 | 92 | @rake['assets:clobber'].invoke 93 | assert !File.exist?("#{@dir}/#{digest_path}") 94 | end 95 | 96 | def test_clean 97 | assert !@environment_ran 98 | digest_path = @assets['foo.js'].digest_path 99 | 100 | @rake['assets:precompile'].invoke 101 | assert File.exist?("#{@dir}/#{digest_path}") 102 | 103 | assert @environment_ran 104 | 105 | @rake['assets:clean'].invoke 106 | assert File.exist?("#{@dir}/#{digest_path}") 107 | end 108 | 109 | def test_clean_with_keep_specified 110 | assert !@environment_ran 111 | path = Pathname.new(@assets['foo.js'].filename) 112 | new_path = path.join("../foo-modified.js") 113 | 114 | FileUtils.cp(path, new_path) 115 | 116 | assert File.exist?(new_path) 117 | digest_path = @assets['foo-modified.js'].digest_path 118 | 119 | @rake['assets:precompile'].invoke 120 | assert File.exist?("#{@dir}/#{digest_path}") 121 | assert @environment_ran 122 | 123 | # clean environment 124 | setup 125 | 126 | # modify file 127 | File.open(new_path, "a") {|f| f.write("var Bar;") } 128 | @rake['assets:precompile'].invoke 129 | old_digest_path = digest_path 130 | digest_path = @assets['foo-modified.js'].digest_path 131 | 132 | refute_equal old_digest_path, digest_path 133 | assert File.exist?("#{@dir}/#{old_digest_path}") 134 | assert File.exist?("#{@dir}/#{digest_path}") 135 | 136 | @rake['assets:clean'].invoke(0) 137 | assert File.exist?("#{@dir}/#{digest_path}") 138 | # refute File.exist?("#{@dir}/#{old_digest_path}") 139 | ensure 140 | FileUtils.rm(new_path) if new_path 141 | end 142 | end 143 | --------------------------------------------------------------------------------