pax_global_header00006660000000000000000000000064144757214040014522gustar00rootroot0000000000000052 comment=5b040f3bec503ded8a98c0c911c7a77dd1f5bf1b sprockets-4.2.1/000077500000000000000000000000001447572140400135435ustar00rootroot00000000000000sprockets-4.2.1/.devcontainer/000077500000000000000000000000001447572140400163025ustar00rootroot00000000000000sprockets-4.2.1/.devcontainer/Dockerfile000066400000000000000000000022061447572140400202740ustar00rootroot00000000000000# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/ruby/.devcontainer/base.Dockerfile # [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.1, 3.0, 2, 2.7, 3-bullseye, 3.1-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 3-buster, 3.1-buster, 3.0-buster, 2-buster, 2.7-buster ARG VARIANT="3.1-bullseye" FROM mcr.microsoft.com/vscode/devcontainers/ruby:0-${VARIANT} # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 ARG NODE_VERSION="none" RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi # [Optional] Uncomment this section to install additional OS packages. # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ # && apt-get -y install --no-install-recommends # [Optional] Uncomment this line to install additional gems. # RUN gem install # [Optional] Uncomment this line to install global node packages. # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1sprockets-4.2.1/.devcontainer/devcontainer.json000066400000000000000000000022471447572140400216630ustar00rootroot00000000000000// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/ruby { "name": "Ruby", "build": { "dockerfile": "Dockerfile", "args": { // Update 'VARIANT' to pick a Ruby version: 3, 3.1, 3.0, 2, 2.7 // Append -bullseye or -buster to pin to an OS version. // Use -bullseye variants on local on arm64/Apple Silicon. "VARIANT": "3-bullseye", // Options "NODE_VERSION": "lts/*" } }, // Configure tool-specific properties. "customizations": { // Configure properties specific to VS Code. "vscode": { // Add the IDs of extensions you want installed when the container is created. "extensions": [ "Shopify.ruby-lsp" ] } }, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "ruby --version", // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode", "features": { "github-cli": "latest" } } sprockets-4.2.1/.gitattributes000066400000000000000000000000701447572140400164330ustar00rootroot00000000000000test/fixtures/**/*.png binary test/fixtures/**/* eol=lf sprockets-4.2.1/.github/000077500000000000000000000000001447572140400151035ustar00rootroot00000000000000sprockets-4.2.1/.github/workflows/000077500000000000000000000000001447572140400171405ustar00rootroot00000000000000sprockets-4.2.1/.github/workflows/ci.yml000066400000000000000000000015701447572140400202610ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: tests: runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} name: ${{ matrix.ruby }} strategy: matrix: experimental: [false] ruby: - "2.5" - "2.6" - "2.7" - "3.0" - "3.1" - "3.2" include: - { ruby: jruby, experimental: true } - { ruby: jruby-head, experimental: true } - { ruby: truffleruby, experimental: true } - { ruby: truffleruby-head, experimental: true } - { ruby: head, experimental: true } steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run tests timeout-minutes: 10 run: bundle exec rake sprockets-4.2.1/.github/workflows/isolated.yml000066400000000000000000000006211447572140400214660ustar00rootroot00000000000000name: CI isolated tests on: [push, pull_request] jobs: tests: runs-on: ubuntu-latest strategy: matrix: ruby: [2.7] steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run tests run: bundle exec rake test_isolated sprockets-4.2.1/.github/workflows/rubocop.yml000066400000000000000000000005401447572140400213330ustar00rootroot00000000000000name: RuboCop on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: ruby: [2.7] steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run linter run: bundle exec rubocop --parallel sprockets-4.2.1/.gitignore000066400000000000000000000001021447572140400155240ustar00rootroot00000000000000*.rbc .bundle Gemfile.lock README.html doc docs pkg tmp .DS_Store sprockets-4.2.1/.rubocop.yml000066400000000000000000000002171447572140400160150ustar00rootroot00000000000000require: rubocop-performance AllCops: DisabledByDefault: true DisplayCopNames: true TargetRubyVersion: 2.5 Performance: Enabled: true sprockets-4.2.1/CHANGELOG.md000066400000000000000000000120421447572140400153530ustar00rootroot00000000000000**Master** Get upgrade notes from Sprockets 3.x to 4.x at https://github.com/rails/sprockets/blob/master/UPGRADING.md ## 4.2.1 - Fix for precompile issues when multiple extensions map to the same MIME type (eg. `.jpeg` / `.jpg`). [#781](https://github.com/rails/sprockets/pull/781) - Fix `application/css-sourcemap+json` charset [#764](https://github.com/rails/sprockets/pull/764) - Fix compatibility with Rack 2 applications. [#790](https://github.com/rails/sprockets/pull/790) ## 4.2.0 - Rack 3 compatibility. [#758](https://github.com/rails/sprockets/pull/758) - Fix thread safety of `Sprockets::CachedEnvironment` and `Sprockets::Cache::MemoryStore`. [#771](https://github.com/rails/sprockets/pull/771) - Add support for Rack 3.0. Headers set by sprockets will now be lower case. [#758](https://github.com/rails/sprockets/pull/758) - Make `Sprockets::Utils.module_include` thread safe on JRuby. [#759](https://github.com/rails/sprockets/pull/759) - Fix typo in `asset.rb` file. [#768](https://github.com/rails/sprockets/pull/768) ## 4.1.1 - Fix `Sprockets::Server` to return response headers to be compatible with Rack::Lint 2.0. ## 4.1.0 - Allow age to be altered in asset:clean rake task. - Fix `Sprockets::Server` to return lower-cased response headers to comply with Rack::Lint 3.0. [#744](https://github.com/rails/sprockets/pull/744) - Adding new directive `depend_on_directory` [#668](https://github.com/rails/sprockets/pull/668) - Fix `application/js-sourcemap+json` charset [#669](https://github.com/rails/sprockets/pull/669) - Fix `CachedEnvironment` caching nil values [#723](https://github.com/rails/sprockets/pull/723) - Process `*.jst.ejs.erb` files with ERBProcessor [#674](https://github.com/rails/sprockets/pull/674) - Fix cache key for coffee script processor to be dependent on the filename [#670](https://github.com/rails/sprockets/pull/670) ## 4.0.3 - Fix `Manifest#find` yielding from a Promise causing issue on Ruby 3.1.0-dev. [#720](https://github.com/rails/sprockets/pull/720) - Better detect the ERB version to avoid deprecation warnings. [#719](https://github.com/rails/sprockets/pull/719) - Allow assets already fingerprinted to be served through `Sprockets::Server` - Do not fingerprint files that already contain a valid digest in their name - Remove remaining support for Ruby < 2.4.[#672](https://github.com/rails/sprockets/pull/672) ## 4.0.2 - Fix `etag` and digest path compilation that were generating string with invalid digest since 4.0.1. ## 4.0.1 - Fix for Ruby 2.7 keyword arguments warning in `base.rb`. [#660](https://github.com/rails/sprockets/pull/660) - Fix for when `x_sprockets_linecount` is missing from a source map. - Fix subresource integrity to match the digest of the asset. ## 4.0.0 - Fixes for Ruby 2.7 keyword arguments warnings [#625](https://github.com/rails/sprockets/pull/625) - Manifest files are sorted alphabetically [#626](https://github.com/rails/sprockets/pull/626) ## 4.0.0.beta10 - Fix YACB (Yet Another Caching Bug) [Fix broken expansion of asset link paths](https://github.com/rails/sprockets/pull/614) ## 4.0.0.beta9 - Minimum Ruby version for Sprockets 4 is now 2.5+ which matches minimum ruby version of Rails [#604] - Fix threading bug introduced in Sprockets 4 [#603] - Warn when two potential manifest files exist. [#560] ## 4.0.0.beta8 - Security release for [CVE-2018-3760](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-3760) ## 4.0.0.beta7 - Fix a year long bug that caused `Sprockets::FileNotFound` errors when the asset was present [#547] - Raise an error when two assets such as foo.js and foo.js.erb would produce the same output artifact (foo.js) [#549 #530] - Process `*.jst.eco.erb` files with ERBProcessor ## 4.0.0.beta6 - Fix source map line offsets [#515] - Return a `400 Bad Request` when the path encoding is invalid. [#514] ## 4.0.0.beta5 - Reduce string allocations - Source map metadata uses compressed form specified by the [source map v3 spec](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k). [#402] **[BREAKING]** - Generate [index maps](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt) when decoding source maps isn't necessary. [#402] - Remove fingerprints from source map files. [#402] ## 4.0.0.beta4 - Changing the version now busts the digest of all assets [#404] - Exporter interface added [#386] - Using ENV vars in templates will recompile templates when the env vars change. [#365] - Source maps for imported sass files with sassc is now fixed [#391] - Load paths now in error messages [#322] - Cache key added to babel processor [#387] - `Environment#find_asset!` can now be used to raise an exception when asset could not be found [#379] ## 4.0.0.beta3 - Source Map fixes [#255] [#367] - Performance improvements ## 4.0.0.beta2 - Fix load_paths on Sass processors [#223] ## 4.0.0.beta1 - Initial release of Sprockets 4 Please upgrade to the latest Sprockets 3 version before upgrading to Sprockets 4. Check the 3.x branch for previous changes https://github.com/rails/sprockets/blob/3.x/CHANGELOG.md. sprockets-4.2.1/CODE_OF_CONDUCT.md000066400000000000000000000004571447572140400163500ustar00rootroot00000000000000# Contributor Code of Conduct The Rails team is committed to fostering a welcoming community. **Our Code of Conduct can be found here**: http://rubyonrails.org/conduct/ For a history of updates, see the page history here: https://github.com/rails/rails.github.com/commits/master/conduct/index.html sprockets-4.2.1/CONTRIBUTING.md000066400000000000000000000060551447572140400160020ustar00rootroot00000000000000Contributing to Sprockets ===================== Sprockets is work of [hundreds of contributors](https://github.com/rails/sprockets/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/rails/sprockets/pulls), [propose features and discuss issues](https://github.com/rails/sprockets/issues). #### Fork the Project Fork the [project on Github](https://github.com/rails/sprockets) and check out your copy. ``` git clone https://github.com/contributor/sprockets.git cd sprockets git remote add upstream https://github.com/rails/sprockets.git ``` #### Create a Topic Branch Make sure your fork is up-to-date and create a topic branch for your feature or bug fix. ``` git checkout master git pull upstream master git checkout -b my-feature-branch ``` #### Bundle Install and Test Ensure that you can build the project and run tests. ``` bundle install bundle exec rake test ``` #### Write Tests 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). We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix. #### Write Code Implement your feature or bug fix. Make sure that `bundle exec rake test` completes without errors. #### Write Documentation Document any external behavior in the [README](README.md). #### Update Changelog Add a line to [CHANGELOG](CHANGELOG.md) under *Next Release*. Make it look like every other line, including your name and link to your Github account. #### Commit Changes Make sure git knows your name and email address: ``` git config --global user.name "Your Name" git config --global user.email "contributor@example.com" ``` Writing good commit logs is important. A commit log should describe what changed and why. ``` git add ... git commit ``` #### Push ``` git push origin my-feature-branch ``` #### Make a Pull Request Go to https://github.com/contributor/sprockets and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days. #### Rebase If you've been working on a change for a while, rebase with upstream/master. ``` git fetch upstream git rebase upstream/master git push origin my-feature-branch -f ``` #### Update CHANGELOG Again Update the [CHANGELOG](CHANGELOG.md) with a short description of your change. A typical entry looks as follows. ``` * Fix static asset mtime fallback. ``` Amend your previous commit and force push the changes. ``` git commit --amend git push origin my-feature-branch -f ``` #### Check on Your Pull Request 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. #### Be Patient 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! #### Thank You Please do know that we really appreciate and value your time and work. We love you, really. sprockets-4.2.1/Gemfile000066400000000000000000000000461447572140400150360ustar00rootroot00000000000000source "https://rubygems.org" gemspec sprockets-4.2.1/ISSUE_TEMPLATE.md000066400000000000000000000016741447572140400162600ustar00rootroot00000000000000### Expected behavior Tell us what should happen ### Actual behavior Tell us what happens instead ### System configuration - Sprockets version - Ruby version ### Example App (Reproduction) - THIS IS IMPORTANT YOUR ISSUE LIKELY WILL NOT BE RESOLVED WITHOUT THIS Please provide an [example app](https://www.codetriage.com/example_app) that reproduces the problem. This will save maintainers time so they can spend it fixing your issues instead of trying to build a reproduction case from sparse instructions. To create an example app, make an application locally that uses your same version of Sprockets. Add files to reproduce the problem. Once you've reproduced the problem add instructions on how to run your example app to a README.md file. Then commit everything to git and push to a new project on github. Once you've pushed your app add a link back to this issue. [More information about example apps](https://www.codetriage.com/example_app) sprockets-4.2.1/MIT-LICENSE000066400000000000000000000021131447572140400151740ustar00rootroot00000000000000Copyright (c) 2014-2019 Sam Stephenson Copyright (c) 2014-2019 Joshua Peek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sprockets-4.2.1/README.md000066400000000000000000000604011447572140400150230ustar00rootroot00000000000000# Sprockets: Rack-based asset packaging Sprockets is a Ruby library for compiling and serving web assets. It features declarative dependency management for JavaScript and CSS assets, as well as a powerful preprocessor pipeline that allows you to write assets in languages like CoffeeScript, Sass and SCSS. ## Installation Install Sprockets from RubyGems: ``` sh $ gem install sprockets ``` Or include it in your project's `Gemfile` with Bundler: ``` ruby gem 'sprockets', '~> 4.0' ``` ## Upgrading to Sprockets 4.x These are the major features in Sprockets 4.x - Source Maps - Manifest.js - ES6 support - Deprecated processor interface in 3.x is removed in 4.x Read more about them by referencing [Upgrading document](UPGRADING.md) ## Guides For most people interested in using Sprockets, you will want to see the README below. If you are a framework developer that is using Sprockets, see [Building an Asset Processing Framework](guides/building_an_asset_processing_framework.md). If you are a library developer who is extending the functionality of Sprockets, see [Extending Sprockets](guides/extending_sprockets.md). If you want to work on Sprockets or better understand how it works read [How Sprockets Works](guides/how_sprockets_works.md) ## Behavior Overview You can interact with Sprockets primarily through directives and file extensions. This section covers how to use each of these things, and the defaults that ship with Sprockets. Since you are likely using Sprockets through another framework (such as the [the Rails asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html)), there will be configuration options you can toggle that will change behavior such as what directories or files get compiled. For that documentation you should see your framework's documentation. #### Accessing Assets Assets in Sprockets are always referenced by their *logical path*. The logical path is the path of the asset source file relative to its containing directory in the load path. For example, if your load path contains the directory `app/assets/javascripts`:
Logical path Source file on disk
application.js app/assets/javascripts/application.js
models/project.js app/assets/javascripts/models/project.js
hello.js app/assets/javascripts/hello.coffee
> Note: For assets that are compiled or transpiled, you want to specify the extension that you want, not the extension on disk. For example we specified `hello.js` even if the file on disk is a coffeescript file, since the asset it will generate is javascript. ### Directives Directives are special comments in your asset file and the main way of interacting with processors. What kind of interactions? You can use these directives to tell Sprockets to load other files, or specify dependencies on other assets. For example, let's say you have custom JavaScript that you've written. You put this javascript in a file called `beta.js`. The javascript makes heavy use of jQuery, so you need to load that before your code executes. You could add a `require` directive to the top of `beta.js`: ```js //= require jquery $().ready({ // my custom code here }) ``` The directive processor understands comment blocks in three formats: ``` css /* Multi-line comment blocks (CSS, SCSS, JavaScript) *= require foo */ ``` ``` js // Single-line comment blocks (SCSS, JavaScript) //= require foo ``` ``` coffee # Single-line comment blocks (CoffeeScript) #= require foo ``` > Note: Directives are only processed if they come before any application code. Once you have a line that does not include a comment or whitespace then Sprockets will stop looking for directives. If you use a directive outside of the "header" of the document it will not do anything, and won't raise any errors. Here is a list of the available directives: - [`require`](#require) - Add the contents of a file to current - [`require_self`](#require_self) - Change order of where current contents are concatenated to current - [`require_directory`](#require_directory) - Add contents of each file in a folder to current - [`require_tree`](#require_tree) - Add contents of all files in all directories in a path to current - [`link`](#link) - Make target file compile and be publicly available without adding contents to current - [`link_directory`](#link_directory) - Make target directory compile and be publicly available without adding contents to current - [`link_tree`](#link_tree) - Make target tree compile and be publicly available without adding contents to current - [`depend_on`](#depend_on) - Recompile current file if target has changed - [`depend_on_directory`](#depend_on_directory) - Recompile current file if any files in target directory has changed - [`stub`](#stub) - Ignore target file You can see what each of these does below. ### Specifying Processors through File Extensions Sprockets uses the filename extensions to determine what processors to run on your file and in what order. For example if you have a file: ``` application.scss ``` Then Sprockets will by default run the sass processor (which implements scss). The output file will be converted to css. You can specify multiple processors by specifying multiple file extensions. For example you can use Ruby's [ERB template language](#invoking-ruby-with-erb) to embed content in your doc before running the sass processor. To accomplish this you would need to name your file ``` application.scss.erb ``` Processors are run from right to left (tail to head), so in the above example the processor associated with `erb` will be run before the processor associated with `scss` extension. For a description of the processors that Sprockets has by default see the "default processors" section below. Other libraries may register additional processors. When "asking" for a compiled file, you always ask for the extension you want. For example if you're using Rails, to get the contents of `application.scss.erb` you would use ``` asset_path("application.css") ``` Sprockets understands that `application.scss.erb` will compile down to a `application.css`. Ask for what you need, not what you have. If this isn't working like you expect, make sure you didn't typo an extension, and make sure the file is on a "load path" (see framework docs for adding new load paths). ## File Order Processing By default files are processed in alphabetical order. This behavior can impact your asset compilation when one asset needs to be loaded before another. For example if you have an `application.js` and it loads another directory ```js //= require_directory my_javascript ``` The files in that directory will be loaded in alphabetical order. If the directory looks like this: ```sh $ ls -1 my_javascript/ alpha.js beta.js jquery.js ``` Then `alpha.js` will be loaded before either of the other two. This can be a problem if `alpha.js` uses jquery. For this reason it is not recommend to use `require_directory` with files that are ordering dependent. You can either require individual files manually: ```js //= require jquery //= require alpha //= require beta ``` Or you can use index files to proxy your folders. ### Index files are proxies for folders In Sprockets index files such as `index.js` or `index.css` files inside of a folder will generate a file with the folder's name. So if you have a `foo/index.js` file it will compile down to `foo.js`. This is similar to NPM's behavior of using [folders as modules](https://nodejs.org/api/modules.html#modules_folders_as_modules). It is also somewhat similar to the way that a file in `public/my_folder/index.html` can be reached by a request to `/my_folder`. This means that you cannot directly use an index file. For example this would not work: ```erb <%= asset_path("foo/index.js") %> ``` Instead you would need to use: ```erb <%= asset_path("foo.js") %> ``` Why would you want to use this behavior? It is common behavior where you might want to include an entire directory of files in a top level JavaScript. You can do this in Sprockets using `require_tree .` ```js //= require_tree . ``` This has the problem that files are required alphabetically. If your directory has `jquery-ui.js` and `jquery.min.js` then Sprockets will require `jquery-ui.js` before `jquery` is required which won't work (because jquery-ui depends on jquery). Previously the only way to get the correct ordering would be to rename your files, something like `0-jquery-ui.js`. Instead of doing that you can use an index file. For example, if you have an `application.js` and want all the files in the `foo/` folder you could do this: ```js //= require foo.js ``` Then create a file `foo/index.js` that requires all the files in that folder in any order you want using relative references: ```js //= require ./foo.min.js //= require ./foo-ui.js ``` Now in your `application.js` will correctly load the `foo.min.js` before `foo-ui.js`. If you used `require_tree` it would not work correctly. ## Cache Compiling assets is slow. It requires a lot of disk use to pull assets off of hard drives, a lot of RAM to manipulate those files in memory, and a lot of CPU for compilation operations. Because of this Sprockets has a cache to speed up asset compilation times. That's the good news. The bad news, is that sprockets has a cache and if you've found a bug it's likely going to involve the cache. By default Sprockets uses the file system to cache assets. It makes sense that Sprockets does not want to generate assets that already exist on disk in `public/assets`, what might not be as intuitive is that Sprockets needs to cache "partial" assets. For example if you have an `application.js` and it is made up of `a.js`, `b.js`, all the way to `z.js` ```js //= require a.js //= require b.js # ... //= require z.js ``` The first time this file is compiled the `application.js` output will be written to disk, but also intermediary compiled files for `a.js` etc. will be written to the cache directory (usually `tmp/cache/assets`). So, if `b.js` changes it will get recompiled. However instead of having to recompile the other files from `a.js` to `z.js` since they did not change, we can use the prior intermediary files stored in the cached values . If these files were expensive to generate, then this "partial" asset cache strategy can save a lot of time. Directives such as `require`, `link`, `depend_on`, and `depend_on_directory` tell Sprockets what assets need to be re-compiled when a file changes. Files are considered "fresh" based on their mtime on disk and a combination of cache keys. On Rails you can force a "clean" install by clearing the `public/assets` and `tmp/cache/assets` directories. ## Default Directives Directives take a path or a path to a file. Paths for directive can be relative to the current file, for example: ```js //= require ../foo.js ``` This would load the file up one directory and named `foo.js`. However this isn't required if `foo.js` is on one of Sprocket's load paths. You can simply use ```js //= require foo.js ``` Without any prepended dots and sprockets will search for the asset. If the asset is on a sub-path of the load path, you can specify it without using a relative path as well: ```js //= require sub/path/foo.js ``` You can also use an absolute path, but this is discouraged unless you know the directory structure of every machine you plan on running code on. Below is a section for each of the built in directive types supported by Sprockets. ### require `require` *path* inserts the contents of the asset source file specified by *path*. If the file is required multiple times, it will appear in the bundle only once. **Example:** If you've got an `a.js`: ```js var a = "A"; ``` and a `b.js`; ```js var b = "B"; ``` Then you could require both of these in an `application.js` ```js //= require a.js //= require b.js ``` Which would generate one concatenated file: ```js var a = "A"; var b = "B"; ``` ### require_self `require_self` tells Sprockets to insert the body of the current source file before any subsequent `require` directives. **Example:** If you've got an `a.js`: ```js var a = "A"; ``` And an `application.js` ```js //= require_self //= require 'a.js' var app_name = "Sprockets"; ``` Then this will take the contents of `application.js` (that come after the last require) and put them at the beginning of the file: ```js var app_name = "Sprockets"; var a = "A"; ``` ### require_directory `require_directory` *path* requires all source files of the same format in the directory specified by *path*. Files are required in alphabetical order. **Example:** If we've got a directory called `alphabet` with an `a.js` and `b.js` files like before, then our `application.js` ```js //= require_directory alphabet ``` Would produce: ```js var a = "A"; var b = "B"; ``` You can also see [Index files are proxies for folders](#index-files-are-proxies-for-folders) for another method of organizing folders that will give you more control. ### require_tree `require_tree` *path* works like `require_directory`, but operates recursively to require all files in all subdirectories of the directory specified by *path*. ### link `link` *path* declares a dependency on the target *path* and adds it to a list of subdependencies to be compiled when the asset is written out to disk. Example: If you've got a `manifest.js` file and you want to specify that a `admin.js` source file should be generated and made available to the public you can link it by including this in the `manifest.js` file: ``` //= link admin.js ``` The argument to `link` is a _logical path_, that is it will be resolved according to the configured asset load paths. See [Accessing Assets](#accessing-assets) above. A path relative to the current file won't work, it must be a logical path. **Caution**: the "link" directive should always have an explicit extension on the end. `link` can also be used to include manifest files from mounted Rails engines: ``` //= link my_engine_manifest ``` This would find a manifest file at `my_engine/app/assets/config/my_engine_manifest.js` and include its directives. ### link_directory `link_directory` *path* links all the files inside the directory specified by the *path*. By "link", we mean they are specified as compilation targets to be written out to disk, and made available to be served to user-agents. Files in subdirectories will not be linked (Compare to [link_tree](#link_tree)). The *path* argument to `link_directory` is _not_ a logical path (it does not use the asset load paths), but is a path relative to the file the `link_directory` directive is found in, and can use `..` to . For instance, you might want: ```js //= link_directory ../stylesheets ``` `link_directory` can take an optional second argument with an extension or content-type, with the two arguments separated by a space: ```js //= link_directory ../stylesheets text/css //= link_directory ../more_stylesheets .css ``` This will limit the matching files to link to only files recognized as that type. An extension is just a shortcut for the type referenced, it does not need to match the source file exactly, but instead identifies the content-type the source file must be recognized as. ### link_tree `link_tree` *path* works like [link_directory](#link_directory), but operates recursively to link all files in all subdirectories of the directory specified by *path*. Example: ```js //= link_tree ./path/to/folder ``` Like `link_directory`, the argument is path relative to the current file, it is *not* a 'logical path' tresolved against load paths. As with `link_directory`, you can also specify a second argument -- separated by a space -- so any extra files not matching the content-type specified will be ignored: ```js //= link_tree ./path/to/folder text/javascript //= link_tree ./path/to/other_folder .js ``` ### depend_on `depend_on` *path* declares a dependency on the given *path* without including it in the bundle. This is useful when you need to expire an asset's cache in response to a change in another file. **Example:** If you have a file such as `bar.data` and you're using data from that file in another file, then you need to tell sprockets that it needs to re-compile the file if `bar.data` changes: ```js //= depend_on "bar.data" var bar = '<%= File.read("bar.data") %>' ``` To depend on an entire directory containing multiple files, use `depend_on_directory` ### depend_on_asset `depend_on_asset` *path* works like `depend_on`, but operates recursively reading the file and following the directives found. This is automatically implied if you use `link`, so consider if it just makes sense using `link` instead of `depend_on_asset`. ### depend_on_directory `depend_on_directory` *path* declares all files in the given *path* without including them in the bundle. This is useful when you need to expire an asset's cache in response to a change in multiple files in a single directory. All paths are relative to your declaration and must begin with `./` Also, your must include these directories in your [load path](guides/building_an_asset_processing_framework.md#the-load-path). **Example:** If we've got a directory called `data` with files `a.data` and `b.data` ``` // ./data/a.data A ``` ``` // ./data/b.data B ``` ``` // ./file.js.erb //= depend_on_directory ./data var a = '<% File.read('data/a.data') %>' var b = '<% File.read('data/b.data') %>' ``` Would produce: ```js var a = "A"; var b = "B"; ``` You can also see [Index files are proxies for folders](#index-files-are-proxies-for-folders) for another method of organizing folders that will give you more control. ### stub `stub` *path* excludes that asset and its dependencies from the asset bundle. The *path* must be a valid asset and may or may not already be part of the bundle. `stub` should only be used at the top level bundle, not within any subdependencies. ### Invoking Ruby with ERB Sprockets provides an ERB engine for preprocessing assets using embedded Ruby code. Append `.erb` to a CSS or JavaScript asset's filename to enable the ERB engine. For example if you have an `app/application/javascripts/app_name.js.erb` you could have this in the template ```js var app_name = "<%= ENV['APP_NAME'] %>"; ``` Generated files are cached. If you're using an `ENV` var then when you change then ENV var the asset will be forced to recompile. This behavior is only true for environment variables, if you are pulling a value from somewhere else, such as a database, you must manually invalidate the cache to see the change. If you're using Rails, there are helpers you can use such as `asset_url` that will cause a recompile if the value changes. For example if you have this in your `application.css` ``` css .logo { background: url(<%= asset_url("logo.png") %>) } ``` When you modify the `logo.png` on disk, it will force `application.css` to be recompiled so that the fingerprint will be correct in the generated asset. You can manually make sprockets depend on any other file that is generated by sprockets by using the `depend_on` or `depend_on_directory` directive. Rails implements the above feature by auto calling `depend_on` on the original asset when the `asset_url` is used inside of an asset. ### Styling with Sass and SCSS [Sass](http://sass-lang.com/) is a language that compiles to CSS and adds features like nested rules, variables, mixins and selector inheritance. If the `sass` gem is available to your application, you can use Sass to write CSS assets in Sprockets. Sprockets supports both Sass syntaxes. For the original whitespace-sensitive syntax, use the extension `.sass`. For the new SCSS syntax, use the extension `.scss`. In Rails if you have `app/application/stylesheets/foo.scss` it can be referenced with `<%= asset_path("foo.css") %>`. When referencing an asset in Rails, always specify the extension you want. Sprockets will convert `foo.scss` to `foo.css`. ### Scripting with CoffeeScript [CoffeeScript](http://jashkenas.github.io/coffeescript/) is a language that compiles to the "good parts" of JavaScript, featuring a cleaner syntax with array comprehensions, classes, and function binding. If the `coffee-script` gem is available to your application, you can use CoffeeScript to write JavaScript assets in Sprockets. Note that the CoffeeScript compiler is written in JavaScript, and you will need an [ExecJS](https://github.com/rails/execjs)-supported runtime on your system to invoke it. To write JavaScript assets with CoffeeScript, use the extension `.coffee`. In Rails if you have `app/application/javascripts/foo.coffee` it can be referenced with `<%= asset_path("foo.js") %>`. When referencing an asset in Rails, always specify the extension you want. Sprockets will convert `foo.coffee` to `foo.js`. ## ES6 Support Sprockets 4 ships with a Babel processor. This allows you to transpile ECMAScript6 to JavaScript just like you would transpile CoffeeScript to JavaScript. To use this, modify your Gemfile: ```ruby gem 'babel-transpiler' ``` Any asset with the extension `es6` will be treated as an ES6 file: ```es6 // app/assets/javascript/application.es6 var square = (n) => n * n console.log(square); ``` Start a Rails server in development mode and visit `localhost:3000/assets/application.js`, and this asset will be transpiled to JavaScript: ```js var square = function square(n) { return n * n; }; console.log(square); ``` ### JavaScript Templating with EJS and Eco Sprockets supports *JavaScript templates* for client-side rendering of strings or markup. JavaScript templates have the special format extension `.jst` and are compiled to JavaScript functions. When loaded, a JavaScript template function can be accessed by its logical path as a property on the global `JST` object. Invoke a template function to render the template as a string. The resulting string can then be inserted into the DOM. ```
Hello, <%= name %>!
// application.js //= require templates/hello $("#hello").html(JST["templates/hello"]({ name: "Sam" })); ``` Sprockets supports two JavaScript template languages: [EJS](https://github.com/sstephenson/ruby-ejs), for embedded JavaScript, and [Eco](https://github.com/sstephenson/ruby-eco), for embedded CoffeeScript. Both languages use the familiar `<% … %>` syntax for embedding logic in templates. If the `ejs` gem is available to your application, you can use EJS templates in Sprockets. EJS templates have the extension `.jst.ejs`. If the `eco` gem is available to your application, you can use [Eco templates](https://github.com/sstephenson/eco) in Sprockets. Eco templates have the extension `.jst.eco`. Note that the `eco` gem depends on the CoffeeScript compiler, so the same caveats apply as outlined above for the CoffeeScript engine. ### Minifying Assets Several JavaScript and CSS minifiers are available through shorthand. In Rails you will specify them with: ```ruby config.assets.js_compressor = :terser config.assets.css_compressor = :scss ``` If you're not using Rails, configure this directly on the "environment". ``` ruby environment.js_compressor = :terser environment.css_compressor = :scss ``` If you are using Sprockets directly with a Rack app, don't forget to add the `terser` and `sass` gems to your Gemfile when using above options. ### Gzip By default when Sprockets generates a compiled asset file it will also produce a gzipped copy of that file. Sprockets only gzips non-binary files such as CSS, javascript, and SVG files. For example if Sprockets is generating ``` application-12345.css ``` Then it will also generate a compressed copy in ``` application-12345.css.gz ``` This behavior can be disabled, refer to your framework specific documentation. ### Serving Assets In production you should generate your assets to a directory on disk and serve them either via Nginx or a feature like Rail's `config.public_file_server.enabled = true`. On Rails you can generate assets by running: ```term $ RAILS_ENV=production rake assets:precompile ``` In development Rails will serve assets from `Sprockets::Server`. ## Contributing to Sprockets Sprockets is the work of hundreds of contributors. You're encouraged to submit pull requests, propose features and discuss issues. See [CONTRIBUTING](CONTRIBUTING.md). ### Version History Please see the [CHANGELOG](https://github.com/rails/sprockets/tree/master/CHANGELOG.md) ## License Sprockets is released under the [MIT License](MIT-LICENSE). sprockets-4.2.1/Rakefile000066400000000000000000000007651447572140400152200ustar00rootroot00000000000000require "rake/testtask" require "bundler/gem_tasks" task :default => :test Rake::TestTask.new do |t| t.libs << "test" t.warning = true end task :test_isolated do Dir["test/test*.rb"].each do |fn| ruby "-Ilib:test", "-w", fn abort unless $?.success? end end begin require "rubocop/rake_task" RuboCop::RakeTask.new(:rubocop) do |task| task.options = ['--display-cop-names'] end rescue LoadError # We are in the production environment, where Rubocop is not required. end sprockets-4.2.1/UPGRADING.md000066400000000000000000000225451447572140400154150ustar00rootroot00000000000000# Guide to upgrading from Sprockets 3.x to 4.x Make sure that you're running the latest Sprockets 3 release. This document is a work in progress and not at all authoritative. It is meant to underline the biggest features and changes from Sprockets 3 to 4. If you're not already on Sprockets 3 check out https://github.com/rails/sprockets/blob/3.x/UPGRADING.md. Sprockets 3 was a compatibility release to bridge Sprockets 4, and many deprecated things have been removed in version 4. This upgrading guide touches on: - Upgrading as a Rails dependency - Source Maps - Manifest.js - In a Rails app, possible backwards incompatible changes in how top-level targets are determined. - ES6 support - Deprecated processor interface in 3.x is removed in 4.x ## Upgrading as a Rails Dependency Your Rails app Gemfile may have a line requiring sass-rails 5.0: gem 'sass-rails', '~> 5.0' # or gem 'sass-rails', '~> 5' These will prevent upgrade to sprockets 4, if you'd like to upgrade to sprockets 4 change to: gem 'sass-rails', '>= 5' And then run `bundle update sass-rails sprockets` to get sass-rails 6.x and sprockets 4.x. ## Source Maps Read more about [What is a source map](https://schneems.com/2017/11/14/wtf-is-a-source-map/). Source maps are a major new feature. As a word of warning, source maps were half finished when this project was transitioned between maintainers. Please try things and if they don't work correctly open an issue with what you expected to happen, what happened and a small sample app showing the problem. First, what is a source map? Source maps are a standard way to make debugging concatenated or compiled assets easier. When using Rails and Sprockets in development mode, no assets are concatenated. If your app used 10 JS files, all of them would be served independently. This helped with debugging: you got helpful errors like `Error in file on line ` that pointed at the problem instead of at an unrelated, minified JS file. Source maps eliminate the need to serve these separate files. Instead, a special source map file can be read by the browser to help it understand how to unpack your assets. It "maps" the current, modified asset to its "source" so you can view the source when debugging. This way you can serve assets in development in the exact same way as in production. Fewer surprises is always better. How do you know if source maps are working correctly? Try adding a syntax error to one of your assets and use the console to debug. Does it show the correct file and source location? Or does it reference the top level `application.js` file? [Here's the last issue](https://github.com/rails/sprockets/issues/157) where source maps were discussed before the beta release. [Here's a guide](https://github.com/rails/sprockets/blob/master/guides/source_maps.md) that talks about what a source map is and how it is used by the browser and generated by Sprockets. ## Manifest.js When compiling assets with Sprockets, Sprockets needs to decide which top-level targets to compile, usually `application.css`, `application.js`, and images. If you are using sprockets prior to 4.0, Rails will compile `application.css`, `application.js`; and *any* files found in your assets directory(ies) that are _not_ recognized as JS or CSS, but do have a filename extension. That latter was meant to apply to all your images usually in `./app/assets/images/`, but could have targeted other files as well. If you wanted to specify additional assets to deliver that were not included by this logic, for instance for a marketing page with its own CSS, you might add something like this: ```ruby # In your Rails configuration, prior to Sprockets 4 config.assets.precompile += ["marketing.css"] ``` If you are using Sprockets 4, Rails changes its default logic for determining top-level targets. It will now use _only_ a file at `./app/assets/config/manifest.js` for specifying top-level targets; this file may already exist in your Rails app (although Rails only starts automatically using it once you are using sprockets 4), if not you should create it. The `manifest.js` file is meant to specify which files to use as a top-level target using sprockets methods `link`, `link_directory`, and `link_tree`. The default `manifest.js` created by `rails new` for the past few Rails versions looks like: ```js //= link_tree ../images //= link_directory ../javascripts .js //= link_directory ../stylesheets .css ``` This is meant to include the contents of all files found in the `./app/assets/images` directory or any subdirectories as well as any file recognized as JS directly at `./app/assets/javascripts` or as CSS directly at `./app/assets/stylesheets` (both not including subdirectories). (The JS line is not generated in Rails 6.0 apps, since Rails 6.0 apps do not manage JS with sprockets). Since the default logic for determining top-level targets changed, you might find some files that were currently compiled by sprockets for delivery to browser no longer are. You will have to edit the `manifest.js` to specify those files. You may also find that some files that were *not* previously compiled as top-level targets are now. For instance, if your existing app has any js files directly at `./app/assets/javascripts` or css/scss files `./app/assets/stylesheets`, Rails with Sprockets 4 will now compile them as top-level targets. Since they were not previously treated as such, you probably don't mean them to be; if they are .scss partials referencing variables meant to be defined in other files, it may even result in an error message that looks like `Undefined variable: $some_variable`. To correct this, you can move these files to some _subdirectory_ of `./app/assets/stylesheets` or `javascripts`; or you can change the `manifest.js` to be more like how Rails with Sprockets 3 works, linking only the specific `application` files as top-level targets: ```js //= link_tree ../images //= link application.css //= link application.js // // maybe another non-standard file? //= link marketing.css ``` **Caution**: the "link" directive should always have an explicit content type or file extension. Now you'll be able to use a `<%= stylesheet_link_tag "application" %>` or `<%= stylesheet_link_tag "marketing" %>` in your code. If you have additional non-standard files you need to be top-level targets, instead of using `config.assets.precompile`, you can use `link`, `link_directory`, and `link_tree` directives in the `manifest.js`. If you are mounting Rails engines which provide their own assets, check to see if they define their own `manifest.js` file. That file can also be linked using the `link` directive: ```js // app/assets/config/manifest.js //= link my_engine // my_engine/app/assets/config/my_engine.js //= link_directory ../stylesheets/my_engine .css ``` This example will direct Sprockets to include the manifest file for the engine `my_engine`; since that manifest uses `link_directory`, the CSS file at `my_engine/app/assets/stylesheets/my_engine/overrides.css` will be made available to Rails (most importantly, to the engine's templates) at `my_engine/overrides`. Existing `config.assets.precompile` settings will still work for string values (although it is discouraged), but if you were previously using regexp or proc values, they won't work at all with Sprockets 4, and if you try you'll get an exception raised that looks like `NoMethodError: undefined method 'start_with?'` Some assets will be compiled as top-level assets when they are referenced from inside of another asset. For example, the `asset_url` erb helper will automatically link assets: ``` css .logo { background: url(<%= asset_url("logo.png") %>) } ``` When you do this Sprockets will "link" `logo.png` behind the scenes. This lets Sprockets know that this file needs to be compiled and made publicly available. If that logo file changes, Sprockets will automatically see that change and re-compile the CSS file. One benefit of using a `manifest.js` file for this type of configuration is that now Sprockets is using Sprockets to understand what files need to be generated instead of a non-portable framework-specific interface. For more information on `link`, `link_tree`, and `link_directory` see the [README](./README.md). ## ES6 Support Sprockets 4 ships with a Babel processor. This allows you to transpile ECMAScript6 to JavaScript just like you would transpile CoffeeScript to JavaScript. To use this, modify your Gemfile: ```ruby gem 'babel-transpiler' ``` Any asset with the extension `es6` will be treated as an ES6 file: ```es6 // app/assets/javascript/application.es6 var square = (n) => n * n console.log(square); ``` Start a Rails server in development mode and visit `localhost:3000/assets/application.js`, and this asset will be transpiled to JavaScript: ```js var square = function square(n) { return n * n; }; console.log(square); ``` ## Supporting All Versions of Sprockets in Processors If you are [extending Sprockets](guides/extending_sprockets.md) you may want to support all current major versions of Sprockets (2, 3, and 4). The processor interface was deprecated from Sprockets 2 and a legacy shim was put into Sprockets 3. Now that Sprockets 4 is out, that shim no longer is active. You'll need to update your gem to either only use the new interface or use both interfaces. Please see the "Supporting all versions of Sprockets in Processors" section in the [extending Sprockets guide](guides/extending_sprockets.md) for details. sprockets-4.2.1/appveyor.yml000066400000000000000000000005021447572140400161300ustar00rootroot00000000000000install: - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% - bundle config --local path vendor/bundle - bundle install build: off cache: - vendor/bundle before_test: - ruby -v - gem -v - bundle -v test_script: - bundle exec rake environment: matrix: - RUBY_VERSION: 26-x64 - RUBY_VERSION: 25-x64 sprockets-4.2.1/bin/000077500000000000000000000000001447572140400143135ustar00rootroot00000000000000sprockets-4.2.1/bin/sprockets000077500000000000000000000043341447572140400162620ustar00rootroot00000000000000#!/usr/bin/env ruby $VERBOSE = nil require 'sprockets' require 'optparse' require 'shellwords' unless ARGV.delete("--noenv") if File.exist?(path = "./.sprocketsrc") rcflags = Shellwords.split(File.read(path)) ARGV.unshift(*rcflags) end end paths = [] environment = Sprockets::Environment.new(Dir.pwd) manifest = nil (ENV['SPROCKETS_PATH'] || "").split(File::PATH_SEPARATOR).each do |path| environment.append_path path end OptionParser.new do |opts| opts.summary_width = 28 opts.banner = "Usage: sprockets [options] filename [filename ...]" def opts.show_usage puts self exit 1 end opts.on("-r", "--require LIBRARY", "Require the LIBRARY before doing anything") do |lib| require lib end opts.on("-I DIRECTORY", "--include=DIRECTORY", "Adds the directory to the Sprockets load path") do |directory| environment.append_path directory end opts.on("-o DIRECTORY", "--output=DIRECTORY", "Copy provided assets into DIRECTORY") do |directory| manifest = Sprockets::Manifest.new(environment, directory) end opts.on("--css-compressor=COMPRESSOR", "Use CSS compressor") do |compressor| environment.css_compressor = compressor.to_sym end opts.on("--js-compressor=COMPRESSOR", "Use JavaScript compressor") do |compressor| environment.js_compressor = compressor.to_sym end opts.on("--noenv", "Disables .sprocketsrc file") do end opts.on("--cache=DIRECTORY", "Enables the FileStore cache using the specified directory") do |directory| environment.cache = Sprockets::Cache::FileStore.new(directory) end opts.on_tail("-h", "--help", "Shows this help message") do opts.show_usage end opts.on_tail("-v", "--version", "Shows version") do puts Sprockets::VERSION exit end opts.show_usage if ARGV.empty? begin opts.order(ARGV) do |path| paths << path end rescue OptionParser::ParseError => e opts.warn e.message opts.show_usage end end if environment.paths.empty? warn "No load paths given" warn "Usage: sprockets -Ijavascripts/ path" exit 1 end if manifest manifest.compile(paths) elsif paths.length == 1 puts environment.find_asset(paths.first).to_s else warn "Only one file can be compiled to stdout at a time" exit 1 end sprockets-4.2.1/guides/000077500000000000000000000000001447572140400150235ustar00rootroot00000000000000sprockets-4.2.1/guides/building_an_asset_processing_framework.md000066400000000000000000000235071447572140400253370ustar00rootroot00000000000000# Building an Asset Processing Framework This guide is for using a Sprockets::Environment to process assets. You would use this class directly if you were building a feature similar to Rail's asset pipeline. If you aren't building an asset processing frameworks, you will want to refer to the [Readme](../README.md). For a reference use of `Sprockets::Environment` see [sprockets-rails](http://github.com/rails/Sprockets-rails). ## Understanding the Sprockets Environment You'll need an instance of the `Sprockets::Environment` class to access and serve assets from your application. Under Rails 4.0 and later, `Rails.application.assets` is a preconfigured `Sprockets::Environment` instance. For Rack-based applications, create an instance in `config.ru`. The Sprockets `Environment` has methods for retrieving and serving assets, manipulating the load path, and registering processors. It is also a Rack application that can be mounted at a URL to serve assets over HTTP. ### The Load Path The *load path* is an ordered list of directories that Sprockets uses to search for assets. In the simplest case, a Sprockets environment's load path will consist of a single directory containing your application's asset source files. When mounted, the environment will serve assets from this directory as if they were static files in your public root. The power of the load path is that it lets you organize your source files into multiple directories -- even directories that live outside your application -- and combine those directories into a single virtual filesystem. That means you can easily bundle JavaScript, CSS and images into a Ruby library or [Bower](http://bower.io) package and import them into your application. #### Manipulating the Load Path To add a directory to your environment's load path, use the `append_path` and `prepend_path` methods. Directories at the beginning of the load path have precedence over subsequent directories. ``` ruby environment = Sprockets::Environment.new environment.append_path 'app/assets/javascripts' environment.append_path 'lib/assets/javascripts' environment.append_path 'vendor/assets/bower_components' ``` In general, you should append to the path by default and reserve prepending for cases where you need to override existing assets. ## Precompile Files In addition to providing a path, you'll need to give sprockets an entry point to start precompiling files for production deployment. This is usually done via the `Sprockets::Manifest` class like: ``` SprocketsManifest#find(config.assets.precompile).map(&:logical_path).to_set ``` You can see an example in [sprockets-rails](https://github.com/rails/sprockets-rails/blob/49bf8022c8d3e1d7348b3fe2e0931b2e448f1f58/lib/sprockets/railtie.rb#L50). ### Accessing Assets Once you've set up your environment's load path, you can mount the environment as a Rack server and request assets via HTTP. You can also access assets programmatically from within your application. #### Logical Paths Assets in Sprockets are always referenced by their *logical path*. The logical path is the path of the asset source file relative to its containing directory in the load path. For example, if your load path contains the directory `app/assets/javascripts`:
Logical path Source file on disk
application.js app/assets/javascripts/application.js
models/project.js app/assets/javascripts/models/project.js
hello.js app/assets/javascripts/hello.coffee
> Note: For assets that are compiled or transpiled, you want to specify the extension that you want, not the extension on disk. For example we specified `hello.js` even if the file on disk is a coffeescript file, since the asset it will generate is javascript. In this way, all directories in the load path are merged to create a virtual filesystem whose entries are logical paths. #### Serving Assets Over HTTP When you mount an environment, all of its assets are accessible as logical paths underneath the *mount point*. For example, if you mount your environment at `/assets` and request the URL `/assets/application.js`, Sprockets will search your load path for the file named `application.js` and serve it. Under Rails 4.0 and later, your Sprockets environment is automatically mounted at `/assets`. If you are using Sprockets with a Rack application, you will need to mount the environment yourself. A good way to do this is with the `map` method in `config.ru`: ``` ruby require 'sprockets' map '/assets' do environment = Sprockets::Environment.new environment.append_path 'app/assets/javascripts' environment.append_path 'app/assets/stylesheets' run environment end map '/' do run YourRackApp end ``` #### Accessing Assets Programmatically You can use the `find_asset` method (aliased as `[]`) to retrieve an asset from a Sprockets environment. Pass it a logical path and you'll get a `Sprockets::Asset` instance back: ``` ruby environment['application.js'] # => # ``` Call `to_s` on the resulting asset to access its contents, `length` to get its length in bytes, `mtime` to query its last-modified time, and `filename` to get its full path on the filesystem. ## Using Processors Asset source files can be written in another format, like SCSS or CoffeeScript, and automatically compiled to CSS or JavaScript by Sprockets. Processors that convert a file from one format to another are called *transformers*. ### Invoking Ruby with ERB Sprockets provides an ERB engine for preprocessing assets using embedded Ruby code. Append `.erb` to a CSS or JavaScript asset's filename to enable the ERB engine. Ruby code embedded in an asset is evaluated in the context of a `Sprockets::Context` instance for the given asset. Common uses for ERB include: - embedding another asset as a Base64-encoded `data:` URI with the `asset_data_uri` helper - inserting the URL to another asset, such as with the `asset_path` helper provided by the Sprockets Rails plugin - embedding other application resources, such as a localized string database, in a JavaScript asset via JSON - embedding version constants loaded from another file See the [Helper Methods](lib/sprockets/context.rb) section for more information about interacting with `Sprockets::Context` instances via ERB. ## Managing and Bundling Dependencies You can create *asset bundles* -- ordered concatenations of asset source files -- by specifying dependencies in a special comment syntax at the top of each source file. Sprockets reads these comments, called *directives*, and processes them to recursively build a dependency graph. When you request an asset with dependencies, the dependencies will be included in order at the top of the file. ### The Directive Processor Sprockets runs the *directive processor* on each CSS and JavaScript source file. The directive processor scans for comment lines beginning with `=` in comment blocks at the top of the file. ``` js //= require jquery //= require jquery-ui //= require backbone //= require_tree . ``` The first word immediately following `=` specifies the directive name. Any words following the directive name are treated as arguments. Arguments may be placed in single or double quotes if they contain spaces, similar to commands in the Unix shell. **Note**: Non-directive comment lines will be preserved in the final asset, but directive comments are stripped after processing. Sprockets will not look for directives in comment blocks that occur after the first line of code. #### Supported Comment Types The directive processor understands comment blocks in three formats: ``` css /* Multi-line comment blocks (CSS, SCSS, JavaScript) *= require foo */ ``` ``` js // Single-line comment blocks (SCSS, JavaScript) //= require foo ``` ``` coffee # Single-line comment blocks (CoffeeScript) #= require foo ``` ## Processor Interface Sprockets 2.x was originally designed around [Tilt](https://github.com/rtomayko/tilt)'s engine interface. However, starting with 3.x, a new interface has been introduced deprecating Tilt. Similar to Rack, a processor is any "callable" (an object that responds to `call`). This may be a simple Proc or a full class that defines a `def self.call(input)` method. The `call` method accepts an `input` Hash and returns a Hash of metadata. Also see [`Sprockets::ProcessorUtils`](https://github.com/rails/sprockets/blob/master/lib/sprockets/processor_utils.rb) for public helper methods. ## Gzip By default when Sprockets generates a compiled asset file it will also produce a gzipped copy of that file. Sprockets only gzips non-binary files such as CSS, JavaScript, and SVG files. For example if Sprockets is generating ``` application-12345.css ``` Then it will also generate a compressed copy in ``` application-12345.css.gz ``` You can disable this behavior `Sprockets::Environment#gzip=` to something falsey for example: ```ruby env = Sprockets::Environment.new(".") env.gzip = false ``` By default Sprockets uses zlib to generate the compiled asset, you can use zopfli by installing the zopfli gem and then telling Sprockets to compile assets with it: ```ruby env = Sprockets::Environment.new(".") env.gzip = :zopfli ``` Setting to any other truthy value will enable zlib compression. ## WIP This guide is a work in progress. There are many different groups of people who interact with Sprockets. Some only need to know directive syntax to put in their asset files, some are building features like the Rails asset pipeline, and some are plugging into Sprockets and writing things like preprocessors. The goal of these guides are to provide task specific guidance to make the expected behavior explicit. If you are using Sprockets and you find missing information in these guides, please consider submitting a pull request with updated information. These guides live in [guides](/guides). sprockets-4.2.1/guides/end_user_asset_generation.md000066400000000000000000000001521447572140400225610ustar00rootroot00000000000000The bulk of the contents of this file have been moved to the [README](https://github.com/rails/sprockets) sprockets-4.2.1/guides/extending_sprockets.md000066400000000000000000000527371447572140400214450ustar00rootroot00000000000000# Extending Sprockets Sprockets can use custom processors, compressors, and directives. This document is intended for library authors who want to extend Sprockets functionality. ## Contents - [Types of Extensions](#types-of-extensions) - [Processors](#processors) - [Transformers](#transformers) - [Compressors](#compressors) - [Exporters](#exporters) - [Extension Interface](#extension-interface) - [Extension Keys](#extension-keys) - [Extension Metadata Keys](#extension-metadata-keys) - [Register Mime Type](#register-mime-types) - [Adding Directives to your Extension](#adding-directives-to-your-extension) - [Adding ERB Support to your Extension](#adding-erb-support-to-your-extension) - [Supporting All Versions of Sprockets in Processors](#supporting-all-versions-of-sprockets-in-processors) - [Registering All Versions of Sprockets in Processors](#registering-all-versions-of-sprockets-in-processors) ## Types of Extensions Sprockets supports a few different ways to extend functionality. - processors - transformers - compressors - exporters For a detailed explanation of each see the respective sections below. Sprockets ships with a number of built in processors, transformers and compressors. You can see all the defaults registered in `lib/sprockets.rb`. ### Processors There are two types of processors: preprocessors and postprocessors. A preprocessor is called as an asset is loaded. Generally preprocessors take in a raw file and convert its contents. For example the `DirectiveProcessor` is the processor that is responsible for reading in the Sprockets directives such as: ```js //= require "foo" ``` It will then load in the `foo` file and add its contents to the original file. A postprocessor is called after all of the transformers run. Generally postprocessors do a final transformation to an asset before it is ready for bundling in the asset pipeline. For example, imagine that you like to write JavaScript without semicolons. However, your project uses a CoffeeScript library. Since CoffeeScript adds semicolons during compilation, you might make a processor that removes all `;` from JavaScript files since CoffeeScript compiles to JavaScript. A simple implementation of this could be a proc: ```ruby remove_semicolons_processor = -> (input) { data = input[:data].gsub(";", "") { data: data } } ``` When you register this processor as a postprocessor your CoffeeScript library will first be compiled to JavaScript, then post-processed by this processor, thus removing all semicolons from the output. Without postprocessors, you would have to ensure that the CoffeeScript transformer is run _before_ your processor so you would have to insert it after the CoffeeScript transformer much like two Rack middlewares that have an order-dependence on one another. With postprocessors, you can register your processor as a postprocessor and not need to worry about ordering it after the CoffeeScript transformer. ## Transformers A transformer takes one asset and converts it into another asset. For example the `CoffeeScriptProcessor` is what takes a file with a `.coffee` file extension and returns a `.js` file. ### Transformers Like a preprocessor, a transformer will return the contents of the file in a `:data` key, and any other metadata. You can register a transformer like this: ```ruby Sprockets.register_transformer 'text/coffeescript', 'application/javascript', CoffeeScriptProcessor ``` The first argument is the mime type of the file that the processor accepts. The second argument is the mime type that it generates, and the last argument is the object that responds to `call`. For example, say you wanted to have Sprockets process a `.html` file, and output the mime type as application/javascript. To accomplish this, you would do the following: ```ruby Sprockets.register_transformer 'text/html', 'application/javascript', MyTemplateProcessor ``` ### Compressors A compressor takes in an asset, and returns a smaller version of that asset. For example the uglifier compressor takes in a JavaScript file, it then removes the whitespace and applies other space saving techniques and returns a smaller JavaScript source. Compressors must respond to `call` and return a `:data` key in a hash similar to a processor. You can register a compressor like this: ```ruby Sprockets.register_compressor 'application/javascript', :uglify, UglifierCompressor ``` Registering a compressor allows it to be used later. After registering a compressor you can activate it using the `js_compressor=` or `css_compressor=` method on the environment or Sprockets global. ```ruby Sprockets.register_compressor 'text/css', :my_css, MyCssCompressor Sprockets.css_compressor = :my_css ``` Compressors only operate on JavaScript and CSS. If you want to compress a different type of asset, use a processor (see "Processors" above) to process the asset. ## Exporters An exporter takes a compiled asset and writes it to disk. The default exporters are `FileExporter` which writes an asset's compiled source to disk and `ZlibExporter` that will produce a `.gz` file extension. You can write your own exporter: ``` register_exporter '*/*', Sprockets::Exporters::ZlibExporter ``` First argument is the mime type of files that the exporter will operate on. For your convenience, a `Sprockets::Exporters::Base` class is provided for you to inherit from. Details about the required interface for an exporter are in that class. Your exporter gets initialized once for each asset to be exported by sprockets with the following keyword arguments - asset (Instance of Sprockets::Asset) - environment (Instance of Sprockets::Environment) - directory (Instance of String) A `setup` method is called right after the exporter is initialized. Your exporter is expected implement a `skip?` method. If this method returns true then sprockets will skip your exporter and move to the next one. An instance of `Sprockets::Logger` is passed into this method that can be used to indicate to the user what is happening. This method is called synchronously. The work of writing the new asset to disk is performed in the `call` method. This method is potentially called in a new thread and should not mutate any global state. A `write` method is provided that takes a `filename` to be written to (full path) and yields an IO object. ## Extension Interface A processor is expected to respond to `call()` and it accepts a hash of file contents. It is expected to return a hash that includes a `:data` key. The value returned in the `:data` key will be used as the contents for the file. Any other keys returned will be stored in the `:metadata` hash of an asset. For example, this result: ```ruby class HelloWorldProcessor def call(input) return { data: input[:data] + "\n'hello world'" } end end ``` Would append the string `'hello world'` on a new line to any asset. For Sprockets to call the processor it must be registered. If we wanted this processor to be called with any JavaScript files we would use the JavaScript mime type `application/javascript` and pass in the object that responds to call, in this case an instance of the `HelloWorldProcessor` class: ```ruby Sprockets.register_preprocessor('application/javascript', HelloWorldProcessor.new) ``` To register a processor as a postprocessor instead of a preprocessor, invoke the `register_postprocessor` command instead: ```ruby Sprockets.register_postprocessor('application/javascript', HelloWorldProcessor.new) ``` ### Extension Keys The `call` interface returns a hash. In different versions of Sprockets there may be different keys. This doc is for Sprockets 4 (master). You can see these values being passed into processors [in the Sprockets loader](https://github.com/rails/sprockets/blob/a9b53daaa5404443c0684103b7f83cd5be208575/lib/sprockets/loader.rb#L148-L161). - `:data` - [String] Contains the contents of the file being passed to the your processor. Example: ``` 'var foo = "bar"' ``` - `:uri` - [String] A full URI to the asset, may include custom Sprockets params. Example: ``` "file:///Users/richardschneeman/Documents/projects/sprockets/test/fixtures/default/application.coffee?type=application/javascript", ``` - `:filename` - [String] Full path to asset on disk. Example: ``` "/Users/richardschneeman/Documents/projects/sprockets/test/fixtures/default/gallery.js\" ``` - `:load_path` - [String] The load path that was used to find the asset. Example: ``` "/Users/richardschneeman/Documents/projects/sprockets/test/fixtures/default" ``` - `:name` - [String] The name of the file being loaded without extension. Example ``` "gallery" ``` - `:content_type` - [String] The corresponding mime content type of the asset. Example: ``` "application/javascript" ``` - `:metadata` - [Hash] Extra data, see the "metadata" section. Example: ``` # See metadata section for more info { dependencies: [].to_set map: { # ... } } ``` - `:cache` - [Sprockets::Cache] A cache object you can use to store and retrieve intermediate objects. You can use `Cache#get`, `Cache#set` and `Cache#fetch` api. Refer to method docs for more info. If using paths for the key or contents, use `Sprockets::Environment#compress_from_root` and `Sprockets::Environment#expand_from_root` as the location of of your files absolute path will change. - `:environment` [Sprockets::Environment] Now you have direct access to all 105 methods that Sprockets uses! Use carefully, we may consider limiting this in the future. If you have feedback on what methods you need or use please say hi, Open an issue and let the Sprockets team know. ### Extension Metadata Keys While you can store arbitrary keys in the metadata returned by your extension, there are some with special meaning and uses inside of Sprockets. More may be added in the future. Anything you add to the metadata will be stored in the Sprockets cache for the asset. - map: This key contains a source map for the asset. A source map is a way to tell a browser how to map a generated file to an original for example if you write a CoffeeScript file, Sprockets will generate a JavaScript file which is what the browser will see. If you need to debug this javascript file it helps if you know where the in your original CoffeeScript file the generated JavaScript code came from. The source map tells the browser how to map from a generated file to an original. Sprockets expects this map to follow the [source map spec](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k). - charset: This key contains the mime charset for an asset. A charset is the encoding for text based assets. If you do not specify a charset then one will be automatically assigned by sprockets based on the encoding type of the contents returned in the `:data` key. Normally you want that, the only time you don't want that is if you're working with binary data, or data you don't want to be compressed. If Sprockets sees a `charset` then it will think that the contents of the file are text and can be compressed via GZIP. You can avoid this by setting the field manually. ```ruby return { data: data, charset: nil } ``` WIP the format of the source map may be subject to change before 4.0 is released. Currently it takes a `:original` and `:generated` key which each hold an array of line and column numbers. Line numbers are 1 indexed column numbers are 0 indexed. The first character of a file will always be `[1,0]`. - required: A `Set` of String Asset URIs that the Bundle processor should concatenate together. - stubbed: A `Set` of String Asset URIs that will be omitted from the `:required` set. - links: A `Set` of String Asset URIs that should be compiled along with this asset. - dependencies: A `Set` of String Cache URIs that should be monitored for caching. ### Register Mime Types You can add new mime-types to Sprockets by using the `register_mime_type` method. For example: ```ruby Sprockets.register_mime_type 'application/json', extensions: ['.json'], charset: :unicode Sprockets.register_mime_type 'application/ruby', extensions: ['.rb'] ``` The first method is the mime-type of the object. The `:extensions` key passed to the second argument contains an array of all the extensions for the given mime type. An optional charset can be registered. This should only be used for text based mimetypes. Your extension may have multiple parts for example some people use `.js.coffee` when this file type is used, we do not wish it to process as javascript first and then as coffee script so we register this entire extension: ```ruby Sprockets.register_mime_type 'text/coffeescript', extensions: ['.coffee', '.js.coffee'] ``` ## Adding Directives to your Extension If you are writing a transformer you likely also want it to understand Sprockets directives (such as the ability to `require` other files). You don't have to add this functionality in manually, instead you can register a custom instance of `DirectiveProcessor` to run with your asset. The `DirectiveProcessor` can be initialized with a `:comments` key that holds an array of characters that denote a comment. For example the coffeescript language uses `#` as a comment. We could apply directive support to coffeescript files by registering: ```ruby Sprockets.register_preprocessor 'text/coffeescript', DirectiveProcessor.new(comments: ["#", ["###", "###"]]) ``` ## Adding ERB Support to your Extension In Sprockets 4 file types are no longer "chainable" this means that if you wanted to use a `.coffee.erb` that it must be registered to sprockets explicitly. This is different from previous versions of sprockets where you would have to register only a `.erb` processor and then a `.coffee` processor and sprockets would chain them (first running erb then coffee). The reason for the change is to have more explicit behavior. It helps sprockets know to do the right thing, decreases magic, and increases speed. It also means that as a library maintainer you must tell sprockets all the extensions you want your project to work with. Going with the coffee script example. You would need to register a mime type ## Supporting All Versions of Sprockets in Processors If you are extending sprockets you may want to support all current major versions of sprockets (2, 3, and 4). The processor interface was deprecated from Sprockets 2 and a legacy shim was put into Sprockets 3. Now that Sprockets 4 is out that shim no longer is active, so you'll need to update your gem to either only use the new interface or use both interfaces. For example: ```ruby # Sprockets 2, 3 & 4 interface class MySprocketsExtension def initialize(filename, &block) @filename = filename @source = block.call end def render(context, empty_hash_wtf) self.class.run(@filename, @source, context) end def self.run(filename, source, context) source + "/* Hello From my sprockets extension */" end def self.call(input) filename = input[:filename] source = input[:data] context = input[:environment].context_class.new(input) result = run(filename, source, context) context.metadata.merge(data: result) end end require 'sprockets/processing' extend Sprockets::Processing register_preprocessor 'text/css', MySprocketsExtension ``` This extension is registered to add a comment `/* Hello From my sprockets extension */` to the end of a `text/css` (.css) file. To understand why all of this is needed, we need to look at the different interfaces for Sprockets 2, 3, and 4. Sprockets 2 let you register a processor with a class that would be instantiated and the method `render` called on it you can [view the calling code in Sprockets 2.x](https://github.com/rails/sprockets/blob/2199a6012cc2b9cdbcbc0049361e5ee02770dff0/lib/sprockets/context.rb#L194-L202). It may have looked like this: ```ruby # Sprockets 2.x interface class MySprocketsExtension def initialize(filename, &block) # code end def render(variable, empty_hash_wtf) # code end end require 'sprockets/processing' extend Sprockets::Processing register_preprocessor 'text/css', MySprocketsExtension ``` You can also pass a block to both 2.x and 3.x+ processor interfaces, however the number of args the method takes has changed so it's very hard to do that method and support multiple processor interfaces. This Sprockets 2.x `render` interface is deprecated, instead you should use the `call` interface which was introduced in Sprockets 3.x. Whatever you pass to the processor can have a `call` method, it does not need to be a class. ```ruby # Sprockets 3.x+ interface module MySprocketsExtension def self.call(input) # code result = input[:data] # :data key holds source { data: result } end end require 'sprockets/processing' extend Sprockets::Processing register_preprocessor 'text/css', MySprocketsExtension ``` So if you want 2, 3, and 4 to work you can pass in a class that also has a `call` method on it as well as a `render` instance method. To see how this can be done you can reference this [autoprefixer-rails pull request](https://github.com/ai/autoprefixer-rails/pull/85). This way you're passing in an object that responds to 3.x's `call` interface and 2.x's `new.render` interface. In generally we're recommending people not use Sprockets 2.x and that they upgrade to Sprockets 3+. If it's easier on you, you can rev a major version and only support the new interface. There are new hash keys introduced in Sprockets 4. The `:source_path` key contains the file that will hold the sourcemap of the current asset. ```ruby # Sprockets 3.x+ interface module MySprocketsExtension def self.call(input) file_where_source_map_will_end_up = input[:source_path] # code { data: result } end end ``` If your application is making serious modifications to the source file (an example could be a coffee script file generating JS will be significantly different) then you'll want to calculate and return an appropriate `map` key in the `metadata` hash. See the "metadata" section for more info on doing this. Once you've written your processor to run on all 3 versions of Sprockets you will need to register it. This is covered next. ### Registering All Versions of Sprockets in Processors In Sprockets 2 and 3 the way you registered a processor was via `register_engine`. Unfortunately they have different method signatures. In Sprockets 4 you must first explicitly register a mime type and use the appropriate processor directive i.e. `register_transformer`, `register_preprocessor`, etc. To register a processor for all 3 versions of sprockets you could do it like this: ```ruby # Sprockets 2, 3, and 4 if env.respond_to?(:register_transformer) env.register_mime_type 'text/css', extensions: ['.css'], charset: :css env.register_preprocessor 'text/css', MySprocketsExtension elsif env.respond_to?(:register_engine) args = ['.css', MySprocketsExtension] args << { mime_type: 'text/css', silence_deprecation: true } if Sprockets::VERSION.start_with?("3") env.register_engine(*args) end ``` To understand why this is all needed, we can break down the parts. First is how you register an "engine" with Sprockets 3: ```ruby # Sprockets 3 env.register_engine '.css', MySprocketsExtension, mime_type: 'text/css', silence_deprecation: true ``` The use of `register_engine` is deprecated in Sprockets 3 and you will get a deprecation warning about it's use. We can pass in `silence_deprecation: true` to let Sprockets know that the interface is going away, only do this on code you know works with Sprockets 4. To get the `register_engine` code working with Sprockets 2 we have to do some version detection since Sprockets 2 will error if you try to pass a hash into it: ``` # Sprockets 2 & 3 args = ['.css', MySprocketsExtension] args << { mime_type: 'text/css', silence_deprecation: true } if Sprockets::VERSION.start_with?("3") env.register_engine(*args) ``` Next we see how to do it with Sprockets 4 when `register_engine` is removed: ```ruby # Sprockets 4 env.register_mime_type 'text/css', extensions: ['.css'], charset: :css env.register_preprocessor 'text/css', MySprocketsExtension ``` In reality you wouldn't need to add a mime type for JS and CSS assets since they're already there by default but you will for other filetypes for example `.coffee` or `.scss`. The above `register_mime_type` example is used for example purposes and wouldn't be required. Sprockets 4 will not chain asset extensions so `.coffee.erb` is explicitly registered in addition to `.coffee`. If your application introduces a new mime/extension combo it will be responsible for registering all combinations. ## WIP This guide is a work in progress. There are many different groups of people who interact with Sprockets. Some only need to know directive syntax to put in their asset files, some are building features like the Rails asset pipeline, and some are plugging into Sprockets and writing things like preprocessors. The goal of these guides are to provide task specific guidance to make the expected behavior explicit. If you are using Sprockets and you find missing information in these guides, please consider submitting a pull request with updated information. These guides live in [guides](/guides). sprockets-4.2.1/guides/how_sprockets_works.md000066400000000000000000000547511447572140400215000ustar00rootroot00000000000000# Introduction > This document is based off of a transcript from [RailsConf 2016 - How Sprockets works by Rafael França](https://www.youtube.com/watch?v=CzFFYelG7WY). It's been edited in an attempt to make sense outside the context of a conference talk. [Rafael](https://github.com/rafaelfranca) is amazing and does great work with Sprockets and Rails. Rails is presented in a lot of different ways, it's not just the Rails gem but there are a lot of different components, for example: [Action View](https://github.com/rails/rails/tree/master/actionview), [Spring](https://github.com/rails/spring), [jquery-ujs](https://github.com/rails/jquery-ujs), [Turbolinks](https://github.com/turbolinks/turbolinks-classic), [Sprockets](https://github.com/rails/sprockets). You can see that the Rails framework involves most aspects of your computer and operating system. You have things on the process level like Spring, and also things running in the browser like jquery-ujs and Turbolinks. This goal of this document is to present something that is not well-known. That is, how the asset pipeline of Rails works right now [ca. 2016]. We will talk about why you need the asset pipeline, which gems are responsible for it, how it works in a Rails application, and how to extend the asset pipeline yourself. First, why do we need an asset pipeline? Before we had the asset pipeline in Rails – introduced in Rails 3.1 – we had a question: "Where should I put my assets?". We did not have any established convention for how to handle client-side code in Rails applications. So we typically had to put all of our assets in the `public` folder. Usually we ended up with a lot of files and it became difficult to know if they are being used or not. Rails is about convention over configuration, so that was not something that we should have done with our client-side code. We also have another problem: we had to make some trade-offs between code organization and performance. Browsers had limitations; the Internet was too slow. There are trade offs to make. For example should we create many small self-contained files, or should we do fewer requests for assets in our applications? Initializing an HTTP connection to download an asset is expensive, yet working inside of one really large file is also difficult. Should we write legible code? Or should we transmit fewer bytes to the clients? There were some technologies that were being used in that time, but could not be used easily in Rails applications. Technologies like [CoffeeScript](https://coffeescript.org/), [SASS](https://sass-lang.com/), and [ECMAScript 6](http://es6-features.org/). To solve these problems, the asset pipeline was created. But how does the asset pipeline work in Rails? Right now, we have some conventions for our client-side code. So our assets live in the `app/assets` folder, and there are also `lib/assets` and `vendor/assets` folders. Assets are compiled on-the-fly in development and need to be precompiled in production. We also have asset fingerprinting so the digest of the asset content becomes part of the filename itself to provide automatic cache busting. The asset pipeline is built from a collection of gems: - [sprockets](https://rubygems.org/gems/sprockets/versions/3.5.2) - [sprockets-rails](https://rubygems.org/gems/sprockets-rails) - [sass-rails](https://rubygems.org/gems/sass-rails) - [execjs](https://rubygems.org/gems/execjs) - [coffee-rails](https://rubygems.org/gems/coffee-rails) We will go through each gem and talk about how they work. The first gem that I'm going to talk about is Sprockets. It's the gem that makes it possible to compile and serve all assets. Sprockets defines a processor pipeline so you can extend the way your assets are processed. ## Sprockets Sprockets has some key components that are: - processors - transformers - compressors - directives - environments - manifest - pipelines ### Processors The processors are the most important components in Sprockets. All the functionality inside of Sprockets is implemented by a processor. This is similar to how Railties is also a Rails engine. The interface for a processor is any `call`-able object that accepts an input hash and returns a hash of both data and optional metadata. Example of a minimal Ruby sprockets processor that is just a lambda expression (which is `call`able): ```ruby -> (input) { data = input[:data].gsub(';', '') { data: data } } ``` This is a minimal yet valid Sprockets processor and can be successfully called. It's doing something that is simple and easy to understand, which is just removing semicolons from the end of each line of the input. It takes a Hash as input that has some special keys that we will talk about later. It also returns a hash with specific keys, including `data` which contains the result of the processor running on the input. The input hash has these keys by default: - `:data` - The string contents of the asset - `:environment` - The current `Sprockets::Environment` instance - `:cache` - The `Sprockets::Cache` instance - `:uri` - The asset URI - `:source_path` - The full path to original file - `:load_path` - The current load path for the file - `:name` - The logical name of the file - `:content_type` - The MIME type of the output asset - `:metadata` - The Hash of processor metadata The return hash has these keys: - `:data` - Replaces the assets `input[:data]` for the next processor in the chain - `:required` - A Set of String asset URIs that `Bundle` processor should concatenate together - `:stubbed` - A Set of String asset URIs that will be omitted from the `:required` set - `:links` - A Set of String asset URIs that should be compiled along with the assets - `:dependencies` - A Set of String cache URIs that should be monitored for caching - `:map` - An Array of source maps for the assets - `:charset` - The MIME charset for an asset As we will see later, the `:required` is really interesting. Each dependency from your asset files will be stored in this field. There are a lot of interesting built-in processors including: - `BabelProcessor` - `CoffeScriptProcessor` - `SassProcessor` - `BundlerProcessor` `BundlerProcessor` is used to run concatenated assets rather than individual files. To register a processor in Sprockets, we use this syntax: ```ruby register_bundle_processor 'application/javascript', Bundle register_bundle_processor 'text/css', Bundle ``` We are saying that for any file with MIME type `application/javascript`, we are using the `Bundle` processor to take care of these files and concatenating them in the same file. So the `Bundle` processor takes a single file asset and prepends all the `required` URIs in the contents. ### Transformers A transformer is a processor that converts a file from one format to another format. One of the examples is the `CoffeeScript` transformer that takes a `CoffeeScript` file and returns a JavaScript file. ```ruby register_transformer 'text/coffescript', 'application/javascript', CoffeScriptProcessor ``` The implementation of these processors is really simple as we can see below: ```ruby module CoffeScriptProcessor def self.call(input) data = input[:data] js, map = input[:cache].fetch([self.cache_key, data]) do result = CoffeScript.compile(data, sourceMap: true, sourceFiles: [input[:source_path]]) [result['js'], decode_source_maps(result['v3SourceMap'])] end map = SourceMapUtils.combine_source_maps(input[:metadata][:map]), map) { data: js, map: map } end end ``` We can see that it's a `call`-able object that takes an input and [passes it] through the `CoffeeScript` compiler and returns the result of this operation under the `data` key in the returned hash. ### Compressors Compressors are a special kind of bundle processor because it runs on the concatenated file. You register a compressor using following syntax: ```ruby register_compressor 'application/javascript', :uglify, UglifierCompressor ``` The main difference between the compressor and the bundle processor is compressors are used differently and you can have only one compressor per MIME type. Sprockets uses a special syntax to enable compressors. You can, for instance, compress any JavaScript file using this syntax: ```ruby env.js_compressor = :uglify ``` ### Directives I'm sure you all have seen directives before because they are just special comments that declare your bundlers and their dependencies. This, for instance, is important for the `application.js` file that is generated by a new Rails application. ```js // app/assets/javascripts/application.js //= require jquery //= require jquery-ui //= require users //= require_tree . ``` It's telling us that to generate this `application.js` file, we have to require these three files – `jquery.js`, `jquery-ui.js`, and `users.js` – including all the files inside the same directory of the `application.js` – which is `app/assets/javascripts`. Another special kind of directive that we have in Sprockets version 3 are the precompile lists wherein you are telling Sprockets to precompile these two files in production. ```ruby Rails.application.config.assets.precompile << %w(application.js application.css) ``` Sprockets has special support for `Procs` on the precompilation. Before, in Sprockets version 3, we had this code that is telling us to precompile all the known JavaScript and stylesheet files in the app directory. ```ruby LOOSE_APP_ASSETS = lambda do |logical_path, filename| filename.start_with?(::Rails.root.join('app/assets').to_s) && !['.js', '.css', ''].include?(File.extname(logical_path)) end config.assets.precompile = [LOOSE_APP_ASSETS, /(?:\/|\\|\A)application\.(.css|js)$/] ``` As you can see, the code above is not easy to understand, so in Sprockets version 4, we have a new syntax for that shown below: ```js // app/assets/config/manifest.js //= link_tree ../images //= link_directory ../javascripts .js //= link_directory ../stylesheets .css //= link my_engine // my_engine/app/assets/config/my_engine.js //= link_tree ../images/bootstrap ``` It's called the link directive, so it's easier to understand what's going on there. You can actually see that all the images in the image directory is going to be precompiled just as the JavaScript and the style sheets do. One can use this directive to compose new libraries. I have that link to my engine that's also defining its own manifest file. It's now easy to understand and to compose. Not that we are going to remove the precompile list, but these new directives are there to help to build the precompile list. We have all these directives by default in Sprockets and later we will explain how you can extend the directives to create your own. ### Environment Another component of Sprockets is the environment, and that is actually where your code runs. The environment has methods to retrieve and serve assets, change the load path, and register processors. When you're doing web requests for your asset file, what is going on is that the Sprockets environment is running and it's trying to find that specific file and send it back to you. ```ruby environment = Sprockets::Environment.new environment.find_assets('application.js') ``` The environment is also where you call all those methods that were discussed before, where you can register processors, compressors, and so on. As a part of the environment, we have the manifest that is just a log of the contents of all your precompiled assets in a directory and it is used to do fast lookups without having to actually compile the asset code. ```ruby environment = Sprockets::Environment.new Environment.register_transformer 'image/svg+xml', 'image/svg', SVGTransformer.new ``` The object below is really simple. It points the asset path to the fingerprinted version that's generated by Sprockets. ```ruby javascript_include_tag 'application' # ``` So, when one writes something like `javascript_include_tag 'application'`, what's going on is that Sprockets looks to the manifest to generate the source attribute of this script tag. A sample manifest is shown below that maps the logical path of the file to the filename including the digest: ```ruby { "application.js" => "application-ae0e5a78gfb231d11e07e00ec30g39f0a.js" } ``` In this example, the logical path of the asset – `application.js` – is mapped to the generated file name of the asset including the digest value. In the example below, which is a reverse mapping, further details of the asset are available including mtime, logical path, and so on. ```ruby { "application-2e8e9a7888bdbd11e97effec30214a82.js" => { "logical-path" => "application.js", "mtime" => "2016-06-16T23:09:08-06:00", "digest" => "2e8e9a7888bdbd11e97effec30214a82" } } ``` Another hash is used to expire caches of Sprockets, so you can actually use the same directory and use the assets that you precompiled in the previous deploys. Later you can use this information to expire all the assets that you do not want to be in that folder anymore. There are more things about Sprockets that can be found in the Sprockets documentation and source code, including MIME types, dependency resolvers, transformer suffix, bundle metadata reducer. ## sprockets-rails So a part of Sprockets, the asset pipeline is made by the `sprockets-rails` gem, and as you can guess, all this gem does is to integrate Sprockets to our Rails application so it defines helpers that we use in our application, like the examples below: ```ruby javascript_include_tag stylesheet_link_tag ``` The gem also configures the Sprockets environment without the configurations we have in the config initializer in `/assets`. It also checks the precompile list, which has been a feature since Sprockets version 3. The gem also makes it easier to know when we make mistakes in development, for example by not including an asset in the assets precompile list. For example, if we were to write the following in our template: ```erb <%= javascript_include_tag 'foo' %> ``` This gem makes it possible to raise an exception that is telling you that we need to include that `foo.js` file in the manifest before using it in development. The exception in this example would be `Sprockets::Rails::Helper::AssetNotPrecompiled`. ## sass-rails Another gem that we have is `sass-rails` gem, so like I said before, the `SassProcessor` is built-in into Sprockets itself, but there are some particularities of integrating Sass with Rails that needs to be done in this gem. The gem defines the generators that will be used by the Rails generator when we make a new scaffold that will also generate the corresponding Sass files. It also creates an importer that knows how to handle globs, paths, and ERB to support having something like this in your Sass files: ```scss @import "foo/*" // bar.scss.erb @import "bar" ``` Here we are using glob imports and importing an ERB file, which would not work without the gem. It also configures the Sass processor with all the information we have in our Rails application. ## execjs The third gem is the `execjs` gem. It allows you to run JavaScript code inside the Ruby environment and it uses the JavaScript environment that is available to you in the machine. We have some runtimes that work by default in the gem, like the Node.js environment and the V8 Google interpreter. The JavaScript code can be run directly within the Ruby VM. Using the gem is really simple as you can see from the examples below: ### Examples ```ruby require 'execjs' ExecJS.eval "'red yellow blue'.split(' ')" # => ["red", "yellow", "blue"] ``` ```ruby require 'execjs' require 'open-uri' source = open('https://coffeescript.org/v1/browser-compiler/coffee-script.js').read context = ExecJS.compile(source) context.call('CoffeeScript.compile', 'square = (x) -> x * x', bare: true) # => "var square;\nsquare = function(x) {\n return x * x;\n};" ``` Here we are actually getting the CoffeeScript source code from the CoffeeScript website and compiling CoffeeScript code using Ruby. So as you can see, this gem is used by the coffee-script gem to compile CoffeeScript code to JavaScript. That brings us the the next gem, which is the coffee-rails gem. ## coffee-rails All this gem does is configures generators, so if you don't use generators, you actually don't need the gem. It also defines a template handler so you can call handler CoffeeScript files from your controllers. ### Asset generation in development In development, when you are using the `javascript_include_tag` as below: ```erb <%= javascript_include_tag 'application'%> ``` The method generates the following HTML code that points to the digest version of that file: ```ruby | path | name | suffix | SHA1 of file contents | extension ``` Note that the filename, is composed of the _path_ to the asset, the _name_ of the asset, the asset _suffix_, the SHA1 _digest_ of the file contents, and the _extension_ indicating file type. The `debug` suffix tells Sprockets that the debug pipeline is being used. When Sprockets generates the file in response to a request, it uses the debug pipeline which is defined like this: ```ruby register_pipeline :debug do [SourceMapCommentProcessor] end ``` The pipeline generates your asset, and puts a SourceMapComment in the end of the file. At the end of the JavaScript code, you are going to see a comment that looks something like this: ```js //# sourceMappingURL=application.js-bf4cd805a31db054ae1dr1417f5c8ce72s13468ae23cbdb19d4a3bb010eh11f3.map ``` This is telling your browser how to get all the information about the source code via this source map file. To build the source code of this asset, Sprockets is going to use the default pipeline. The default pipeline is defined as shown below. It's just a small function call inside the Sprockets environment, and what this function call does is check if you have any kind of bundle processor for that MIME type that we are going to handle and use that bundle processor to build the asset. ```ruby register_pipeline :default do |env, type, file_type| env.default_processors_for(type, file_type) end def default_processors_for(type, file_type) bundled_processors = config[:bundle_processors][type] if bundled_processors.any? bundled_processors else self_processors_for(type, file_type) end end ``` For JavaScript, it uses the `bundled_processors` already configured inside Sprockets. In the bundle processor, all of the required files are compiled and merged to compose the final output file. To do this, Sprockets is going to use the _self pipeline_. The self pipeline is defined like this: ```ruby register_pipeline :self do |env, type, file_type| env.self_processors_for(type, file_type) end def self_processors_for(type, file_type) processors = [] processors.concat config[:postprocessors][type] if type != file_type && processor = config[:transformers][file_type][type] processors << processor end processors.concat config[:preprocessors][type] if processors.any? && mime_type_charset_deteceter(type) processors << FileReader end processors end ``` First, it determines the `postprocessors`, `transformers`, and `preprocessors`, for that MIME type. To read the file from the file system, it adds a new processor that is the `FileReader` that reads the file system to get the source code. You can see that it is a pipeline where each component provides output which is used as input for the next component. In the end, the bundler processor merges all of the output and the result is sent back to the browser. This how the asset compilation works in development. The key difference between development and production is that in production, all of this happens in the precompile task and only the resulting static asset is returned to the browser. We can now use this knowledge to extend Sprockets itself. ## Creating new directives We can for instance, create new directives. For example the code below is from a real world use case: ```ruby class NpmDirectiveProcessor < Sprockets::DirectiveProcessor def process_npm_directive(path) dirs = node_modules_paths(@filename) uri, deps = @environment.resolve!( path, accept: @content_type, pipeline: :self, load_paths: dirs ) @dependecies.merge(deps) @required << uri end end ``` We have an `NpmDirectiveProcessor` that goes to your `node_modules_path` and tries to get the dependencies from the Npm installation. We create a new directive processor that is inherited from `Sprockets::DirectiveProcessor`. Sprockets uses a convention that every method that starts with `process` and ends with `directive` is going to be used by directive processor. For example, if you have the `NpmDirectiveProcessor`, the method name will be `process_npm_directive`. After that, we just register that preprocessor for the appropriate MIME type with the appropriate processor. ```ruby register_preprocessor 'application/javascript', 'NpmDirectiveProcessor'.new(comments: ['//', ['/*', '*/']]) ``` Below is an example of loading the `lodash` library via Npm module: ```js // app/assets/javascripts/my_component.js //= npm lodash ``` Another real world example, is were we have a lot of images that are SVG but we also have to support browsers that do not support SVG. So, we have to convert the images from SVG to PNG. That happens automatically in the asset precompiling rake task. All we need to do is register a transformer from SVG to PNG. ```ruby environment.register_transformer 'image/svg+xml', 'image/png', SVGTransformer.new ``` We can use something like the code below when we need to generate `foo.png` from `foo.svg`. If we only have the SVG version in our file system, we can dynamically generate the PNG file on-the-fly from our SVG files in the `../images` folder. An example of that is shown below using the `link` option: ```js // app/assets/config/manifest.js // Given you have foo.svg //= link foo.png // or //= link_tree ../images .png ``` This is the real code. It's just a `call` method that gets the input that is the SVG source code and uses RMagick to generate the PNG file which is returned as a binary blob under the `data` Hash key. ```ruby require 'rmagick' class SvgTransformer def self.call(input) image_list = Magick::Image.from_blob(input[:data]) { self.format = 'SVG' } image = image_list.first image.format = 'PNG' { data: image.to_blob } end end ``` sprockets-4.2.1/guides/source_maps.md000066400000000000000000000307651447572140400177000ustar00rootroot00000000000000# Source Maps This is mostly a guide for developers of Sprockets, contents may change without notice. For a base level description see [What is a source map](https://schneems.com/2017/11/14/wtf-is-a-source-map/). ## What is a source map? In production Sprockets combines files together and minifies them when possible. This makes serving HTTP 1.x traffic faster, but if there is an error in your assets, it becomes very difficult to debug. In Rails asset pipeline it was the convention to not concatenate these files in development, so instead of serving 1 file you might see 10 or so. A source map is a standard that allows assets bundled into one file to declare a "source map" that lets browsers know what code came from what sources. - [Source Map 3 proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit) - [Mozilla source-map library](https://github.com/mozilla/source-map/) So if a source map is used, the exact same method of concatenation and minification in production can be used in development. This encourages developers to use standardized tools that are adopted across browsers to debug their assets. ## Source Map Detection When an asset is served to the browser it lets the browser know if a source map is available by adding a special comment to the bottom. For javascript files with a the comment starts with ```js //# sourceMappingURL= ``` For example a javascript file like application.js was being served from `public/assets/application.js` then it might have a map file in `public/assets/application.js.map`. In that case the comment could either be a full path ```js //# sourceMappingURL=/assets/application.js.map ``` Or it could be relative to the parent's directory: ```js //# sourceMappingURL=application.js.map ``` Css files have a different comment specification ``` /*# sourceMappingURL=application.css.map */ ``` When this comment is served, the browser can make an additional request to that location to get the source map associated with the file ## Encode/Decode Source Map Mozilla maintains a node module that can encode and decode source maps. [Mozilla source-map library](https://github.com/mozilla/source-map/). We are considering this the source implementation against which sprockets can be compared. First you'll need `npm` installed, google it. First we will build a source map using the uglify-js library ``` $ npm install uglify-js uglify-js@2.6.1 node_modules/uglify-js ├── uglify-to-browserify@1.0.2 ├── async@0.2.10 ├── source-map@0.5.3 └── yargs@3.10.0 (decamelize@1.1.1, window-size@0.1.0, camelcase@1.2.1, cliui@2.1.0) ``` Now we need an original javascript file: ``` $ cat foo.js var foo = "foo"; var bar = "bar"; ``` We can run uglifyier on this file to generate a smaller version as well as a source map file by specifying the file name with the `--source-map` flag. ``` $ uglifyjs foo.js --source-map foo.js.map var foo="foo";var bar="bar"; //# sourceMappingURL=foo.js.map ``` Now you can view this file: ``` $ cat foo.js.map { "version": 3, "sources": ["foo.js"], "names": ["foo","bar"], "mappings": "AAAA,GAAIA,KAAM,KACV,IAAIC,KAAM" } ``` Next you'll need to install the `source-map` library ```sh $ npm install source-map source-map@0.5.3 node_modules/source-map ``` now we'll need simple node script that parses this file: ```js var sourceMap = require('source-map'); var fs = require('fs'); fs.readFile('./foo.js.map', 'utf8', function (err, data) { if (err) { return console.log(err); } var smc = new sourceMap.SourceMapConsumer(data); smc.eachMapping(function(m) { console.log(m); }); }); ``` Save this in `read-source-map.js` when you run this file: ``` $ node read-source-map.js { source: 'foo.js', generatedLine: 1, generatedColumn: 0, originalLine: 1, originalColumn: 0, name: null } { source: 'foo.js', generatedLine: 1, generatedColumn: 3, originalLine: 1, originalColumn: 4, name: 'foo' } { source: 'foo.js', generatedLine: 1, generatedColumn: 8, originalLine: 1, originalColumn: 10, name: null } { source: 'foo.js', generatedLine: 1, generatedColumn: 13, originalLine: 2, originalColumn: 0, name: null } { source: 'foo.js', generatedLine: 1, generatedColumn: 17, originalLine: 2, originalColumn: 4, name: 'bar' } { source: 'foo.js', generatedLine: 1, generatedColumn: 22, originalLine: 2, originalColumn: 10, name: null } ``` Each of these correspond to an object in our javascript file. If we look at the `foo` variable: ``` { source: 'foo.js', generatedLine: 1, generatedColumn: 3, originalLine: 1, originalColumn: 4, name: 'foo' } ``` ## Source Map file If we generate a source map for a 1 line javascript file that is not concatenated (it is generated by only one file) we can get a sense of a simple source map. For example if we generate a source map of `foo.js` which has these contents: ```js var foo; ``` Then the resultant `foo.js.map` will be ```js { "version": 3, "sources": ["foo.js"], "names": [ ], "mappings": "AAAA,GAAIA" } ``` - `version` The version of the source map specification we are using. The current is version 3. - `sources` An array of source files, these are the files used to generate `foo.js` if there were more files concatenated we would be expected to see multiple entries here. - `names` Names of functions if available - `mappings` The secret sauce, this includes a VLQ base 64 encoded string that tells the browser how to map lines and locations in the generated file to files, in our case `foo.js` ## Understanding Mappings Mappings are encoded from the version 3 spec. They use a [Variable Length Quantity](https://en.wikipedia.org/wiki/Variable-length_quantity) of Base 64 encoded strings. This allows us to represent arbitrarily large strings. It works like this: ### VLQ Base 64 bit mappings We can represent integers in base64. First we generate an array of valid base64 characters ```ruby BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('') ``` Then we can generate a hash of those characters to their corresponding numeric value ```ruby BASE64_VALUES = (0...64).each_with_object({}) { |i, hash| hash[BASE64_DIGITS[i]] = i } ``` So the value of "A" would be `0` and "9" would be `61`. So now we can represent numbers 0 up to 64 with only one character. Since we need to go higher than 64 digits, the VLQ lets us use a bit inside of the base64 bit value to determine if we continue or stop. In the spec it says that the base64 digit can contain 6 bits of data. The 6th bit is the "continuation" bit which tells us to either stop or keep going. We can determine if a continuation bit is set with bit shifting and masking. So if we have 6 bits representing 1: "000001" it would take us 5 shifts to represent "100000" which would be only the continuation bit. ```ruby VLQ_BASE_SHIFT = 5 ``` We then shift that position onto 1 to determine our mask ```ruby VLQ_CONTINUATION_BIT = 1 << VLQ_BASE_SHIFT ``` From the Ruby 2.2.3 docs this will shift the fixnum on the left (1) by the count positions on the right (VLQ_BASE_SHIFT) which is 5. This generates the number 32. We can verify this is our 6th bit with a little inspection ```ruby VLQ_CONTINUATION_BIT.to_s(2) # => "100000" ``` This works because we `to_s` accepts a base, by passing in a base of 2 we are returning binary representation. Now we can determine if an integer has the continuation bit set by bit masking. Using a [bitwise &](http://ruby-doc.org/core-2.2.3/Fixnum.html#method-i-26) we mask out all the bits to zero except for the first one. If the result returned is 0 it means that the bit is not set and processing should not be continued: ```ruby digit = BASE64_VALUES["A"] digit & VLQ_CONTINUATION_BIT # => 0 ``` So now we know how to detect for continuation bits, but how do we actually use them? In the previous example our mapping returned "AAAA". Since an "A" maps to zero this would generate an array like ``` str = "A" vlq_decode(str) # => [0] str = "AAAA" vlq_decode(str) # => [0, 0, 0, 0] ``` The first character that has its continuation bit set is lowercase "g". A lowercase "g" returns a value of `32`. The value of`vlq_decode("gA")` turns out to be equal to 0. So what would `vlq_decode("gB")` result in? To understand this we need to look a the whole method: ```ruby def vlq_decode(str) result = [] chars = str.split('') while chars.any? vlq = 0 shift = 0 continuation = true while continuation char = chars.shift raise ArgumentError unless char digit = BASE64_VALUES[char] continuation = false if (digit & VLQ_CONTINUATION_BIT) == 0 digit &= VLQ_BASE_MASK vlq += digit << shift shift += VLQ_BASE_SHIFT end result << (vlq & 1 == 1 ? -(vlq >> 1) : vlq >> 1) end result end ``` For the first inner loop we would get a digit of `32` for the character "g". We see the continuation bit is set, so we keep `continuation variable to `true`. We then mask and set the `digit` with `VLQ_BASE_MASK` which is ``` VLQ_BASE_MASK = VLQ_BASE - 1 # => 31 31.to_s(2) # => "111111" ``` So then ```ruby digit &= VLQ_BASE_MASK # => 0 digit.to_s(2) # => "0" # or "000000" ``` Whe then generate a `vl` by shifting the digit with the default value of `shift` which is 0 ``` vlq += digit << shift # => 0 ``` So the value for this iteration would be zero. Finally we update the shift value: ``` shift += VLQ_BASE_SHIFT # => 5 ``` Since continuation is set to true, we go on to the next character "B". ``` digit = BASE64_VALUES["B"] # => 1 continuation = false if (digit & VLQ_CONTINUATION_BIT) == 0 # => false digit &= VLQ_BASE_MASK # => 1 vlq += digit << shift # => 32 shift += VLQ_BASE_SHIFT # => 10 ``` Now we have no more characters and continuation is false. We then add to our result. We use the first bit to check for sign so "000001" is a negative number. Since that is not the case, we shift the value of the vlq to the right so 32 which is "100000" becomes "010000" which is: ``` vlq >> 1 # => [16] ``` This is our result: ``` vlq_decode("gC") # => [16] ``` So what would `vlq_decode("gC")` generate? The first iteration will be the same. ``` digit = BASE64_VALUES["g"] # => 32 continuation = false if (digit & VLQ_CONTINUATION_BIT) == 0 # continuation does not change, still true digit &= VLQ_BASE_MASK # => 0 vlq += digit << shift # => 0 shift += VLQ_BASE_SHIFT # => 5 ``` The second time the only thing that is different with `"C"` is the digit and vlq: ``` digit &= VLQ_BASE_MASK # => 2 vlq += digit << shift # => 64 ``` The vlq `64` does not have it's first bit set so it is positive. We shift this right by 1 and since we lose a bit, we get `32`. ``` vlq_decode("gC") # => [32] ``` So our initial output from `foo.js` is ``` vlq_decode("AAAA") #=> [0, 0, 0, 0] vlq_decode("GAAIA") #=> [3, 0, 0, 4, 0] ``` ## Sprockets Internal Map support Internally sprockets stores maps as hashes that look like this: We need to be able to generate information like ``` { :source=>"example.coffee", :generated=>[6, 2], :original=>[2, 0], :name=>"number" } ``` This would be for the case where `example.coffee` has a value called `number` ``` # Assignment: number = 42 ``` In the original document is on the 2nd line, and it's first character is on the 1st column, so it starts on the 0th column. Compiling this file will generate a coffee script file that starts with this: ``` // Generated by CoffeeScript 1.8.0 (function() { var cubes, list, math, num, number, opposite, race, square, __slice = [].slice; number = 42; # ... ``` You can see that the generated `number` variable gets assigned on the 6th line and the first character is on the 3rd column so it starts on the 2nd column. ## Mapping format The mapping field contains VLQ encoded strings as well as commas "," and semicolons ";" that are used as delimiters. ``` // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, // the next four bits are the actual value, and the 6th bit is the // continuation bit. The continuation bit tells us whether there are more // digits in this value following this digit. // // Continuation // | Sign // | | // V V // 101011 ``` I have no idea The “mappings” data is broken down as follows: - each group representing a line in the generated file is separated by a ”;” - each segment is separated by a “,” - each segment is made up of 1,4 or 5 variable length fields. Confused? I was. Lots of this is raw notes, take with a grain of salt. sprockets-4.2.1/lib/000077500000000000000000000000001447572140400143115ustar00rootroot00000000000000sprockets-4.2.1/lib/rake/000077500000000000000000000000001447572140400152335ustar00rootroot00000000000000sprockets-4.2.1/lib/rake/sprocketstask.rb000066400000000000000000000076741447572140400204760ustar00rootroot00000000000000# frozen_string_literal: true require 'rake' require 'rake/tasklib' require 'sprockets' require 'logger' module Rake # Simple Sprockets compilation Rake task macro. # # Rake::SprocketsTask.new do |t| # t.environment = Sprockets::Environment.new # t.output = "./public/assets" # t.assets = %w( application.js application.css ) # end # class SprocketsTask < Rake::TaskLib # Name of the task. Defaults to "assets". # # The name will also be used to suffix the clean and clobber # tasks, "clean_assets" and "clobber_assets". attr_accessor :name # `Environment` instance used for finding assets. # # You'll most likely want to reassign `environment` to your own. # # Rake::SprocketsTask.new do |t| # t.environment = Foo::Assets # end # def environment if !@environment.is_a?(Sprockets::Base) && @environment.respond_to?(:call) @environment = @environment.call else @environment end end attr_writer :environment # Returns cached cached environment def cached @cached ||= environment.cached if environment end alias_method :index, :cached # `Manifest` instance used for already compiled assets. # # Will be created by default if an environment and output # directory are given def manifest if !@manifest.is_a?(Sprockets::Manifest) && @manifest.respond_to?(:call) @manifest = @manifest.call else @manifest end end attr_writer :manifest # Directory to write compiled assets too. As well as the manifest file. # # t.output = "./public/assets" # attr_accessor :output # Array of asset logical paths to compile. # # t.assets = %w( application.js jquery.js application.css ) # attr_accessor :assets # Minimum number of old assets to keep. See Sprockets::Manifest#clean for more information. attr_accessor :keep # Assets created within this age will be kept. See Sprockets::Manifest#clean for more information. attr_accessor :age # Logger to use during rake tasks. Defaults to using stderr. # # t.logger = Logger.new($stdout) # attr_accessor :logger # Returns logger level Integer. def log_level @logger.level end # Set logger level with constant or symbol. # # t.log_level = Logger::INFO # t.log_level = :debug # def log_level=(level) if level.is_a?(Integer) @logger.level = level else @logger.level = Logger.const_get(level.to_s.upcase) end end def initialize(name = :assets) @name = name @environment = lambda { Sprockets::Environment.new(Dir.pwd) } @manifest = lambda { Sprockets::Manifest.new(cached, output) } @logger = Logger.new($stderr) @logger.level = Logger::INFO @keep = 2 @age = 3600 yield self if block_given? define end # Define tasks def define desc name == :assets ? "Compile assets" : "Compile #{name} assets" task name do with_logger do manifest.compile(assets) end end desc name == :assets ? "Remove all assets" : "Remove all #{name} assets" task "clobber_#{name}" do with_logger do manifest.clobber end end task clobber: ["clobber_#{name}"] desc name == :assets ? "Clean old assets" : "Clean old #{name} assets" task "clean_#{name}" do with_logger do manifest.clean(keep, age) end end task clean: ["clean_#{name}"] end private # Sub out environment logger with our rake task logger that # writes to stderr. def with_logger if env = manifest.environment old_logger = env.logger env.logger = @logger end yield ensure env.logger = old_logger if env end end end sprockets-4.2.1/lib/sprockets.rb000066400000000000000000000237071447572140400166640ustar00rootroot00000000000000# encoding: utf-8 # frozen_string_literal: true require 'sprockets/version' require 'sprockets/cache' require 'sprockets/environment' require 'sprockets/errors' require 'sprockets/manifest' module Sprockets require 'sprockets/processor_utils' extend ProcessorUtils # Extend Sprockets module to provide global registry require 'sprockets/configuration' require 'sprockets/context' require 'digest/sha2' extend Configuration self.config = { bundle_processors: Hash.new { |h, k| [].freeze }.freeze, bundle_reducers: Hash.new { |h, k| {}.freeze }.freeze, compressors: Hash.new { |h, k| {}.freeze }.freeze, dependencies: Set.new.freeze, dependency_resolvers: {}.freeze, digest_class: Digest::SHA256, mime_exts: {}.freeze, mime_types: {}.freeze, paths: [].freeze, pipelines: {}.freeze, pipeline_exts: {}.freeze, postprocessors: Hash.new { |h, k| [].freeze }.freeze, preprocessors: Hash.new { |h, k| [].freeze }.freeze, registered_transformers: [].freeze, root: __dir__.dup.freeze, transformers: Hash.new { |h, k| {}.freeze }.freeze, exporters: Hash.new { |h, k| Set.new.freeze }.freeze, version: "", gzip_enabled: true, export_concurrent: true }.freeze @context_class = Context require 'logger' @logger = Logger.new($stderr) @logger.level = Logger::FATAL # Common asset text types register_mime_type 'application/javascript', extensions: ['.js'], charset: :unicode register_mime_type 'application/json', extensions: ['.json'], charset: :unicode register_mime_type 'application/ruby', extensions: ['.rb'] register_mime_type 'application/xml', extensions: ['.xml'] register_mime_type 'application/manifest+json', extensions: ['.webmanifest'] register_mime_type 'text/css', extensions: ['.css'], charset: :css register_mime_type 'text/html', extensions: ['.html', '.htm'], charset: :html register_mime_type 'text/plain', extensions: ['.txt', '.text'] register_mime_type 'text/yaml', extensions: ['.yml', '.yaml'], charset: :unicode # Common image types register_mime_type 'image/x-icon', extensions: ['.ico'] register_mime_type 'image/bmp', extensions: ['.bmp'] register_mime_type 'image/gif', extensions: ['.gif'] register_mime_type 'image/webp', extensions: ['.webp'] register_mime_type 'image/png', extensions: ['.png'] register_mime_type 'image/jpeg', extensions: ['.jpg', '.jpeg'] register_mime_type 'image/tiff', extensions: ['.tiff', '.tif'] register_mime_type 'image/svg+xml', extensions: ['.svg'] # Common audio/video types register_mime_type 'video/webm', extensions: ['.webm'] register_mime_type 'audio/basic', extensions: ['.snd', '.au'] register_mime_type 'audio/aiff', extensions: ['.aiff'] register_mime_type 'audio/mpeg', extensions: ['.mp3', '.mp2', '.m2a', '.m3a'] register_mime_type 'application/ogg', extensions: ['.ogx'] register_mime_type 'audio/ogg', extensions: ['.ogg', '.oga'] register_mime_type 'audio/midi', extensions: ['.midi', '.mid'] register_mime_type 'video/avi', extensions: ['.avi'] register_mime_type 'audio/wave', extensions: ['.wav', '.wave'] register_mime_type 'video/mp4', extensions: ['.mp4', '.m4v'] register_mime_type 'audio/aac', extensions: ['.aac'] register_mime_type 'audio/mp4', extensions: ['.m4a'] register_mime_type 'audio/flac', extensions: ['.flac'] # Common font types register_mime_type 'application/vnd.ms-fontobject', extensions: ['.eot'] register_mime_type 'application/x-font-opentype', extensions: ['.otf'] register_mime_type 'application/x-font-ttf', extensions: ['.ttf'] register_mime_type 'application/font-woff', extensions: ['.woff'] register_mime_type 'application/font-woff2', extensions: ['.woff2'] require 'sprockets/source_map_processor' register_mime_type 'application/js-sourcemap+json', extensions: ['.js.map'], charset: :unicode register_mime_type 'application/css-sourcemap+json', extensions: ['.css.map'], charset: :unicode register_transformer 'application/javascript', 'application/js-sourcemap+json', SourceMapProcessor register_transformer 'text/css', 'application/css-sourcemap+json', SourceMapProcessor register_pipeline :source do |env| [] end register_pipeline :self do |env, type, file_type| env.self_processors_for(type, file_type) end register_pipeline :default do |env, type, file_type| env.default_processors_for(type, file_type) end require 'sprockets/add_source_map_comment_to_asset_processor' register_pipeline :debug do [AddSourceMapCommentToAssetProcessor] end require 'sprockets/directive_processor' register_preprocessor 'text/css', DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]) register_preprocessor 'application/javascript', DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]) require 'sprockets/bundle' register_bundle_processor 'application/javascript', Bundle register_bundle_processor 'text/css', Bundle register_bundle_metadata_reducer '*/*', :data, proc { +"" }, :concat register_bundle_metadata_reducer 'application/javascript', :data, proc { +"" }, Utils.method(:concat_javascript_sources) register_bundle_metadata_reducer '*/*', :links, :+ register_bundle_metadata_reducer '*/*', :sources, proc { [] }, :+ require 'sprockets/closure_compressor' require 'sprockets/sass_compressor' require 'sprockets/sassc_compressor' require 'sprockets/jsminc_compressor' require 'sprockets/uglifier_compressor' require 'sprockets/yui_compressor' register_compressor 'text/css', :sass, SassCompressor register_compressor 'text/css', :sassc, SasscCompressor register_compressor 'text/css', :scss, SassCompressor register_compressor 'text/css', :scssc, SasscCompressor register_compressor 'text/css', :yui, YUICompressor register_compressor 'application/javascript', :closure, ClosureCompressor register_compressor 'application/javascript', :jsmin, JSMincCompressor register_compressor 'application/javascript', :jsminc, JSMincCompressor register_compressor 'application/javascript', :uglifier, UglifierCompressor register_compressor 'application/javascript', :uglify, UglifierCompressor register_compressor 'application/javascript', :yui, YUICompressor # Babel, TheFuture™ is now require 'sprockets/babel_processor' register_mime_type 'application/ecmascript-6', extensions: ['.es6'], charset: :unicode register_transformer 'application/ecmascript-6', 'application/javascript', BabelProcessor register_preprocessor 'application/ecmascript-6', DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]) # Mmm, CoffeeScript require 'sprockets/coffee_script_processor' register_mime_type 'text/coffeescript', extensions: ['.coffee', '.js.coffee'] register_transformer 'text/coffeescript', 'application/javascript', CoffeeScriptProcessor register_preprocessor 'text/coffeescript', DirectiveProcessor.new(comments: ["#", ["###", "###"]]) # JST processors require 'sprockets/eco_processor' require 'sprockets/ejs_processor' require 'sprockets/jst_processor' register_mime_type 'text/eco', extensions: ['.eco', '.jst.eco'] register_mime_type 'text/ejs', extensions: ['.ejs', '.jst.ejs'] register_transformer 'text/eco', 'application/javascript+function', EcoProcessor register_transformer 'text/ejs', 'application/javascript+function', EjsProcessor register_transformer 'application/javascript+function', 'application/javascript', JstProcessor # CSS processors require 'sprockets/sassc_processor' register_mime_type 'text/sass', extensions: ['.sass', '.css.sass'] register_mime_type 'text/scss', extensions: ['.scss', '.css.scss'] register_transformer 'text/sass', 'text/css', SasscProcessor register_transformer 'text/scss', 'text/css', ScsscProcessor register_preprocessor 'text/sass', DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]) register_preprocessor 'text/scss', DirectiveProcessor.new(comments: ["//", ["/*", "*/"]]) register_bundle_metadata_reducer 'text/css', :sass_dependencies, Set.new, :+ # ERB require 'sprockets/erb_processor' register_transformer_suffix(%w( application/ecmascript-6 application/javascript application/json application/manifest+json application/xml text/coffeescript text/css text/html text/plain text/sass text/scss text/yaml text/eco text/ejs ), 'application/\2+ruby', '.erb', ERBProcessor) register_mime_type 'application/html+ruby', extensions: ['.html.erb', '.erb', '.rhtml'], charset: :html register_mime_type 'application/xml+ruby', extensions: ['.xml.erb', '.rxml'] require 'sprockets/exporters/file_exporter' require 'sprockets/exporters/zlib_exporter' require 'sprockets/exporters/zopfli_exporter' register_exporter '*/*', Exporters::FileExporter register_exporter '*/*', Exporters::ZlibExporter register_dependency_resolver 'environment-version' do |env| env.version end register_dependency_resolver 'environment-paths' do |env| env.paths.map {|path| env.compress_from_root(path) } end register_dependency_resolver 'file-digest' do |env, str| env.file_digest(env.parse_file_digest_uri(str)) end register_dependency_resolver 'processors' do |env, str| env.resolve_processors_cache_key_uri(str) end register_dependency_resolver 'env' do |env, str| _, var = str.split(':', 2) ENV[var] end depend_on 'environment-version' depend_on 'environment-paths' require 'sprockets/preprocessors/default_source_map' register_preprocessor 'text/css', Preprocessors::DefaultSourceMap.new register_preprocessor 'application/javascript', Preprocessors::DefaultSourceMap.new register_bundle_metadata_reducer 'text/css', :map, proc { |input| { "version" => 3, "file" => PathUtils.split_subpath(input[:load_path], input[:filename]), "sections" => [] } }, SourceMapUtils.method(:concat_source_maps) register_bundle_metadata_reducer 'application/javascript', :map, proc { |input| { "version" => 3, "file" => PathUtils.split_subpath(input[:load_path], input[:filename]), "sections" => [] } }, SourceMapUtils.method(:concat_source_maps) end sprockets-4.2.1/lib/sprockets/000077500000000000000000000000001447572140400163265ustar00rootroot00000000000000sprockets-4.2.1/lib/sprockets/add_source_map_comment_to_asset_processor.rb000066400000000000000000000041641447572140400273470ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/uri_utils' require 'sprockets/path_utils' module Sprockets # This is a processor designed to add a source map "comment" # to the bottom of a css or JS file that is serving a source # map. An example of a comment might look like this # # //# application.js-80af0efcc960fc2ac93eda2f7b12e3db40ab360bf6ea269ceed3bea3678326f9.map # # As an asset is built it gets source map information added # to the `asset.to_hash[:metadata][:map]` key. This contains all the # information that is needed to build a source map file. # # To add this comment we must have an asset we can link to. # To do this we ensure that the original asset is loaded, then # we use a use a special mime type. For example `application/js-sourcemap+json` # for a JS source map. # # This will trigger a new asset to be loaded and generated by the # `SourceMapProcessor` processor. # # Finally once we have that file, we can generate a link to it # with it's full fingerprint. This is done and then # added to the original asset as a comment at the bottom. # class AddSourceMapCommentToAssetProcessor def self.call(input) case input[:content_type] when "application/javascript" comment = "\n//# sourceMappingURL=%s" map_type = "application/js-sourcemap+json" when "text/css" comment = "\n/*# sourceMappingURL=%s */" map_type = "application/css-sourcemap+json" else fail input[:content_type] end env = input[:environment] uri, _ = env.resolve!(input[:filename], accept: input[:content_type]) asset = env.load(uri) uri, _ = env.resolve!(input[:filename], accept: map_type) map = env.load(uri) uri, params = URIUtils.parse_asset_uri(input[:uri]) uri = env.expand_from_root(params[:index_alias]) if params[:index_alias] path = PathUtils.relative_path_from(PathUtils.split_subpath(input[:load_path], uri), map.digest_path) asset.metadata.merge( data: asset.source + (comment % path) + "\n", links: asset.links + [asset.uri, map.uri] ) end end end sprockets-4.2.1/lib/sprockets/asset.rb000066400000000000000000000117051447572140400177760ustar00rootroot00000000000000# frozen_string_literal: true require 'fileutils' require 'sprockets/digest_utils' module Sprockets class Asset attr_reader :logical_path # Private: Initialize Asset wrapper from attributes Hash. # # Asset wrappers should not be initialized directly, only # Environment#find_asset should vend them. # # attributes - Hash of ivars # # Returns Asset. def initialize(attributes = {}) @attributes = attributes @content_type = attributes[:content_type] @filename = attributes[:filename] @id = attributes[:id] @load_path = attributes[:load_path] @logical_path = attributes[:logical_path] @metadata = attributes[:metadata] @name = attributes[:name] @source = attributes[:source] @uri = attributes[:uri] end # Internal: Return all internal instance variables as a hash. # # Returns a Hash. def to_hash @attributes end # Public: Metadata accumulated from pipeline process. # # The API status of the keys is dependent on the pipeline processors # itself. So some values maybe considered public and others internal. # See the pipeline processor documentation itself. # # Returns Hash. attr_reader :metadata # Public: Returns String path of asset. attr_reader :filename # Internal: Unique asset object ID. # # Returns a String. attr_reader :id # Public: Internal URI to lookup asset by. # # NOT a publicly accessible URL. # # Returns URI. attr_reader :uri # Public: Return logical path with digest spliced in. # # "foo/bar-37b51d194a7513e45b56f6524f2d51f2.js" # # Returns String. def digest_path if DigestUtils.already_digested?(@name) logical_path else logical_path.sub(/\.(\w+)$/) { |ext| "-#{etag}#{ext}" } end end # Public: Return load path + logical path with digest spliced in. # # Returns String. def full_digest_path File.join(@load_path, digest_path) end # Public: Returns String MIME type of asset. Returns nil if type is unknown. attr_reader :content_type # Public: Get all externally linked asset filenames from asset. # # All linked assets should be compiled anytime this asset is. # # Returns Set of String asset URIs. def links metadata[:links] || Set.new end # Public: Return `String` of concatenated source. # # Returns String. def source if @source @source else # File is read everytime to avoid memory bloat of large binary files File.binread(filename) end end # Public: Alias for #source. # # Returns String. def to_s source end # Public: Get charset of source. # # Returns a String charset name or nil if binary. def charset metadata[:charset] end # Public: Returns Integer length of source. def length metadata[:length] end alias_method :bytesize, :length # Public: Returns String byte digest of source. def digest metadata[:digest] end # Private: Return the version of the environment where the asset was generated. def environment_version metadata[:environment_version] end # Public: Returns String hexdigest of source. def hexdigest DigestUtils.pack_hexdigest(digest) end # Public: ETag String of Asset. def etag version = environment_version if version && version != "" DigestUtils.hexdigest(version + digest) else DigestUtils.pack_hexdigest(digest) end end # Public: Returns String base64 digest of source. def base64digest DigestUtils.pack_base64digest(digest) end # Public: A "named information" URL for subresource integrity. def integrity DigestUtils.integrity_uri(digest) end # Public: Add enumerator to allow `Asset` instances to be used as Rack # compatible body objects. # # block # part - String body chunk # # Returns nothing. def each yield to_s end # Deprecated: Save asset to disk. # # filename - String target # # Returns nothing. def write_to(filename) FileUtils.mkdir_p File.dirname(filename) PathUtils.atomic_write(filename) do |f| f.write source end nil end # Public: Pretty inspect # # Returns String. def inspect "#<#{self.class}:#{object_id.to_s(16)} #{uri.inspect}>" end # Public: Implements Object#hash so Assets can be used as a Hash key or # in a Set. # # Returns Integer hash of the id. def hash id.hash end # Public: Compare assets. # # Assets are equal if they share the same path and digest. # # Returns true or false. def eql?(other) self.class == other.class && self.id == other.id end alias_method :==, :eql? end end sprockets-4.2.1/lib/sprockets/autoload.rb000066400000000000000000000011521447572140400204620ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets module Autoload autoload :Babel, 'sprockets/autoload/babel' autoload :Closure, 'sprockets/autoload/closure' autoload :CoffeeScript, 'sprockets/autoload/coffee_script' autoload :Eco, 'sprockets/autoload/eco' autoload :EJS, 'sprockets/autoload/ejs' autoload :JSMinC, 'sprockets/autoload/jsminc' autoload :Sass, 'sprockets/autoload/sass' autoload :SassC, 'sprockets/autoload/sassc' autoload :Uglifier, 'sprockets/autoload/uglifier' autoload :YUI, 'sprockets/autoload/yui' autoload :Zopfli, 'sprockets/autoload/zopfli' end end sprockets-4.2.1/lib/sprockets/autoload/000077500000000000000000000000001447572140400201365ustar00rootroot00000000000000sprockets-4.2.1/lib/sprockets/autoload/babel.rb000066400000000000000000000001731447572140400215310ustar00rootroot00000000000000# frozen_string_literal: true require 'babel/transpiler' module Sprockets module Autoload Babel = ::Babel end end sprockets-4.2.1/lib/sprockets/autoload/closure.rb000066400000000000000000000001771447572140400221440ustar00rootroot00000000000000# frozen_string_literal: true require 'closure-compiler' module Sprockets module Autoload Closure = ::Closure end end sprockets-4.2.1/lib/sprockets/autoload/coffee_script.rb000066400000000000000000000002061447572140400232740ustar00rootroot00000000000000# frozen_string_literal: true require 'coffee_script' module Sprockets module Autoload CoffeeScript = ::CoffeeScript end end sprockets-4.2.1/lib/sprockets/autoload/eco.rb000066400000000000000000000001521447572140400212270ustar00rootroot00000000000000# frozen_string_literal: true require 'eco' module Sprockets module Autoload Eco = ::Eco end end sprockets-4.2.1/lib/sprockets/autoload/ejs.rb000066400000000000000000000001521447572140400212420ustar00rootroot00000000000000# frozen_string_literal: true require 'ejs' module Sprockets module Autoload EJS = ::EJS end end sprockets-4.2.1/lib/sprockets/autoload/jsminc.rb000066400000000000000000000001621447572140400217450ustar00rootroot00000000000000# frozen_string_literal: true require 'jsminc' module Sprockets module Autoload JSMinC = ::JSMinC end endsprockets-4.2.1/lib/sprockets/autoload/sass.rb000066400000000000000000000001551447572140400214350ustar00rootroot00000000000000# frozen_string_literal: true require 'sass' module Sprockets module Autoload Sass = ::Sass end end sprockets-4.2.1/lib/sprockets/autoload/sassc.rb000066400000000000000000000001601447572140400215740ustar00rootroot00000000000000# frozen_string_literal: true require 'sassc' module Sprockets module Autoload SassC = ::SassC end end sprockets-4.2.1/lib/sprockets/autoload/uglifier.rb000066400000000000000000000001711447572140400222700ustar00rootroot00000000000000# frozen_string_literal: true require 'uglifier' module Sprockets module Autoload Uglifier = ::Uglifier end end sprockets-4.2.1/lib/sprockets/autoload/yui.rb000066400000000000000000000001651447572140400212730ustar00rootroot00000000000000# frozen_string_literal: true require 'yui/compressor' module Sprockets module Autoload YUI = ::YUI end end sprockets-4.2.1/lib/sprockets/autoload/zopfli.rb000066400000000000000000000001251447572140400217640ustar00rootroot00000000000000require 'zopfli' module Sprockets module Autoload Zopfli = ::Zopfli end end sprockets-4.2.1/lib/sprockets/babel_processor.rb000066400000000000000000000032561447572140400220250ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/path_utils' require 'sprockets/source_map_utils' require 'json' module Sprockets class BabelProcessor VERSION = '1' def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key def initialize(options = {}) @options = options.merge({ 'blacklist' => (options['blacklist'] || []) + ['useStrict'], 'sourceMap' => true }).freeze @cache_key = [ self.class.name, Autoload::Babel::Transpiler::VERSION, Autoload::Babel::Source::VERSION, VERSION, @options ].freeze end def call(input) data = input[:data] result = input[:cache].fetch(@cache_key + [input[:filename]] + [data]) do opts = { 'moduleRoot' => nil, 'filename' => input[:filename], 'filenameRelative' => PathUtils.split_subpath(input[:load_path], input[:filename]), 'sourceFileName' => File.basename(input[:filename]), 'sourceMapTarget' => input[:filename] }.merge(@options) if opts['moduleIds'] && opts['moduleRoot'] opts['moduleId'] ||= File.join(opts['moduleRoot'], input[:name]) elsif opts['moduleIds'] opts['moduleId'] ||= input[:name] end Autoload::Babel::Transpiler.transform(data, opts) end map = SourceMapUtils.format_source_map(result["map"], input) map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map) { data: result['code'], map: map } end end end sprockets-4.2.1/lib/sprockets/base.rb000066400000000000000000000100131447572140400175600ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/asset' require 'sprockets/bower' require 'sprockets/cache' require 'sprockets/configuration' require 'sprockets/digest_utils' require 'sprockets/errors' require 'sprockets/loader' require 'sprockets/npm' require 'sprockets/path_dependency_utils' require 'sprockets/path_digest_utils' require 'sprockets/path_utils' require 'sprockets/resolve' require 'sprockets/server' require 'sprockets/source_map_utils' require 'sprockets/uri_tar' module Sprockets class DoubleLinkError < Sprockets::Error def initialize(parent_filename:, logical_path:, last_filename:, filename:) super <<~MSG Multiple files with the same output path cannot be linked (#{logical_path.inspect}) In #{parent_filename.inspect} these files were linked: - #{last_filename} - #{filename} MSG end end # `Base` class for `Environment` and `CachedEnvironment`. class Base include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils include Configuration include Server include Resolve, Loader include Bower include Npm # Get persistent cache store attr_reader :cache # Set persistent cache store # # The cache store must implement a pair of getters and # setters. Either `get(key)`/`set(key, value)`, # `[key]`/`[key]=value`, `read(key)`/`write(key, value)`. def cache=(cache) @cache = Cache.new(cache, logger) end # Return an `CachedEnvironment`. Must be implemented by the subclass. def cached raise NotImplementedError end alias_method :index, :cached # Internal: Compute digest for path. # # path - String filename or directory path. # # Returns a String digest or nil. def file_digest(path) if stat = self.stat(path) # Caveat: Digests are cached by the path's current mtime. Its possible # for a files contents to have changed and its mtime to have been # negligently reset thus appearing as if the file hasn't changed on # disk. Also, the mtime is only read to the nearest second. It's # also possible the file was updated more than once in a given second. key = UnloadedAsset.new(path, self).file_digest_key(stat.mtime.to_i) cache.fetch(key) do self.stat_digest(path, stat) end end end # Find asset by logical path or expanded path. def find_asset(*args, **options) uri, _ = resolve(*args, **options) if uri load(uri) end end def find_all_linked_assets(*args) return to_enum(__method__, *args) unless block_given? parent_asset = asset = find_asset(*args) return unless asset yield asset stack = asset.links.to_a linked_paths = {} while uri = stack.shift yield asset = load(uri) last_filename = linked_paths[asset.logical_path] if last_filename && last_filename != asset.filename raise DoubleLinkError.new( parent_filename: parent_asset.filename, last_filename: last_filename, logical_path: asset.logical_path, filename: asset.filename ) end linked_paths[asset.logical_path] = asset.filename stack = asset.links.to_a + stack end nil end # Preferred `find_asset` shorthand. # # environment['application.js'] # def [](*args, **options) find_asset(*args, **options) end # Find asset by logical path or expanded path. # # If the asset is not found an error will be raised. def find_asset!(*args) uri, _ = resolve!(*args) if uri load(uri) end end # Pretty inspect def inspect "#<#{self.class}:0x#{object_id.to_s(16)} " + "root=#{root.to_s.inspect}, " + "paths=#{paths.inspect}>" end def compress_from_root(uri) URITar.new(uri, self).compress end def expand_from_root(uri) URITar.new(uri, self).expand end end end sprockets-4.2.1/lib/sprockets/bower.rb000066400000000000000000000033241447572140400177730ustar00rootroot00000000000000# frozen_string_literal: true require 'json' module Sprockets module Bower # Internal: All supported bower.json files. # # https://github.com/bower/json/blob/0.4.0/lib/json.js#L7 POSSIBLE_BOWER_JSONS = ['bower.json', 'component.json', '.bower.json'] # Internal: Override resolve_alternates to install bower.json behavior. # # load_path - String environment path # logical_path - String path relative to base # # Returns candidate filenames. def resolve_alternates(load_path, logical_path) candidates, deps = super # bower.json can only be nested one level deep if !logical_path.index('/'.freeze) dirname = File.join(load_path, logical_path) if directory?(dirname) filenames = POSSIBLE_BOWER_JSONS.map { |basename| File.join(dirname, basename) } filename = filenames.detect { |fn| self.file?(fn) } if filename deps << build_file_digest_uri(filename) read_bower_main(dirname, filename) do |path| if file?(path) candidates << path end end end end end return candidates, deps end # Internal: Read bower.json's main directive. # # dirname - String path to component directory. # filename - String path to bower.json. # # Returns nothing. def read_bower_main(dirname, filename) bower = JSON.parse(File.read(filename), create_additions: false) case bower['main'] when String yield File.expand_path(bower['main'], dirname) when Array bower['main'].each do |name| yield File.expand_path(name, dirname) end end end end end sprockets-4.2.1/lib/sprockets/bundle.rb000066400000000000000000000065031447572140400201300ustar00rootroot00000000000000# frozen_string_literal: true require 'set' require 'sprockets/utils' require 'sprockets/uri_utils' module Sprockets # Internal: Bundle processor takes a single file asset and prepends all the # `:required` URIs to the contents. # # Uses pipeline metadata: # # :required - Ordered Set of asset URIs to prepend # :stubbed - Set of asset URIs to subtract from the required set. # # Also see DirectiveProcessor. class Bundle def self.call(input) env = input[:environment] type = input[:content_type] input[:links] ||= Set.new dependencies = Set.new(input[:metadata][:dependencies]) processed_uri, deps = env.resolve(input[:filename], accept: type, pipeline: :self) dependencies.merge(deps) # DirectiveProcessor (and any other transformers called here with pipeline=self) primary_asset = env.load(processed_uri) to_load = primary_asset.metadata.delete(:to_load) || Set.new to_link = primary_asset.metadata.delete(:to_link) || Set.new to_load.each do |uri| loaded_asset = env.load(uri) dependencies.merge(loaded_asset.metadata[:dependencies]) if to_link.include?(uri) primary_metadata = primary_asset.metadata input[:links] << loaded_asset.uri primary_metadata[:links] << loaded_asset.uri end end find_required = proc { |uri| env.load(uri).metadata[:required] } required = Utils.dfs(processed_uri, &find_required) stubbed = Utils.dfs(env.load(processed_uri).metadata[:stubbed], &find_required) required.subtract(stubbed) dedup(required) assets = required.map { |uri| env.load(uri) } (required + stubbed).each do |uri| dependencies.merge(env.load(uri).metadata[:dependencies]) end reducers = Hash[env.match_mime_type_keys(env.config[:bundle_reducers], type).flat_map(&:to_a)] process_bundle_reducers(input, assets, reducers).merge(dependencies: dependencies, included: assets.map(&:uri)) end # Internal: Removes uri from required if it's already included as an alias. # # required - Set of required uris # # Returns deduped set of uris def self.dedup(required) dupes = required.reduce([]) do |r, uri| path, params = URIUtils.parse_asset_uri(uri) if (params.delete(:index_alias)) r << URIUtils.build_asset_uri(path, params) end r end required.subtract(dupes) end # Internal: Run bundle reducers on set of Assets producing a reduced # metadata Hash. # # filename - String bundle filename # assets - Array of Assets # reducers - Array of [initial, reducer_proc] pairs # # Returns reduced asset metadata Hash. def self.process_bundle_reducers(input, assets, reducers) initial = {} reducers.each do |k, (v, _)| if v.respond_to?(:call) initial[k] = v.call(input) elsif !v.nil? initial[k] = v end end assets.reduce(initial) do |h, asset| reducers.each do |k, (_, block)| value = k == :data ? asset.source : asset.metadata[k] if h.key?(k) if !value.nil? h[k] = block.call(h[k], value) end else h[k] = value end end h end end end end sprockets-4.2.1/lib/sprockets/cache.rb000066400000000000000000000167161447572140400177310ustar00rootroot00000000000000# frozen_string_literal: true require 'logger' require 'sprockets/digest_utils' module Sprockets # Public: Wrapper interface to backend cache stores. Ensures a consistent API # even when the backend uses get/set or read/write. # # Public cache interface # # Always assign the backend store instance to Environment#cache=. # # environment.cache = Sprockets::Cache::MemoryStore.new(1000) # # Environment#cache will always return a wrapped Cache interface. See the # methods marked public on this class. # # # Backend cache interface # # The Backend cache store must implement two methods. # # get(key) # # key - An opaque String with a length less than 250 characters. # # Returns an JSON serializable object. # # set(key, value) # # Will only be called once per key. Setting a key "foo" with value "bar", # then later key "foo" with value "baz" is an undefined behavior. # # key - An opaque String with a length less than 250 characters. # value - A JSON serializable object. # # Returns argument value. # # clear(options) # # Clear the entire cache. Be careful with this method since it could # affect other processes if shared cache is being used. # # The options hash is passed to the underlying cache implementation. class Cache # Builtin cache stores. autoload :FileStore, 'sprockets/cache/file_store' autoload :MemoryStore, 'sprockets/cache/memory_store' autoload :NullStore, 'sprockets/cache/null_store' # Internal: Cache key version for this class. Rarely should have to change # unless the cache format radically changes. Will be bump on major version # releases though. VERSION = '4.0.0' def self.default_logger logger = Logger.new($stderr) logger.level = Logger::FATAL logger end # Internal: Wrap a backend cache store. # # Always assign a backend cache store instance to Environment#cache= and # use Environment#cache to retrieve a wrapped interface. # # cache - A compatible backend cache store instance. def initialize(cache = nil, logger = self.class.default_logger) @cache_wrapper = get_cache_wrapper(cache) @fetch_cache = Cache::MemoryStore.new(1024) @logger = logger end # Public: Prefer API to retrieve and set values in the cache store. # # key - JSON serializable key # block - # Must return a consistent JSON serializable object for the given key. # # Examples # # cache.fetch("foo") { "bar" } # # Returns a JSON serializable object. def fetch(key) start = Time.now.to_f expanded_key = expand_key(key) value = @fetch_cache.get(expanded_key) if value.nil? value = @cache_wrapper.get(expanded_key) if value.nil? value = yield @cache_wrapper.set(expanded_key, value) @logger.debug do ms = "(#{((Time.now.to_f - start) * 1000).to_i}ms)" "Sprockets Cache miss #{peek_key(key)} #{ms}" end end @fetch_cache.set(expanded_key, value) end value end # Public: Low level API to retrieve item directly from the backend cache # store. # # This API may be used publicly, but may have undefined behavior # depending on the backend store being used. Prefer the # Cache#fetch API over using this. # # key - JSON serializable key # local - Check local cache first (default: false) # # Returns a JSON serializable object or nil if there was a cache miss. def get(key, local = false) expanded_key = expand_key(key) if local && value = @fetch_cache.get(expanded_key) return value end value = @cache_wrapper.get(expanded_key) @fetch_cache.set(expanded_key, value) if local value end # Public: Low level API to set item directly to the backend cache store. # # This API may be used publicly, but may have undefined behavior # depending on the backend store being used. Prefer the # Cache#fetch API over using this. # # key - JSON serializable key # value - A consistent JSON serializable object for the given key. Setting # a different value for the given key has undefined behavior. # local - Set on local cache (default: false) # # Returns the value argument. def set(key, value, local = false) expanded_key = expand_key(key) @fetch_cache.set(expanded_key, value) if local @cache_wrapper.set(expanded_key, value) end # Public: Pretty inspect # # Returns String. def inspect "#<#{self.class} local=#{@fetch_cache.inspect} store=#{@cache_wrapper.cache.inspect}>" end # Public: Clear cache # # Returns truthy on success, potentially raises exception on failure def clear(options=nil) @cache_wrapper.clear @fetch_cache.clear end private # Internal: Expand object cache key into a short String key. # # The String should be under 250 characters so its compatible with # Memcache. # # key - JSON serializable key # # Returns a String with a length less than 250 characters. def expand_key(key) digest_key = DigestUtils.pack_urlsafe_base64digest(DigestUtils.digest(key)) namespace = digest_key[0, 2] "sprockets/v#{VERSION}/#{namespace}/#{digest_key}" end PEEK_SIZE = 100 # Internal: Show first 100 characters of cache key for logging purposes. # # Returns a String with a length less than 100 characters. def peek_key(key) case key when Integer key.to_s when String key[0, PEEK_SIZE].inspect when Array str = [] key.each { |k| str << peek_key(k) } str.join(':')[0, PEEK_SIZE] else peek_key(DigestUtils.pack_urlsafe_base64digest(DigestUtils.digest(key))) end end def get_cache_wrapper(cache) if cache.is_a?(Cache) cache # `Cache#get(key)` for Memcache elsif cache.respond_to?(:get) GetWrapper.new(cache) # `Cache#[key]` so `Hash` can be used elsif cache.respond_to?(:[]) HashWrapper.new(cache) # `Cache#read(key)` for `ActiveSupport::Cache` support elsif cache.respond_to?(:read) ReadWriteWrapper.new(cache) else cache = Sprockets::Cache::NullStore.new GetWrapper.new(cache) end end class Wrapper < Struct.new(:cache) end class GetWrapper < Wrapper def get(key) cache.get(key) end def set(key, value) cache.set(key, value) end def clear(options=nil) # dalli has a #flush method so try it if cache.respond_to?(:flush) cache.flush(options) else cache.clear(options) end true end end class HashWrapper < Wrapper def get(key) cache[key] end def set(key, value) cache[key] = value end def clear(options=nil) cache.clear true end end class ReadWriteWrapper < Wrapper def get(key) cache.read(key) end def set(key, value) cache.write(key, value) end def clear(options=nil) cache.clear(options) true end end end end sprockets-4.2.1/lib/sprockets/cache/000077500000000000000000000000001447572140400173715ustar00rootroot00000000000000sprockets-4.2.1/lib/sprockets/cache/file_store.rb000066400000000000000000000134721447572140400220600ustar00rootroot00000000000000# frozen_string_literal: true require 'fileutils' require 'logger' require 'sprockets/encoding_utils' require 'sprockets/path_utils' require 'zlib' module Sprockets class Cache # Public: A file system cache store that automatically cleans up old keys. # # Assign the instance to the Environment#cache. # # environment.cache = Sprockets::Cache::FileStore.new("/tmp") # # See Also # # ActiveSupport::Cache::FileStore # class FileStore # Internal: Default key limit for store. DEFAULT_MAX_SIZE = 25 * 1024 * 1024 EXCLUDED_DIRS = ['.', '..'].freeze GITKEEP_FILES = ['.gitkeep', '.keep'].freeze # Internal: Default standard error fatal logger. # # Returns a Logger. def self.default_logger logger = Logger.new($stderr) logger.level = Logger::FATAL logger end # Public: Initialize the cache store. # # root - A String path to a directory to persist cached values to. # max_size - A Integer of the maximum size the store will hold (in bytes). # (default: 25MB). # logger - The logger to which some info will be printed. # (default logger level is FATAL and won't output anything). def initialize(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger) @root = root @max_size = max_size @gc_size = max_size * 0.75 @logger = logger end # Public: Retrieve value from cache. # # This API should not be used directly, but via the Cache wrapper API. # # key - String cache key. # # Returns Object or nil or the value is not set. def get(key) path = File.join(@root, "#{key}.cache") value = safe_open(path) do |f| begin EncodingUtils.unmarshaled_deflated(f.read, Zlib::MAX_WBITS) rescue Exception => e @logger.error do "#{self.class}[#{path}] could not be unmarshaled: " + "#{e.class}: #{e.message}" end nil end end if value FileUtils.touch(path) value end end # Public: Set a key and value in the cache. # # This API should not be used directly, but via the Cache wrapper API. # # key - String cache key. # value - Object value. # # Returns Object value. def set(key, value) path = File.join(@root, "#{key}.cache") # Ensure directory exists FileUtils.mkdir_p File.dirname(path) # Check if cache exists before writing exists = File.exist?(path) # Serialize value marshaled = Marshal.dump(value) # Compress if larger than 4KB if marshaled.bytesize > 4 * 1024 deflater = Zlib::Deflate.new( Zlib::BEST_COMPRESSION, Zlib::MAX_WBITS, Zlib::MAX_MEM_LEVEL, Zlib::DEFAULT_STRATEGY ) deflater << marshaled raw = deflater.finish else raw = marshaled end # Write data PathUtils.atomic_write(path) do |f| f.write(raw) @size = size + f.size unless exists end # GC if necessary gc! if size > @max_size value end # Public: Pretty inspect # # Returns String. def inspect "#<#{self.class} size=#{size}/#{@max_size}>" end # Public: Clear the cache # # adapted from ActiveSupport::Cache::FileStore#clear # # Deletes all items from the cache. In this case it deletes all the entries in the specified # file store directory except for .keep or .gitkeep. Be careful which directory is specified # as @root because everything in that directory will be deleted. # # Returns true def clear(options=nil) if File.exist?(@root) root_dirs = Dir.entries(@root).reject { |f| (EXCLUDED_DIRS + GITKEEP_FILES).include?(f) } FileUtils.rm_r(root_dirs.collect{ |f| File.join(@root, f) }) end true end private # Internal: Get all cache files along with stats. # # Returns an Array of [String filename, File::Stat] pairs sorted by # mtime. def find_caches Dir.glob(File.join(@root, '**/*.cache')).reduce([]) { |stats, filename| stat = safe_stat(filename) # stat maybe nil if file was removed between the time we called # dir.glob and the next stat stats << [filename, stat] if stat stats }.sort_by { |_, stat| stat.mtime.to_i } end def size @size ||= compute_size(find_caches) end def compute_size(caches) caches.inject(0) { |sum, (_, stat)| sum + stat.size } end def safe_stat(fn) File.stat(fn) rescue Errno::ENOENT nil end def safe_open(path, &block) if File.exist?(path) File.open(path, 'rb', &block) end rescue Errno::ENOENT end def gc! start_time = Time.now caches = find_caches size = compute_size(caches) delete_caches, keep_caches = caches.partition { |filename, stat| deleted = size > @gc_size size -= stat.size deleted } return if delete_caches.empty? FileUtils.remove(delete_caches.map(&:first), force: true) @size = compute_size(keep_caches) @logger.warn do secs = Time.now.to_f - start_time.to_f "#{self.class}[#{@root}] garbage collected " + "#{delete_caches.size} files (#{(secs * 1000).to_i}ms)" end end end end end sprockets-4.2.1/lib/sprockets/cache/memory_store.rb000066400000000000000000000037631447572140400224530ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets class Cache # Public: Basic in memory LRU cache. # # Assign the instance to the Environment#cache. # # environment.cache = Sprockets::Cache::MemoryStore.new(1000) # # See Also # # ActiveSupport::Cache::MemoryStore # class MemoryStore # Internal: Default key limit for store. DEFAULT_MAX_SIZE = 1000 # Public: Initialize the cache store. # # max_size - A Integer of the maximum number of keys the store will hold. # (default: 1000). def initialize(max_size = DEFAULT_MAX_SIZE) @max_size = max_size @cache = {} @mutex = Mutex.new end # Public: Retrieve value from cache. # # This API should not be used directly, but via the Cache wrapper API. # # key - String cache key. # # Returns Object or nil or the value is not set. def get(key) @mutex.synchronize do exists = true value = @cache.delete(key) { exists = false } if exists @cache[key] = value else nil end end end # Public: Set a key and value in the cache. # # This API should not be used directly, but via the Cache wrapper API. # # key - String cache key. # value - Object value. # # Returns Object value. def set(key, value) @mutex.synchronize do @cache.delete(key) @cache[key] = value @cache.shift if @cache.size > @max_size end value end # Public: Pretty inspect # # Returns String. def inspect @mutex.synchronize do "#<#{self.class} size=#{@cache.size}/#{@max_size}>" end end # Public: Clear the cache # # Returns true def clear(options=nil) @mutex.synchronize do @cache.clear end true end end end end sprockets-4.2.1/lib/sprockets/cache/null_store.rb000066400000000000000000000022711447572140400221060ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets class Cache # Public: A compatible cache store that doesn't store anything. Used by # default when no Environment#cache is configured. # # Assign the instance to the Environment#cache. # # environment.cache = Sprockets::Cache::NullStore.new # # See Also # # ActiveSupport::Cache::NullStore # class NullStore # Public: Simulate a cache miss. # # This API should not be used directly, but via the Cache wrapper API. # # key - String cache key. # # Returns nil. def get(key) nil end # Public: Simulate setting a value in the cache. # # This API should not be used directly, but via the Cache wrapper API. # # key - String cache key. # value - Object value. # # Returns Object value. def set(key, value) value end # Public: Pretty inspect # # Returns String. def inspect "#<#{self.class}>" end # Public: Simulate clearing the cache # # Returns true def clear(options=nil) true end end end end sprockets-4.2.1/lib/sprockets/cached_environment.rb000066400000000000000000000035151447572140400225120ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/base' module Sprockets # `CachedEnvironment` is a special cached version of `Environment`. # # The exception is that all of its file system methods are cached # for the instances lifetime. This makes `CachedEnvironment` much faster. This # behavior is ideal in production environments where the file system # is immutable. # # `CachedEnvironment` should not be initialized directly. Instead use # `Environment#cached`. class CachedEnvironment < Base def initialize(environment) initialize_configuration(environment) @cache = environment.cache @stats = Concurrent::Map.new @entries = Concurrent::Map.new @uris = Concurrent::Map.new @processor_cache_keys = Concurrent::Map.new @resolved_dependencies = Concurrent::Map.new end # No-op return self as cached environment. def cached self end alias_method :index, :cached # Internal: Cache Environment#entries def entries(path) @entries.fetch_or_store(path) { super(path) } end # Internal: Cache Environment#stat def stat(path) @stats.fetch_or_store(path) { super(path) } end # Internal: Cache Environment#load def load(uri) @uris.fetch_or_store(uri) { super(uri) } end # Internal: Cache Environment#processor_cache_key def processor_cache_key(str) @processor_cache_keys.fetch_or_store(str) { super(str) } end # Internal: Cache Environment#resolve_dependency def resolve_dependency(str) @resolved_dependencies.fetch_or_store(str) { super(str) } end private # Cache is immutable, any methods that try to change the runtime config # should bomb. def config=(config) raise RuntimeError, "can't modify immutable cached environment" end end end sprockets-4.2.1/lib/sprockets/closure_compressor.rb000066400000000000000000000022721447572140400226060ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/digest_utils' module Sprockets # Public: Closure Compiler minifier. # # To accept the default options # # environment.register_bundle_processor 'application/javascript', # Sprockets::ClosureCompressor # # Or to pass options to the Closure::Compiler class. # # environment.register_bundle_processor 'application/javascript', # Sprockets::ClosureCompressor.new({ ... }) # class ClosureCompressor VERSION = '1' # Public: Return singleton instance with default options. # # Returns ClosureCompressor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key def initialize(options = {}) @options = options @cache_key = "#{self.class.name}:#{Autoload::Closure::VERSION}:#{Autoload::Closure::COMPILER_VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze end def call(input) @compiler ||= Autoload::Closure::Compiler.new(@options) @compiler.compile(input[:data]) end end end sprockets-4.2.1/lib/sprockets/coffee_script_processor.rb000066400000000000000000000021021447572140400235600ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/source_map_utils' module Sprockets # Processor engine class for the CoffeeScript compiler. # Depends on the `coffee-script` and `coffee-script-source` gems. # # For more information see: # # https://github.com/rails/ruby-coffee-script # module CoffeeScriptProcessor VERSION = '2' def self.cache_key @cache_key ||= "#{name}:#{Autoload::CoffeeScript::Source.version}:#{VERSION}".freeze end def self.call(input) data = input[:data] js, map = input[:cache].fetch([self.cache_key, data, input[:filename]]) do result = Autoload::CoffeeScript.compile( data, sourceMap: "v3", sourceFiles: [File.basename(input[:filename])], generatedFile: input[:filename] ) [result['js'], JSON.parse(result['v3SourceMap'])] end map = SourceMapUtils.format_source_map(map, input) map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map) { data: js, map: map } end end end sprockets-4.2.1/lib/sprockets/compressing.rb000066400000000000000000000101121447572140400211770ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/utils' module Sprockets # `Compressing` is an internal mixin whose public methods are exposed on # the `Environment` and `CachedEnvironment` classes. module Compressing include Utils def compressors config[:compressors] end # Public: Register a new compressor `klass` at `sym` for `mime_type`. # # Registering a processor allows it to be looked up by `sym` later when # assigning a JavaScript or CSS compressor. # # Compressors only operate on JavaScript and CSS. If you want to compress a # different type of asset, use a processor instead. # # Examples # # register_compressor 'text/css', :my_sass, MySassCompressor # css_compressor = :my_sass # # mime_type - String MIME Type (one of: 'test/css' or 'application/javascript'). # sym - Symbol registration address. # klass - The compressor class. # # Returns nothing. def register_compressor(mime_type, sym, klass) self.config = hash_reassoc(config, :compressors, mime_type) do |compressors| compressors[sym] = klass compressors end end # Return CSS compressor or nil if none is set def css_compressor if defined? @css_compressor @css_compressor end end # Assign a compressor to run on `text/css` assets. # # The compressor object must respond to `compress`. def css_compressor=(compressor) unregister_bundle_processor 'text/css', @css_compressor if defined? @css_compressor @css_compressor = nil return unless compressor if compressor.is_a?(Symbol) @css_compressor = klass = config[:compressors]['text/css'][compressor] || raise(Error, "unknown compressor: #{compressor}") elsif compressor.respond_to?(:compress) klass = proc { |input| compressor.compress(input[:data]) } @css_compressor = :css_compressor else @css_compressor = klass = compressor end register_bundle_processor 'text/css', klass end # Return JS compressor or nil if none is set def js_compressor if defined? @js_compressor @js_compressor end end # Assign a compressor to run on `application/javascript` assets. # # The compressor object must respond to `compress`. def js_compressor=(compressor) unregister_bundle_processor 'application/javascript', @js_compressor if defined? @js_compressor @js_compressor = nil return unless compressor if compressor.is_a?(Symbol) @js_compressor = klass = config[:compressors]['application/javascript'][compressor] || raise(Error, "unknown compressor: #{compressor}") elsif compressor.respond_to?(:compress) klass = proc { |input| compressor.compress(input[:data]) } @js_compressor = :js_compressor else @js_compressor = klass = compressor end register_bundle_processor 'application/javascript', klass end # Public: Checks if Gzip is enabled. def gzip? config[:gzip_enabled] end # Public: Checks if Gzip is disabled. def skip_gzip? !gzip? end # Public: Enable or disable the creation of Gzip files. # # To disable gzip generation set to a falsey value: # # environment.gzip = false # # To enable set to a truthy value. By default zlib wil # be used to gzip assets. If you have the Zopfli gem # installed you can specify the zopfli algorithm to be used # instead: # # environment.gzip = :zopfli # def gzip=(gzip) self.config = config.merge(gzip_enabled: gzip).freeze case gzip when false, nil self.unregister_exporter Exporters::ZlibExporter self.unregister_exporter Exporters::ZopfliExporter when :zopfli self.unregister_exporter Exporters::ZlibExporter self.register_exporter '*/*', Exporters::ZopfliExporter else self.unregister_exporter Exporters::ZopfliExporter self.register_exporter '*/*', Exporters::ZlibExporter end gzip end end end sprockets-4.2.1/lib/sprockets/configuration.rb000066400000000000000000000042471447572140400215310ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/compressing' require 'sprockets/dependencies' require 'sprockets/mime' require 'sprockets/paths' require 'sprockets/processing' require 'sprockets/exporting' require 'sprockets/transformers' require 'sprockets/utils' module Sprockets module Configuration include Paths, Mime, Transformers, Processing, Exporting, Compressing, Dependencies, Utils def initialize_configuration(parent) @config = parent.config @logger = parent.logger @context_class = Class.new(parent.context_class) end attr_reader :config def config=(config) raise TypeError, "can't assign mutable config" unless config.frozen? @config = config end # Get and set `Logger` instance. attr_accessor :logger # The `Environment#version` is a custom value used for manually # expiring all asset caches. # # Sprockets is able to track most file and directory changes and # will take care of expiring the cache for you. However, its # impossible to know when any custom helpers change that you mix # into the `Context`. # # It would be wise to increment this value anytime you make a # configuration change to the `Environment` object. def version config[:version] end # Assign an environment version. # # environment.version = '2.0' # def version=(version) self.config = hash_reassoc(config, :version) { version.dup } end # Public: Returns a `Digest` implementation class. # # Defaults to `Digest::SHA256`. def digest_class config[:digest_class] end # Deprecated: Assign a `Digest` implementation class. This maybe any Ruby # `Digest::` implementation such as `Digest::SHA256` or # `Digest::SHA512`. # # environment.digest_class = Digest::SHA512 # def digest_class=(klass) self.config = config.merge(digest_class: klass).freeze end # This class maybe mutated and mixed in with custom helpers. # # environment.context_class.instance_eval do # include MyHelpers # def asset_url; end # end # attr_reader :context_class end end sprockets-4.2.1/lib/sprockets/context.rb000066400000000000000000000215661447572140400203510ustar00rootroot00000000000000# frozen_string_literal: true require 'rack/utils' require 'set' require 'sprockets/errors' require 'delegate' module Sprockets # They are typically accessed by ERB templates. You can mix in custom helpers # by injecting them into `Environment#context_class`. Do not mix them into # `Context` directly. # # environment.context_class.class_eval do # include MyHelper # def asset_url; end # end # # <%= asset_url "foo.png" %> # # The `Context` also collects dependencies declared by # assets. See `DirectiveProcessor` for an example of this. class Context # Internal: Proxy for ENV that keeps track of the environment variables used class ENVProxy < SimpleDelegator def initialize(context) @context = context super(ENV) end def [](key) @context.depend_on_env(key) super end def fetch(key, *) @context.depend_on_env(key) super end end attr_reader :environment, :filename def initialize(input) @environment = input[:environment] @metadata = input[:metadata] @load_path = input[:load_path] @logical_path = input[:name] @filename = input[:filename] @dirname = File.dirname(@filename) @content_type = input[:content_type] @required = Set.new(@metadata[:required]) @stubbed = Set.new(@metadata[:stubbed]) @links = Set.new(@metadata[:links]) @dependencies = Set.new(input[:metadata][:dependencies]) end def metadata { required: @required, stubbed: @stubbed, links: @links, dependencies: @dependencies } end def env_proxy ENVProxy.new(self) end # Returns the environment path that contains the file. # # If `app/javascripts` and `app/stylesheets` are in your path, and # current file is `app/javascripts/foo/bar.js`, `load_path` would # return `app/javascripts`. attr_reader :load_path alias_method :root_path, :load_path # Returns logical path without any file extensions. # # 'app/javascripts/application.js' # # => 'application' # attr_reader :logical_path # Returns content type of file # # 'application/javascript' # 'text/css' # attr_reader :content_type # Public: Given a logical path, `resolve` will find and return an Asset URI. # Relative paths will also be resolved. An accept type maybe given to # restrict the search. # # resolve("foo.js") # # => "file:///path/to/app/javascripts/foo.js?type=application/javascript" # # resolve("./bar.js") # # => "file:///path/to/app/javascripts/bar.js?type=application/javascript" # # path - String logical or absolute path # accept - String content accept type # # Returns an Asset URI String. def resolve(path, **kargs) kargs[:base_path] = @dirname uri, deps = environment.resolve!(path, **kargs) @dependencies.merge(deps) uri end # Public: Load Asset by AssetURI and track it as a dependency. # # uri - AssetURI # # Returns Asset. def load(uri) asset = environment.load(uri) @dependencies.merge(asset.metadata[:dependencies]) asset end # `depend_on` allows you to state a dependency on a file without # including it. # # This is used for caching purposes. Any changes made to # the dependency file will invalidate the cache of the # source file. def depend_on(path) if environment.absolute_path?(path) && environment.stat(path) @dependencies << environment.build_file_digest_uri(path) else resolve(path) end nil end # `depend_on_asset` allows you to state an asset dependency # without including it. # # This is used for caching purposes. Any changes that would # invalidate the dependency asset will invalidate the source # file. Unlike `depend_on`, this will recursively include # the target asset's dependencies. def depend_on_asset(path) load(resolve(path)) end # `depend_on_env` allows you to state a dependency on an environment # variable. # # This is used for caching purposes. Any changes in the value of the # environment variable will invalidate the cache of the source file. def depend_on_env(key) @dependencies << "env:#{key}" end # `require_asset` declares `path` as a dependency of the file. The # dependency will be inserted before the file and will only be # included once. # # If ERB processing is enabled, you can use it to dynamically # require assets. # # <%= require_asset "#{framework}.js" %> # def require_asset(path) @required << resolve(path, accept: @content_type, pipeline: :self) nil end # `stub_asset` blacklists `path` from being included in the bundle. # `path` must be an asset which may or may not already be included # in the bundle. def stub_asset(path) @stubbed << resolve(path, accept: @content_type, pipeline: :self) nil end # `link_asset` declares an external dependency on an asset without directly # including it. The target asset is returned from this function making it # easy to construct a link to it. # # Returns an Asset or nil. def link_asset(path) asset = depend_on_asset(path) @links << asset.uri asset end # Returns a `data:` URI with the contents of the asset at the specified # path, and marks that path as a dependency of the current file. # # Uses URI encoding for SVG files, base64 encoding for all the other files. # # Use `asset_data_uri` from ERB with CSS or JavaScript assets: # # #logo { background: url(<%= asset_data_uri 'logo.png' %>) } # # $('').attr('src', '<%= asset_data_uri 'avatar.jpg' %>') # def asset_data_uri(path) asset = depend_on_asset(path) if asset.content_type == 'image/svg+xml' svg_asset_data_uri(asset) else base64_asset_data_uri(asset) end end # Expands logical path to full url to asset. # # NOTE: This helper is currently not implemented and should be # customized by the application. Though, in the future, some # basic implementation may be provided with different methods that # are required to be overridden. def asset_path(path, options = {}) message = <<-EOS Custom asset_path helper is not implemented Extend your environment context with a custom method. environment.context_class.class_eval do def asset_path(path, options = {}) end end EOS raise NotImplementedError, message end # Expand logical image asset path. def image_path(path) asset_path(path, type: :image) end # Expand logical video asset path. def video_path(path) asset_path(path, type: :video) end # Expand logical audio asset path. def audio_path(path) asset_path(path, type: :audio) end # Expand logical font asset path. def font_path(path) asset_path(path, type: :font) end # Expand logical javascript asset path. def javascript_path(path) asset_path(path, type: :javascript) end # Expand logical stylesheet asset path. def stylesheet_path(path) asset_path(path, type: :stylesheet) end protected # Returns a URI-encoded data URI (always "-quoted). def svg_asset_data_uri(asset) svg = asset.source.dup optimize_svg_for_uri_escaping!(svg) data = Rack::Utils.escape(svg) optimize_quoted_uri_escapes!(data) "\"data:#{asset.content_type};charset=utf-8,#{data}\"" end # Returns a Base64-encoded data URI. def base64_asset_data_uri(asset) data = Rack::Utils.escape(EncodingUtils.base64(asset.source)) "data:#{asset.content_type};base64,#{data}" end # Optimizes an SVG for being URI-escaped. # # This method only performs these basic but crucial optimizations: # * Replaces " with ', because ' does not need escaping. # * Removes comments, meta, doctype, and newlines. # * Collapses whitespace. def optimize_svg_for_uri_escaping!(svg) # Remove comments, xml meta, and doctype svg.gsub!(/|<\?.*?\?>|/m, '') # Replace consecutive whitespace and newlines with a space svg.gsub!(/\s+/, ' ') # Collapse inter-tag whitespace svg.gsub!('> <', '><') # Replace " with ' svg.gsub!(/([\w:])="(.*?)"/, "\\1='\\2'") svg.strip! end # Un-escapes characters in the given URI-escaped string that do not need # escaping in "-quoted data URIs. def optimize_quoted_uri_escapes!(escaped) escaped.gsub!('%3D', '=') escaped.gsub!('%3A', ':') escaped.gsub!('%2F', '/') escaped.gsub!('%27', "'") escaped.tr!('+', ' ') end end end sprockets-4.2.1/lib/sprockets/dependencies.rb000066400000000000000000000036311447572140400213040ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/digest_utils' require 'sprockets/path_digest_utils' require 'sprockets/uri_utils' module Sprockets # `Dependencies` is an internal mixin whose public methods are exposed on the # `Environment` and `CachedEnvironment` classes. module Dependencies include DigestUtils, PathDigestUtils, URIUtils # Public: Mapping dependency schemes to resolver functions. # # key - String scheme # value - Proc.call(Environment, String) # # Returns Hash. def dependency_resolvers config[:dependency_resolvers] end # Public: Default set of dependency URIs for assets. # # Returns Set of String URIs. def dependencies config[:dependencies] end # Public: Register new dependency URI resolver. # # scheme - String scheme # block - # environment - Environment # uri - String dependency URI # # Returns nothing. def register_dependency_resolver(scheme, &block) self.config = hash_reassoc(config, :dependency_resolvers) do |hash| hash.merge(scheme => block) end end # Public: Add environmental dependency inherited by all assets. # # uri - String dependency URI # # Returns nothing. def add_dependency(uri) self.config = hash_reassoc(config, :dependencies) do |set| set + Set.new([uri]) end end alias_method :depend_on, :add_dependency # Internal: Resolve dependency URIs. # # Returns resolved Object. def resolve_dependency(str) # Optimize for the most common scheme to # save 22k allocations on an average Spree app. scheme = if str.start_with?('file-digest:'.freeze) 'file-digest'.freeze else str[/([^:]+)/, 1] end if resolver = config[:dependency_resolvers][scheme] resolver.call(self, str) else nil end end end end sprockets-4.2.1/lib/sprockets/digest_utils.rb000066400000000000000000000124731447572140400213610ustar00rootroot00000000000000# frozen_string_literal: true require 'digest/sha1' require 'digest/sha2' require 'set' module Sprockets # Internal: Hash functions and digest related utilities. Mixed into # Environment. module DigestUtils extend self # Internal: Default digest class. # # Returns a Digest::Base subclass. def digest_class Digest::SHA256 end # Internal: Maps digest bytesize to the digest class. DIGEST_SIZES = { 20 => Digest::SHA1, 32 => Digest::SHA256, 48 => Digest::SHA384, 64 => Digest::SHA512 } # Internal: Detect digest class hash algorithm for digest bytes. # # While not elegant, all the supported digests have a unique bytesize. # # Returns Digest::Base or nil. def detect_digest_class(bytes) DIGEST_SIZES[bytes.bytesize] end ADD_VALUE_TO_DIGEST = { String => ->(val, digest) { digest << val }, FalseClass => ->(val, digest) { digest << 'FalseClass'.freeze }, TrueClass => ->(val, digest) { digest << 'TrueClass'.freeze }, NilClass => ->(val, digest) { digest << 'NilClass'.freeze }, Symbol => ->(val, digest) { digest << 'Symbol'.freeze digest << val.to_s }, Integer => ->(val, digest) { digest << 'Integer'.freeze digest << val.to_s }, Array => ->(val, digest) { digest << 'Array'.freeze val.each do |element| ADD_VALUE_TO_DIGEST[element.class].call(element, digest) end }, Hash => ->(val, digest) { digest << 'Hash'.freeze val.sort.each do |array| ADD_VALUE_TO_DIGEST[Array].call(array, digest) end }, Set => ->(val, digest) { digest << 'Set'.freeze ADD_VALUE_TO_DIGEST[Array].call(val, digest) }, Encoding => ->(val, digest) { digest << 'Encoding'.freeze digest << val.name } } ADD_VALUE_TO_DIGEST.compare_by_identity.rehash ADD_VALUE_TO_DIGEST.default_proc = ->(_, val) { raise TypeError, "couldn't digest #{ val }" } private_constant :ADD_VALUE_TO_DIGEST # Internal: Generate a hexdigest for a nested JSON serializable object. # # This is used for generating cache keys, so its pretty important its # wicked fast. Microbenchmarks away! # # obj - A JSON serializable object. # # Returns a String digest of the object. def digest(obj) build_digest(obj).digest end # Internal: Generate a hexdigest for a nested JSON serializable object. # # The same as `pack_hexdigest(digest(obj))`. # # obj - A JSON serializable object. # # Returns a String digest of the object. def hexdigest(obj) build_digest(obj).hexdigest! end # Internal: Pack a binary digest to a hex encoded string. # # bin - String bytes # # Returns hex String. def pack_hexdigest(bin) bin.unpack('H*'.freeze).first end # Internal: Unpack a hex encoded digest string into binary bytes. # # hex - String hex # # Returns binary String. def unpack_hexdigest(hex) [hex].pack('H*') end # Internal: Pack a binary digest to a base64 encoded string. # # bin - String bytes # # Returns base64 String. def pack_base64digest(bin) [bin].pack('m0') end # Internal: Pack a binary digest to a urlsafe base64 encoded string. # # bin - String bytes # # Returns urlsafe base64 String. def pack_urlsafe_base64digest(bin) str = pack_base64digest(bin) str.tr!('+/'.freeze, '-_'.freeze) str.tr!('='.freeze, ''.freeze) str end # Internal: Maps digest class to the CSP hash algorithm name. HASH_ALGORITHMS = { Digest::SHA256 => 'sha256'.freeze, Digest::SHA384 => 'sha384'.freeze, Digest::SHA512 => 'sha512'.freeze } # Public: Generate hash for use in the `integrity` attribute of an asset tag # as per the subresource integrity specification. # # digest - The String byte digest of the asset content. # # Returns a String or nil if hash algorithm is incompatible. def integrity_uri(digest) case digest when Digest::Base digest_class = digest.class digest = digest.digest when String digest_class = DIGEST_SIZES[digest.bytesize] else raise TypeError, "unknown digest: #{digest.inspect}" end if hash_name = HASH_ALGORITHMS[digest_class] "#{hash_name}-#{pack_base64digest(digest)}" end end # Public: Generate hash for use in the `integrity` attribute of an asset tag # as per the subresource integrity specification. # # digest - The String hexbyte digest of the asset content. # # Returns a String or nil if hash algorithm is incompatible. def hexdigest_integrity_uri(hexdigest) integrity_uri(unpack_hexdigest(hexdigest)) end # Internal: Checks an asset name for a valid digest # # name - The name of the asset # # Returns true if the name contains a digest like string and .digested before the extension def already_digested?(name) return name =~ /-([0-9a-zA-Z]{7,128})\.digested/ end private def build_digest(obj) digest = digest_class.new ADD_VALUE_TO_DIGEST[obj.class].call(obj, digest) digest end end end sprockets-4.2.1/lib/sprockets/directive_processor.rb000066400000000000000000000336241447572140400227400ustar00rootroot00000000000000# frozen_string_literal: true require 'set' require 'shellwords' module Sprockets # The `DirectiveProcessor` is responsible for parsing and evaluating # directive comments in a source file. # # A directive comment starts with a comment prefix, followed by an "=", # then the directive name, then any arguments. # # // JavaScript # //= require "foo" # # # CoffeeScript # #= require "bar" # # /* CSS # *= require "baz" # */ # # This makes it possible to disable or modify the processor to do whatever # you'd like. You could add your own custom directives or invent your own # directive syntax. # # `Environment#processors` includes `DirectiveProcessor` by default. # # To remove the processor entirely: # # env.unregister_processor('text/css', Sprockets::DirectiveProcessor) # env.unregister_processor('application/javascript', Sprockets::DirectiveProcessor) # # Then inject your own preprocessor: # # env.register_processor('text/css', MyProcessor) # class DirectiveProcessor # Directives are denoted by a `=` followed by the name, then # argument list. # # A few different styles are allowed: # # // =require foo # //= require foo # //= require "foo" # DIRECTIVE_PATTERN = / ^ \W* = \s* (\w+.*?) (\*\/)? $ /x def self.instance # Default to C comment styles @instance ||= new(comments: ["//", ["/*", "*/"]]) end def self.call(input) instance.call(input) end def initialize(comments: []) @header_pattern = compile_header_pattern(Array(comments)) end def call(input) dup._call(input) end def _call(input) @environment = input[:environment] @uri = input[:uri] @filename = input[:filename] @dirname = File.dirname(@filename) # If loading a source map file like `application.js.map` resolve # dependencies using `.js` instead of `.js.map` @content_type = SourceMapProcessor.original_content_type(input[:content_type], error_when_not_found: false) @required = Set.new(input[:metadata][:required]) @stubbed = Set.new(input[:metadata][:stubbed]) @links = Set.new(input[:metadata][:links]) @dependencies = Set.new(input[:metadata][:dependencies]) @to_link = Set.new @to_load = Set.new data, directives = process_source(input[:data]) process_directives(directives) { data: data, required: @required, stubbed: @stubbed, links: @links, to_load: @to_load, to_link: @to_link, dependencies: @dependencies } end protected # Directives will only be picked up if they are in the header # of the source file. C style (/* */), JavaScript (//), and # Ruby (#) comments are supported. # # Directives in comments after the first non-whitespace line # of code will not be processed. def compile_header_pattern(comments) re = comments.map { |c| case c when String "(?:#{Regexp.escape(c)}.*\\n?)+" when Array "(?:#{Regexp.escape(c[0])}(?m:.*?)#{Regexp.escape(c[1])})" else raise TypeError, "unknown comment type: #{c.class}" end }.join("|") Regexp.compile("\\A(?:(?m:\\s*)(?:#{re}))+") end def process_source(source) header = source[@header_pattern, 0] || "" body = $' || source header, directives = extract_directives(header) data = +"" data.force_encoding(body.encoding) data << header unless header.empty? data << body # Ensure body ends in a new line data << "\n" if data.length > 0 && data[-1] != "\n" return data, directives end # Returns an Array of directive structures. Each structure # is an Array with the line number as the first element, the # directive name as the second element, followed by any # arguments. # # [[1, "require", "foo"], [2, "require", "bar"]] # def extract_directives(header) processed_header = +"" directives = [] header.lines.each_with_index do |line, index| if directive = line[DIRECTIVE_PATTERN, 1] name, *args = Shellwords.shellwords(directive) if respond_to?("process_#{name}_directive", true) directives << [index + 1, name, *args] # Replace directive line with a clean break line = "\n" end end processed_header << line end processed_header.chomp! # Ensure header ends in a new line like before it was processed processed_header << "\n" if processed_header.length > 0 && header[-1] == "\n" return processed_header, directives end # Gathers comment directives in the source and processes them. # Any directive method matching `process_*_directive` will # automatically be available. This makes it easy to extend the # processor. # # To implement a custom directive called `require_glob`, subclass # `Sprockets::DirectiveProcessor`, then add a method called # `process_require_glob_directive`. # # class DirectiveProcessor < Sprockets::DirectiveProcessor # def process_require_glob_directive(glob) # Dir["#{dirname}/#{glob}"].sort.each do |filename| # require(filename) # end # end # end # # Replace the current processor on the environment with your own: # # env.unregister_processor('text/css', Sprockets::DirectiveProcessor) # env.register_processor('text/css', DirectiveProcessor) # def process_directives(directives) directives.each do |line_number, name, *args| begin send("process_#{name}_directive", *args) rescue Exception => e e.set_backtrace(["#{@filename}:#{line_number}"] + e.backtrace) raise e end end end # The `require` directive functions similar to Ruby's own `require`. # It provides a way to declare a dependency on a file in your path # and ensures it's only loaded once before the source file. # # `require` works with files in the environment path: # # //= require "foo.js" # # Extensions are optional. If your source file is ".js", it # assumes you are requiring another ".js". # # //= require "foo" # # Relative paths work too. Use a leading `./` to denote a relative # path: # # //= require "./bar" # def process_require_directive(path) @required << resolve(path, accept: @content_type, pipeline: :self) end # `require_self` causes the body of the current file to be inserted # before any subsequent `require` directives. Useful in CSS files, where # it's common for the index file to contain global styles that need to # be defined before other dependencies are loaded. # # /*= require "reset" # *= require_self # *= require_tree . # */ # def process_require_self_directive if @required.include?(@uri) raise ArgumentError, "require_self can only be called once per source file" end @required << @uri end # `require_directory` requires all the files inside a single # directory. It's similar to `path/*` since it does not follow # nested directories. # # //= require_directory "./javascripts" # def process_require_directory_directive(path = ".") path = expand_relative_dirname(:require_directory, path) require_paths(*@environment.stat_directory_with_dependencies(path)) end # `require_tree` requires all the nested files in a directory. # Its glob equivalent is `path/**/*`. # # //= require_tree "./public" # def process_require_tree_directive(path = ".") path = expand_relative_dirname(:require_tree, path) require_paths(*@environment.stat_sorted_tree_with_dependencies(path)) end # Allows you to state a dependency on a file without # including it. # # This is used for caching purposes. Any changes made to # the dependency file will invalidate the cache of the # source file. # # This is useful if you are using ERB and File.read to pull # in contents from another file. # # //= depend_on "foo.png" # def process_depend_on_directive(path) resolve(path) end # Allows you to state a dependency on an asset without including # it. # # This is used for caching purposes. Any changes that would # invalidate the asset dependency will invalidate the cache of # the source file. # # Unlike `depend_on`, the path must be a requirable asset. # # //= depend_on_asset "bar.js" # def process_depend_on_asset_directive(path) to_load(resolve(path)) end # Allows you to state a dependency on a relative directory # without including it. # # This is used for caching purposes. Any changes made to # the dependency directory will invalidate the cache of the # source file. # # This is useful if you are using ERB and File.read to pull # in contents from multiple files in a directory. # # //= depend_on_directory ./data # def process_depend_on_directory_directive(path = ".", accept = nil) path = expand_relative_dirname(:depend_on_directory, path) accept = expand_accept_shorthand(accept) resolve_paths(*@environment.stat_directory_with_dependencies(path), accept: accept) end # Allows dependency to be excluded from the asset bundle. # # The `path` must be a valid asset and may or may not already # be part of the bundle. Once stubbed, it is blacklisted and # can't be brought back by any other `require`. # # //= stub "jquery" # def process_stub_directive(path) @stubbed << resolve(path, accept: @content_type, pipeline: :self) end # Declares a linked dependency on the target asset. # # The `path` must be a valid asset and should not already be part of the # bundle. Any linked assets will automatically be compiled along with the # current. # # /*= link "logo.png" */ # def process_link_directive(path) uri = to_load(resolve(path)) @to_link << uri end # `link_directory` links all the files inside a single # directory. It's similar to `path/*` since it does not follow # nested directories. # # //= link_directory "./fonts" # # Use caution when linking against JS or CSS assets. Include an explicit # extension or content type in these cases. # # //= link_directory "./scripts" .js # def process_link_directory_directive(path = ".", accept = nil) path = expand_relative_dirname(:link_directory, path) accept = expand_accept_shorthand(accept) link_paths(*@environment.stat_directory_with_dependencies(path), accept) end # `link_tree` links all the nested files in a directory. # Its glob equivalent is `path/**/*`. # # //= link_tree "./images" # # Use caution when linking against JS or CSS assets. Include an explicit # extension or content type in these cases. # # //= link_tree "./styles" .css # def process_link_tree_directive(path = ".", accept = nil) path = expand_relative_dirname(:link_tree, path) accept = expand_accept_shorthand(accept) link_paths(*@environment.stat_sorted_tree_with_dependencies(path), accept) end private def expand_accept_shorthand(accept) if accept.nil? nil elsif accept.include?("/") accept elsif accept.start_with?(".") @environment.mime_exts[accept] else @environment.mime_exts[".#{accept}"] end end def require_paths(paths, deps) resolve_paths(paths, deps, accept: @content_type, pipeline: :self) do |uri| @required << uri end end def link_paths(paths, deps, accept) resolve_paths(paths, deps, accept: accept) do |uri| @to_link << to_load(uri) end end def resolve_paths(paths, deps, **kargs) @dependencies.merge(deps) paths.each do |subpath, stat| next if subpath == @filename || stat.directory? uri, deps = @environment.resolve(subpath, **kargs) @dependencies.merge(deps) yield uri if uri && block_given? end end def expand_relative_dirname(directive, path) if @environment.relative_path?(path) path = File.expand_path(path, @dirname) stat = @environment.stat(path) if stat && stat.directory? path else raise ArgumentError, "#{directive} argument must be a directory" end else # The path must be relative and start with a `./`. raise ArgumentError, "#{directive} argument must be a relative path" end end def to_load(uri) @to_load << uri uri end def resolve(path, **kargs) # Prevent absolute paths in directives if @environment.absolute_path?(path) raise FileOutsidePaths, "can't require absolute file: #{path}" end kargs[:base_path] = @dirname uri, deps = @environment.resolve!(path, **kargs) @dependencies.merge(deps) uri end end end sprockets-4.2.1/lib/sprockets/eco_processor.rb000066400000000000000000000014431447572140400215220ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' module Sprockets # Processor engine class for the Eco compiler. Depends on the `eco` gem. # # For more information see: # # https://github.com/sstephenson/ruby-eco # https://github.com/sstephenson/eco # module EcoProcessor VERSION = '1' def self.cache_key @cache_key ||= "#{name}:#{Autoload::Eco::Source::VERSION}:#{VERSION}".freeze end # Compile template data with Eco compiler. # # Returns a JS function definition String. The result should be # assigned to a JS variable. # # # => "function(...) {...}" # def self.call(input) data = input[:data] input[:cache].fetch([cache_key, data]) do Autoload::Eco.compile(data) end end end end sprockets-4.2.1/lib/sprockets/ejs_processor.rb000066400000000000000000000013271447572140400215360ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' module Sprockets # Processor engine class for the EJS compiler. Depends on the `ejs` gem. # # For more information see: # # https://github.com/sstephenson/ruby-ejs # module EjsProcessor VERSION = '1' def self.cache_key @cache_key ||= "#{name}:#{VERSION}".freeze end # Compile template data with EJS compiler. # # Returns a JS function definition String. The result should be # assigned to a JS variable. # # # => "function(obj){...}" # def self.call(input) data = input[:data] input[:cache].fetch([cache_key, data]) do Autoload::EJS.compile(data) end end end end sprockets-4.2.1/lib/sprockets/encoding_utils.rb000066400000000000000000000146461447572140400216740ustar00rootroot00000000000000# frozen_string_literal: true require 'base64' require 'stringio' require 'zlib' module Sprockets # Internal: HTTP transport encoding and charset detecting related functions. # Mixed into Environment. module EncodingUtils extend self ## Binary encodings ## # Public: Use deflate to compress data. # # str - String data # # Returns a compressed String def deflate(str) deflater = Zlib::Deflate.new( Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS, Zlib::MAX_MEM_LEVEL, Zlib::DEFAULT_STRATEGY ) deflater << str deflater.finish end # Internal: Unmarshal optionally deflated data. # # Checks leading marshal header to see if the bytes are uncompressed # otherwise inflate the data an unmarshal. # # str - Marshaled String # window_bits - Integer deflate window size. See ZLib::Inflate.new() # # Returns unmarshaled Object or raises an Exception. def unmarshaled_deflated(str, window_bits = -Zlib::MAX_WBITS) major, minor = str[0], str[1] if major && major.ord == Marshal::MAJOR_VERSION && minor && minor.ord <= Marshal::MINOR_VERSION marshaled = str else begin marshaled = Zlib::Inflate.new(window_bits).inflate(str) rescue Zlib::DataError marshaled = str end end Marshal.load(marshaled) end # Public: Use gzip to compress data. # # str - String data # # Returns a compressed String def gzip(str) io = StringIO.new gz = Zlib::GzipWriter.new(io, Zlib::BEST_COMPRESSION) gz.mtime = 1 gz << str gz.finish io.string end # Public: Use base64 to encode data. # # str - String data # # Returns a encoded String def base64(str) Base64.strict_encode64(str) end ## Charset encodings ## # Internal: Shorthand aliases for detecter functions. CHARSET_DETECT = {} # Internal: Mapping unicode encodings to byte order markers. BOM = { Encoding::UTF_32LE => [0xFF, 0xFE, 0x00, 0x00], Encoding::UTF_32BE => [0x00, 0x00, 0xFE, 0xFF], Encoding::UTF_8 => [0xEF, 0xBB, 0xBF], Encoding::UTF_16LE => [0xFF, 0xFE], Encoding::UTF_16BE => [0xFE, 0xFF] } # Public: Basic string detecter. # # Attempts to parse any Unicode BOM otherwise falls back to the # environment's external encoding. # # str - ASCII-8BIT encoded String # # Returns encoded String. def detect(str) str = detect_unicode_bom(str) # Attempt Charlock detection if str.encoding == Encoding::BINARY charlock_detect(str) end # Fallback to environment's external encoding if str.encoding == Encoding::BINARY str.force_encoding(Encoding.default_external) end str end CHARSET_DETECT[:default] = method(:detect) # Internal: Use Charlock Holmes to detect encoding. # # To enable this code path, require 'charlock_holmes' # # Returns encoded String. def charlock_detect(str) if defined? CharlockHolmes::EncodingDetector if detected = CharlockHolmes::EncodingDetector.detect(str) str.force_encoding(detected[:encoding]) if detected[:encoding] end end str end # Public: Detect Unicode string. # # Attempts to parse Unicode BOM and falls back to UTF-8. # # str - ASCII-8BIT encoded String # # Returns encoded String. def detect_unicode(str) str = detect_unicode_bom(str) # Fallback to UTF-8 if str.encoding == Encoding::BINARY str.force_encoding(Encoding::UTF_8) end str end CHARSET_DETECT[:unicode] = method(:detect_unicode) # Public: Detect and strip BOM from possible unicode string. # # str - ASCII-8BIT encoded String # # Returns UTF 8/16/32 encoded String without BOM or the original String if # no BOM was present. def detect_unicode_bom(str) bom_bytes = str.byteslice(0, 4).bytes.to_a BOM.each do |encoding, bytes| if bom_bytes[0, bytes.size] == bytes str = str.dup str.force_encoding(Encoding::BINARY) str.slice!(0, bytes.size) str.force_encoding(encoding) return str end end return str end # Public: Detect and strip @charset from CSS style sheet. # # str - String. # # Returns a encoded String. def detect_css(str) str = detect_unicode_bom(str) if name = scan_css_charset(str) encoding = Encoding.find(name) str = str.dup str.force_encoding(encoding) len = "@charset \"#{name}\";".encode(encoding).size str.slice!(0, len) str end # Fallback to UTF-8 if str.encoding == Encoding::BINARY str.force_encoding(Encoding::UTF_8) end str end CHARSET_DETECT[:css] = method(:detect_css) # Internal: @charset bytes CHARSET_START = [0x40, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x20, 0x22] CHARSET_SIZE = CHARSET_START.size # Internal: Scan binary CSS string for @charset encoding name. # # str - ASCII-8BIT encoded String # # Returns encoding String name or nil. def scan_css_charset(str) buf = [] i = 0 str.each_byte.each do |byte| # Halt on line breaks break if byte == 0x0A || byte == 0x0D # Only ascii bytes next unless 0x0 < byte && byte <= 0xFF if i < CHARSET_SIZE elsif i == CHARSET_SIZE if buf == CHARSET_START buf = [] else break end elsif byte == 0x22 return buf.pack('C*') end buf << byte i += 1 end nil end # Public: Detect charset from HTML document. # # Attempts to parse any Unicode BOM otherwise attempt Charlock detection # and finally falls back to the environment's external encoding. # # str - String. # # Returns a encoded String. def detect_html(str) str = detect_unicode_bom(str) # Attempt Charlock detection if str.encoding == Encoding::BINARY charlock_detect(str) end # Fallback to environment's external encoding if str.encoding == Encoding::BINARY str.force_encoding(Encoding.default_external) end str end CHARSET_DETECT[:html] = method(:detect_html) end end sprockets-4.2.1/lib/sprockets/environment.rb000066400000000000000000000022701447572140400212200ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/base' require 'sprockets/cache/memory_store' require 'sprockets/cached_environment' module Sprockets class Environment < Base # `Environment` should be initialized with your application's root # directory. This should be the same as your Rails or Rack root. # # env = Environment.new(Rails.root) # def initialize(root = ".") initialize_configuration(Sprockets) self.root = root self.cache = Cache::MemoryStore.new yield self if block_given? end # Returns a cached version of the environment. # # All of its file system calls are cached which makes `cached` much # faster. This behavior is ideal in production since the file # system only changes between deploys. def cached CachedEnvironment.new(self) end alias_method :index, :cached def find_asset(*args, **options) cached.find_asset(*args, **options) end def find_asset!(*args) cached.find_asset!(*args) end def find_all_linked_assets(*args, &block) cached.find_all_linked_assets(*args, &block) end def load(*args) cached.load(*args) end end end sprockets-4.2.1/lib/sprockets/erb_processor.rb000066400000000000000000000020371447572140400215240ustar00rootroot00000000000000# frozen_string_literal: true require 'erb' class Sprockets::ERBProcessor # Public: Return singleton instance with default options. # # Returns ERBProcessor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def initialize(&block) @block = block end def call(input) if keyword_constructor? # Ruby 2.6+ engine = ::ERB.new(input[:data], trim_mode: '<>') else engine = ::ERB.new(input[:data], nil, '<>') end engine.filename = input[:filename] context = input[:environment].context_class.new(input) klass = (class << context; self; end) klass.const_set(:ENV, context.env_proxy) klass.class_eval(&@block) if @block data = engine.result(context.instance_eval('binding')) context.metadata.merge(data: data) end private def keyword_constructor? return @keyword_constructor if defined? @keyword_constructor @keyword_constructor = ::ERB.instance_method(:initialize).parameters.include?([:key, :trim_mode]) end end sprockets-4.2.1/lib/sprockets/errors.rb000066400000000000000000000007201447572140400201660ustar00rootroot00000000000000# frozen_string_literal: true # Define some basic Sprockets error classes module Sprockets class Error < StandardError; end class ArgumentError < Error; end class ContentTypeMismatch < Error; end class NotImplementedError < Error; end class NotFound < Error; end class ConversionError < NotFound; end class FileNotFound < NotFound; end class FileOutsidePaths < NotFound; end end sprockets-4.2.1/lib/sprockets/exporters/000077500000000000000000000000001447572140400203615ustar00rootroot00000000000000sprockets-4.2.1/lib/sprockets/exporters/base.rb000066400000000000000000000050601447572140400216210ustar00rootroot00000000000000module Sprockets module Exporters # Convenience class for all exporters to inherit from # # An exporter is responsible for exporting a Sprockets::Asset # to a file system. For example the Exporters::File class # writes the asset to it's destination. The Exporters::Zlib class # writes a gzip copy of the asset to disk. class Base attr_reader :asset, :environment, :directory, :target # Public: Creates new instance # # Initialize will be called with # keyword arguments: # # - asset: An instance of Sprockets::Asset. # - environment: An instance of Sprockets::Environment. # - directory: String representing the target directory to write to. # # These will all be stored as accessible values. In addition a # +target+ will be available which is the target directory and # the asset's digest path combined. def initialize(asset: nil, environment: nil, directory: nil) @asset = asset @environment = environment @directory = directory @target = ::File.join(directory, asset.digest_path) setup end # Public: Callback that is executed after initialization # # Any setup that needs to be done can be performed in the +setup+ # method. It will be called immediately after initialization. def setup end # Public: Handles logic for skipping exporter and notifying logger # # The `skip?` will be called before anything will be written. # If `skip?` returns truthy it will not continue. This method # takes a `logger` that responds to +debug+ and +info+. The `skip?` # method is the only place expected to write to a logger, any other # messages may produce jumbled logs. def skip?(logger) false end # Public: Contains logic for writing "exporting" asset to disk # # If the exporter is not skipped it then Sprockets will execute it's # `call` method. This method takes no arguments and should only use # elements passed in via initialize or stored in `setup`. def call raise "Must subclass and implement call" end # Public: Yields a file that can be written to with the input # # `filename`. Defaults to the `target`. Method # is safe to use in forked or threaded environments. def write(filename = target) FileUtils.mkdir_p File.dirname(filename) PathUtils.atomic_write(filename) do |f| yield f end end end end end sprockets-4.2.1/lib/sprockets/exporters/file_exporter.rb000066400000000000000000000007661447572140400235660ustar00rootroot00000000000000require 'sprockets/exporters/base' module Sprockets module Exporters # Writes a an asset file to disk class FileExporter < Exporters::Base def skip?(logger) if ::File.exist?(target) logger.debug "Skipping #{ target }, already exists" true else logger.info "Writing #{ target }" false end end def call write(target) do |file| file.write(asset.source) end end end end end sprockets-4.2.1/lib/sprockets/exporters/zlib_exporter.rb000066400000000000000000000015421447572140400236000ustar00rootroot00000000000000require 'sprockets/exporters/base' require 'sprockets/utils/gzip' module Sprockets module Exporters # Generates a `.gz` file using the zlib algorithm built into # Ruby's standard library. class ZlibExporter < Exporters::Base def setup @gzip_target = "#{ target }.gz" @gzip = Sprockets::Utils::Gzip.new(asset, archiver: Utils::Gzip::ZlibArchiver) end def skip?(logger) return true if environment.skip_gzip? return true if @gzip.cannot_compress? if ::File.exist?(@gzip_target) logger.debug "Skipping #{ @gzip_target }, already exists" true else logger.info "Writing #{ @gzip_target }" false end end def call write(@gzip_target) do |file| @gzip.compress(file, target) end end end end end sprockets-4.2.1/lib/sprockets/exporters/zopfli_exporter.rb000066400000000000000000000005711447572140400241440ustar00rootroot00000000000000require 'sprockets/exporters/zlib_exporter' module Sprockets module Exporters # Generates a `.gz` file using the zopfli algorithm from the # Zopfli gem. class ZopfliExporter < ZlibExporter def setup @gzip_target = "#{ target }.gz" @gzip = Sprockets::Utils::Gzip.new(asset, archiver: Utils::Gzip::ZopfliArchiver) end end end end sprockets-4.2.1/lib/sprockets/exporting.rb000066400000000000000000000044361447572140400207010ustar00rootroot00000000000000module Sprockets # `Exporting` is an internal mixin whose public methods are exposed on # the `Environment` and `CachedEnvironment` classes. module Exporting # Exporters are ran on the assets:precompile task def exporters config[:exporters] end # Public: Registers a new Exporter `klass` for `mime_type`. # # If your exporter depends on one or more other exporters you can # specify this via the `depend_on` keyword. # # register_exporter '*/*', Sprockets::Exporters::ZlibExporter # # This ensures that `Sprockets::Exporters::File` will always execute before # `Sprockets::Exporters::Zlib` def register_exporter(mime_types, klass = nil) mime_types = Array(mime_types) mime_types.each do |mime_type| self.config = hash_reassoc(config, :exporters, mime_type) do |_exporters| _exporters << klass end end end # Public: Remove Exporting processor `klass` for `mime_type`. # # environment.unregister_exporter '*/*', Sprockets::Exporters::ZlibExporter # # Can be called without a mime type # # environment.unregister_exporter Sprockets::Exporters::ZlibExporter # # Does not remove any exporters that depend on `klass`. def unregister_exporter(mime_types, exporter = nil) unless mime_types.is_a? Array if mime_types.is_a? String mime_types = [mime_types] else # called with no mime type exporter = mime_types mime_types = nil end end self.config = hash_reassoc(config, :exporters) do |_exporters| _exporters.each do |mime_type, exporters_array| next if mime_types && !mime_types.include?(mime_type) if exporters_array.include? exporter _exporters[mime_type] = exporters_array.dup.delete exporter end end end end # Public: Checks if concurrent exporting is allowed def export_concurrent config[:export_concurrent] end # Public: Enable or disable the concurrently exporting files # # Defaults to true. # # environment.export_concurrent = false # def export_concurrent=(export_concurrent) self.config = config.merge(export_concurrent: export_concurrent).freeze end end end sprockets-4.2.1/lib/sprockets/file_reader.rb000066400000000000000000000010211447572140400211060ustar00rootroot00000000000000# frozen_string_literal: true require 'set' module Sprockets # Internal: The first processor in the pipeline that reads the file into # memory and passes it along as `input[:data]`. class FileReader def self.call(input) env = input[:environment] data = env.read_file(input[:filename], input[:content_type]) dependencies = Set.new(input[:metadata][:dependencies]) dependencies += [env.build_file_digest_uri(input[:filename])] { data: data, dependencies: dependencies } end end end sprockets-4.2.1/lib/sprockets/http_utils.rb000066400000000000000000000101121447572140400210450ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets # Internal: HTTP URI utilities. Many adapted from Rack::Utils. Mixed into # Environment. module HTTPUtils extend self # Public: Test mime type against mime range. # # match_mime_type?('text/html', 'text/*') => true # match_mime_type?('text/plain', '*') => true # match_mime_type?('text/html', 'application/json') => false # # Returns true if the given value is a mime match for the given mime match # specification, false otherwise. def match_mime_type?(value, matcher) v1, v2 = value.split('/'.freeze, 2) m1, m2 = matcher.split('/'.freeze, 2) (m1 == '*'.freeze || v1 == m1) && (m2.nil? || m2 == '*'.freeze || m2 == v2) end # Public: Return values from Hash where the key matches the mime type. # # hash - Hash of String matcher keys to Object values # mime_type - String mime type # # Returns Array of Object values. def match_mime_type_keys(hash, mime_type) type, subtype = mime_type.split('/', 2) [ hash["*"], hash["*/*"], hash["#{type}/*"], hash["#{type}/#{subtype}"] ].compact end # Internal: Parse Accept header quality values. # # values - String e.g. "application/javascript" # # Adapted from Rack::Utils#q_values. Quality values are # described in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html # # parse_q_values("application/javascript") # # => [["application/javascript", 1.0]] # # parse_q_values("*/*") # # => [["*/*", 1.0]] # # parse_q_values("text/plain; q=0.5, image/*") # # => [["text/plain", 0.5], ["image/*", 1.0]] # # parse_q_values("application/javascript, text/css") # # => [["application/javascript", 1.0], ["text/css", 1.0]] # # Returns an Array of [String, Float]. def parse_q_values(values) values.to_s.split(/\s*,\s*/).map do |part| value, parameters = part.split(/\s*;\s*/, 2) quality = 1.0 if md = /\Aq=([\d.]+)/.match(parameters) quality = md[1].to_f end [value, quality] end end # Internal: Find all qvalue matches from an Array of available options. # # Adapted from Rack::Utils#q_values. # # Returns Array of matched Strings from available Array or []. def find_q_matches(q_values, available, &matcher) matcher ||= lambda { |a, b| a == b } matches = [] case q_values when Array when String q_values = parse_q_values(q_values) when NilClass q_values = [] else raise TypeError, "unknown q_values type: #{q_values.class}" end i = 0 q_values.each do |accepted, quality| if match = available.find { |option| matcher.call(option, accepted) } i += 1 matches << [-quality, i, match] end end matches.sort! matches.map! { |_, _, match| match } matches end # Internal: Find the best qvalue match from an Array of available options. # # Adapted from Rack::Utils#q_values. # # Returns the matched String from available Array or nil. def find_best_q_match(q_values, available, &matcher) find_q_matches(q_values, available, &matcher).first end # Internal: Find the all qvalue match from an Array of available mime type # options. # # Adapted from Rack::Utils#q_values. # # Returns Array of matched mime type Strings from available Array or []. def find_mime_type_matches(q_value_header, available) find_q_matches(q_value_header, available) do |a, b| match_mime_type?(a, b) end end # Internal: Find the best qvalue match from an Array of available mime type # options. # # Adapted from Rack::Utils#q_values. # # Returns the matched mime type String from available Array or nil. def find_best_mime_type_match(q_value_header, available) find_best_q_match(q_value_header, available) do |a, b| match_mime_type?(a, b) end end end end sprockets-4.2.1/lib/sprockets/jsminc_compressor.rb000066400000000000000000000012011447572140400224040ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/digest_utils' module Sprockets class JSMincCompressor VERSION = '1' def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key def initialize(options = {}) @compressor_class = Autoload::JSMinC @cache_key = "#{self.class.name}:#{Autoload::JSMinC::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze end def call(input) @compressor_class.minify(input[:data]) end end end sprockets-4.2.1/lib/sprockets/jst_processor.rb000066400000000000000000000023301447572140400215500ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets # Public: JST transformer. # # Exports server side compiled templates to an object. # # Name your template "users/show.ejs", "users/new.eco", etc. # # To accept the default options # # environment.register_transformer # 'application/javascript+function', # 'application/javascript', JstProcessor # # Change the default namespace. # # environment.register_transformer # 'application/javascript+function', # 'application/javascript', JstProcessor.new(namespace: 'App.templates') # class JstProcessor def self.default_namespace 'this.JST' end # Public: Return singleton instance with default options. # # Returns JstProcessor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def initialize(namespace: self.class.default_namespace) @namespace = namespace end def call(input) data = input[:data].gsub(/$(.)/m, "\\1 ").strip key = input[:name] <<-JST (function() { #{@namespace} || (#{@namespace} = {}); #{@namespace}[#{key.inspect}] = #{data}; }).call(this); JST end end end sprockets-4.2.1/lib/sprockets/loader.rb000066400000000000000000000331531447572140400201260ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/asset' require 'sprockets/digest_utils' require 'sprockets/errors' require 'sprockets/file_reader' require 'sprockets/mime' require 'sprockets/path_utils' require 'sprockets/processing' require 'sprockets/processor_utils' require 'sprockets/resolve' require 'sprockets/transformers' require 'sprockets/uri_utils' require 'sprockets/unloaded_asset' module Sprockets # The loader phase takes a asset URI location and returns a constructed Asset # object. module Loader include DigestUtils, PathUtils, ProcessorUtils, URIUtils include Mime, Processing, Resolve, Transformers # Public: Load Asset by Asset URI. # # uri - A String containing complete URI to a file including schema # and full path such as: # "file:///Path/app/assets/js/app.js?type=application/javascript" # # Returns Asset. def load(uri) unloaded = UnloadedAsset.new(uri, self) if unloaded.params.key?(:id) unless asset = asset_from_cache(unloaded.asset_key) id = unloaded.params.delete(:id) uri_without_id = build_asset_uri(unloaded.filename, unloaded.params) asset = load_from_unloaded(UnloadedAsset.new(uri_without_id, self)) if asset[:id] != id @logger.warn "Sprockets load error: Tried to find #{uri}, but latest was id #{asset[:id]}" end end else asset = fetch_asset_from_dependency_cache(unloaded) do |paths| # When asset is previously generated, its "dependencies" are stored in the cache. # The presence of `paths` indicates dependencies were stored. # We can check to see if the dependencies have not changed by "resolving" them and # generating a digest key from the resolved entries. If this digest key has not # changed, the asset will be pulled from cache. # # If this `paths` is present but the cache returns nothing then `fetch_asset_from_dependency_cache` # will confusingly be called again with `paths` set to nil where the asset will be # loaded from disk. if paths digest = DigestUtils.digest(resolve_dependencies(paths)) if uri_from_cache = cache.get(unloaded.digest_key(digest), true) asset_from_cache(UnloadedAsset.new(uri_from_cache, self).asset_key) end else load_from_unloaded(unloaded) end end end Asset.new(asset) end private def compress_key_from_hash(hash, key) return unless hash.key?(key) value = hash[key].dup return if !value if block_given? value.map! do |x| if yield x compress_from_root(x) else x end end else value.map! { |x| compress_from_root(x) } end hash[key] = value end def expand_key_from_hash(hash, key) return unless hash.key?(key) value = hash[key].dup return if !value if block_given? value.map! do |x| if yield x expand_from_root(x) else x end end else value.map! { |x| expand_from_root(x) } end hash[key] = value end # Internal: Load asset hash from cache # # key - A String containing lookup information for an asset # # This method converts all "compressed" paths to absolute paths. # Returns a hash of values representing an asset def asset_from_cache(key) asset = cache.get(key, true) if asset asset[:uri] = expand_from_root(asset[:uri]) asset[:load_path] = expand_from_root(asset[:load_path]) asset[:filename] = expand_from_root(asset[:filename]) expand_key_from_hash(asset[:metadata], :included) expand_key_from_hash(asset[:metadata], :links) expand_key_from_hash(asset[:metadata], :stubbed) expand_key_from_hash(asset[:metadata], :required) expand_key_from_hash(asset[:metadata], :to_load) expand_key_from_hash(asset[:metadata], :to_link) expand_key_from_hash(asset[:metadata], :dependencies) { |uri| uri.start_with?("file-digest://") } asset[:metadata].each_key do |k| next unless k.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith expand_key_from_hash(asset[:metadata], k) end end asset end # Internal: Loads an asset and saves it to cache # # unloaded - An UnloadedAsset # # This method is only called when the given unloaded asset could not be # successfully pulled from cache. def load_from_unloaded(unloaded) unless file?(unloaded.filename) raise FileNotFound, "could not find file: #{unloaded.filename}" end path_to_split = if index_alias = unloaded.params[:index_alias] expand_from_root index_alias else unloaded.filename end load_path, logical_path = paths_split(config[:paths], path_to_split) unless load_path target = path_to_split target += " (index alias of #{unloaded.filename})" if unloaded.params[:index_alias] raise FileOutsidePaths, "#{target} is no longer under a load path: #{self.paths.join(', ')}" end extname, file_type = match_path_extname(logical_path, mime_exts) logical_path = logical_path.chomp(extname) name = logical_path if pipeline = unloaded.params[:pipeline] logical_path += ".#{pipeline}" end if type = unloaded.params[:type] extensions = config[:mime_types][type][:extensions] extension = extensions.include?(extname) ? extname : extensions.first logical_path += extension end if type != file_type && !config[:transformers][file_type][type] raise ConversionError, "could not convert #{file_type.inspect} to #{type.inspect}" end processors = processors_for(type, file_type, pipeline) processors_dep_uri = build_processors_uri(type, file_type, pipeline) dependencies = config[:dependencies] + [processors_dep_uri] # Read into memory and process if theres a processor pipeline if processors.any? result = call_processors(processors, { environment: self, cache: self.cache, uri: unloaded.uri, filename: unloaded.filename, load_path: load_path, name: name, content_type: type, metadata: { dependencies: dependencies } }) validate_processor_result!(result) source = result.delete(:data) metadata = result metadata[:charset] = source.encoding.name.downcase unless metadata.key?(:charset) metadata[:digest] = digest(source) metadata[:length] = source.bytesize metadata[:environment_version] = version else dependencies << build_file_digest_uri(unloaded.filename) metadata = { digest: file_digest(unloaded.filename), length: self.stat(unloaded.filename).size, dependencies: dependencies, environment_version: version, } end asset = { uri: unloaded.uri, load_path: load_path, filename: unloaded.filename, name: name, logical_path: logical_path, content_type: type, source: source, metadata: metadata, dependencies_digest: DigestUtils.digest(resolve_dependencies(metadata[:dependencies])) } asset[:id] = hexdigest(asset) asset[:uri] = build_asset_uri(unloaded.filename, unloaded.params.merge(id: asset[:id])) store_asset(asset, unloaded) asset end # Internal: Save a given asset to the cache # # asset - A hash containing values of loaded asset # unloaded - The UnloadedAsset used to lookup the `asset` # # This method converts all absolute paths to "compressed" paths # which are relative if they're in the root. def store_asset(asset, unloaded) # Save the asset in the cache under the new URI cached_asset = asset.dup cached_asset[:uri] = compress_from_root(asset[:uri]) cached_asset[:filename] = compress_from_root(asset[:filename]) cached_asset[:load_path] = compress_from_root(asset[:load_path]) if cached_asset[:metadata] # Deep dup to avoid modifying `asset` cached_asset[:metadata] = cached_asset[:metadata].dup compress_key_from_hash(cached_asset[:metadata], :included) compress_key_from_hash(cached_asset[:metadata], :links) compress_key_from_hash(cached_asset[:metadata], :stubbed) compress_key_from_hash(cached_asset[:metadata], :required) compress_key_from_hash(cached_asset[:metadata], :to_load) compress_key_from_hash(cached_asset[:metadata], :to_link) compress_key_from_hash(cached_asset[:metadata], :dependencies) { |uri| uri.start_with?("file-digest://") } cached_asset[:metadata].each do |key, value| next unless key.match?(/_dependencies\z/) # rubocop:disable Performance/EndWith compress_key_from_hash(cached_asset[:metadata], key) end end # Unloaded asset and stored_asset now have a different URI stored_asset = UnloadedAsset.new(asset[:uri], self) cache.set(stored_asset.asset_key, cached_asset, true) # Save the new relative path for the digest key of the unloaded asset cache.set(unloaded.digest_key(asset[:dependencies_digest]), stored_asset.compressed_path, true) end # Internal: Resolve set of dependency URIs. # # uris - An Array of "dependencies" for example: # ["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css", # "file-digest:///Full/path/app/assets/stylesheets/application.css", # "processors:type=text/css&file_type=text/css&pipeline=self", # "file-digest:///Full/path/app/assets/stylesheets"] # # Returns back array of things that the given uri depends on # For example the environment version, if you're using a different version of sprockets # then the dependencies should be different, this is used only for generating cache key # for example the "environment-version" may be resolved to "environment-1.0-3.2.0" for # version "3.2.0" of sprockets. # # Any paths that are returned are converted to relative paths # # Returns array of resolved dependencies def resolve_dependencies(uris) uris.map { |uri| resolve_dependency(uri) } end # Internal: Retrieves an asset based on its digest # # unloaded - An UnloadedAsset # limit - An Integer which sets the maximum number of versions of "histories" # stored in the cache # # This method attempts to retrieve the last `limit` number of histories of an asset # from the cache a "history" which is an array of unresolved "dependencies" that the asset needs # to compile. In this case a dependency can refer to either an asset e.g. index.js # may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect # compilation, such as the VERSION of Sprockets (i.e. the environment) and what "processors" # are used. # # For example a history array may look something like this # # [["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css", # "file-digest:///Full/path/app/assets/stylesheets/application.css", # "processors:type=text/css&file_digesttype=text/css&pipeline=self", # "file-digest:///Full/path/app/assets/stylesheets"]] # # Where the first entry is a Set of dependencies for last generated version of that asset. # Multiple versions are stored since Sprockets keeps the last `limit` number of assets # generated present in the system. # # If a "history" of dependencies is present in the cache, each version of "history" will be # yielded to the passed block which is responsible for loading the asset. If found, the existing # history will be saved with the dependency that found a valid asset moved to the front. # # If no history is present, or if none of the histories could be resolved to a valid asset then, # the block is yielded to and expected to return a valid asset. # When this happens the dependencies for the returned asset are added to the "history", and older # entries are removed if the "history" is above `limit`. def fetch_asset_from_dependency_cache(unloaded, limit = 3) key = unloaded.dependency_history_key history = cache.get(key) || [] history.each_with_index do |deps, index| expanded_deps = deps.map do |path| path.start_with?("file-digest://") ? expand_from_root(path) : path end if asset = yield(expanded_deps) cache.set(key, history.rotate!(index)) if index > 0 return asset end end asset = yield deps = asset[:metadata][:dependencies].dup.map! do |uri| uri.start_with?("file-digest://") ? compress_from_root(uri) : uri end cache.set(key, history.unshift(deps).take(limit)) asset end end end sprockets-4.2.1/lib/sprockets/manifest.rb000066400000000000000000000235221447572140400204650ustar00rootroot00000000000000# frozen_string_literal: true require 'json' require 'time' require 'concurrent' require 'sprockets/manifest_utils' module Sprockets # The Manifest logs the contents of assets compiled to a single directory. It # records basic attributes about the asset for fast lookup without having to # compile. A pointer from each logical path indicates which fingerprinted # asset is the current one. # # The JSON is part of the public API and should be considered stable. This # should make it easy to read from other programming languages and processes # that don't have sprockets loaded. See `#assets` and `#files` for more # information about the structure. class Manifest include ManifestUtils attr_reader :environment # Create new Manifest associated with an `environment`. `filename` is a full # path to the manifest json file. The file may or may not already exist. The # dirname of the `filename` will be used to write compiled assets to. # Otherwise, if the path is a directory, the filename will default a random # ".sprockets-manifest-*.json" file in that directory. # # Manifest.new(environment, "./public/assets/manifest.json") # def initialize(*args) if args.first.is_a?(Base) || args.first.nil? @environment = args.shift end @directory, @filename = args[0], args[1] # Whether the manifest file is using the old manifest-*.json naming convention @legacy_manifest = false # Expand paths @directory = File.expand_path(@directory) if @directory @filename = File.expand_path(@filename) if @filename # If filename is given as the second arg if @directory && File.extname(@directory) != "" @directory, @filename = nil, @directory end # Default dir to the directory of the filename @directory ||= File.dirname(@filename) if @filename # If directory is given w/o filename, pick a random manifest location if @directory && @filename.nil? @filename = find_directory_manifest(@directory, logger) end unless @directory && @filename raise ArgumentError, "manifest requires output filename" end data = {} begin if File.exist?(@filename) data = json_decode(File.read(@filename)) end rescue JSON::ParserError => e logger.error "#{@filename} is invalid: #{e.class} #{e.message}" end @data = data end # Returns String path to manifest.json file. attr_reader :filename alias_method :path, :filename attr_reader :directory alias_method :dir, :directory # Returns internal assets mapping. Keys are logical paths which # map to the latest fingerprinted filename. # # Logical path (String): Fingerprint path (String) # # { "application.js" => "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js", # "jquery.js" => "jquery-ae0908555a245f8266f77df5a8edca2e.js" } # def assets @data['assets'] ||= {} end # Returns internal file directory listing. Keys are filenames # which map to an attributes array. # # Fingerprint path (String): # logical_path: Logical path (String) # mtime: ISO8601 mtime (String) # digest: Base64 hex digest (String) # # { "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js" => # { 'logical_path' => "application.js", # 'mtime' => "2011-12-13T21:47:08-06:00", # 'digest' => "2e8e9a7c6b0aafa0c9bdeec90ea30213" } } # def files @data['files'] ||= {} end # Public: Find all assets matching pattern set in environment. # # Returns Enumerator of Assets. def find(*args, &block) unless environment raise Error, "manifest requires environment for compilation" end return to_enum(__method__, *args) unless block_given? environment = self.environment.cached promises = args.flatten.map do |path| Concurrent::Promise.execute(executor: executor) do environment.find_all_linked_assets(path).to_a end end promises.each do |promise| promise.value!.each(&block) end nil end # Public: Find the source of assets by paths. # # Returns Enumerator of assets file content. def find_sources(*args) return to_enum(__method__, *args) unless block_given? if environment find(*args).each do |asset| yield asset.source end else args.each do |path| asset = assets[path] yield File.binread(File.join(dir, asset)) if asset end end end # Compile asset to directory. The asset is written to a # fingerprinted filename like # `application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js`. An entry is # also inserted into the manifest file. # # compile("application.js") # def compile(*args) unless environment raise Error, "manifest requires environment for compilation" end filenames = [] concurrent_exporters = [] assets_to_export = Concurrent::Array.new find(*args) do |asset| assets_to_export << asset end assets_to_export.each do |asset| mtime = Time.now.iso8601 files[asset.digest_path] = { 'logical_path' => asset.logical_path, 'mtime' => mtime, 'size' => asset.bytesize, 'digest' => asset.hexdigest, # Deprecated: Remove beta integrity attribute in next release. # Callers should DigestUtils.hexdigest_integrity_uri to compute the # digest themselves. 'integrity' => DigestUtils.hexdigest_integrity_uri(asset.hexdigest) } assets[asset.logical_path] = asset.digest_path filenames << asset.filename promise = nil exporters_for_asset(asset) do |exporter| next if exporter.skip?(logger) if promise.nil? promise = Concurrent::Promise.new(executor: executor) { exporter.call } concurrent_exporters << promise.execute else concurrent_exporters << promise.then { exporter.call } end end end # make sure all exporters have finished before returning the main thread concurrent_exporters.each(&:wait!) save filenames end # Removes file from directory and from manifest. `filename` must # be the name with any directory path. # # manifest.remove("application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js") # def remove(filename) path = File.join(dir, filename) gzip = "#{path}.gz" logical_path = files[filename]['logical_path'] if assets[logical_path] == filename assets.delete(logical_path) end files.delete(filename) FileUtils.rm(path) if File.exist?(path) FileUtils.rm(gzip) if File.exist?(gzip) save logger.info "Removed #{filename}" nil end # Cleanup old assets in the compile directory. By default it will # keep the latest version, 2 backups and any created within the past hour. # # Examples # # To force only 1 backup to be kept, set count=1 and age=0. # # To only keep files created within the last 10 minutes, set count=0 and # age=600. # def clean(count = 2, age = 3600) asset_versions = files.group_by { |_, attrs| attrs['logical_path'] } asset_versions.each do |logical_path, versions| current = assets[logical_path] versions.reject { |path, _| path == current }.sort_by { |_, attrs| # Sort by timestamp Time.parse(attrs['mtime']) }.reverse.each_with_index.drop_while { |(_, attrs), index| _age = [0, Time.now - Time.parse(attrs['mtime'])].max # Keep if under age or within the count limit _age < age || index < count }.each { |(path, _), _| # Remove old assets remove(path) } end end # Wipe directive def clobber FileUtils.rm_r(directory) if File.exist?(directory) logger.info "Removed #{directory}" # if we have an environment clear the cache too environment.cache.clear if environment nil end # Persist manifest back to FS def save data = json_encode(@data) FileUtils.mkdir_p File.dirname(@filename) PathUtils.atomic_write(@filename) do |f| f.write(data) end end private # Given an asset, finds all exporters that # match its mime-type. # # Will yield each expoter to the passed in block. # # array = [] # puts asset.content_type # => "application/javascript" # exporters_for_asset(asset) do |exporter| # array << exporter # end # # puts array => [Exporters::FileExporter, Exporters::ZlibExporter] def exporters_for_asset(asset) exporters = [Exporters::FileExporter] environment.exporters.each do |mime_type, exporter_list| next unless asset.content_type next unless environment.match_mime_type? asset.content_type, mime_type exporter_list.each do |exporter| exporters << exporter end end exporters.uniq! exporters.each do |exporter| yield exporter.new(asset: asset, environment: environment, directory: dir) end end def json_decode(obj) JSON.parse(obj, create_additions: false) end def json_encode(obj) JSON.generate(obj) end def logger if environment environment.logger else logger = Logger.new($stderr) logger.level = Logger::FATAL logger end end def executor @executor ||= environment.export_concurrent ? :fast : :immediate end end end sprockets-4.2.1/lib/sprockets/manifest_utils.rb000066400000000000000000000032711447572140400217040ustar00rootroot00000000000000# frozen_string_literal: true require 'securerandom' require 'logger' module Sprockets # Public: Manifest utilities. module ManifestUtils extend self MANIFEST_RE = /^\.sprockets-manifest-[0-9a-f]{32}.json$/ # Public: Generate a new random manifest path. # # Manifests are not intended to be accessed publicly, but typically live # alongside public assets for convenience. To avoid being served, the # filename is prefixed with a "." which is usually hidden by web servers # like Apache. To help in other environments that may not control this, # a random hex string is appended to the filename to prevent people from # guessing the location. If directory indexes are enabled on the server, # all bets are off. # # Return String path. def generate_manifest_path ".sprockets-manifest-#{SecureRandom.hex(16)}.json" end # Public: Find or pick a new manifest filename for target build directory. # # dirname - String dirname # # Examples # # find_directory_manifest("/app/public/assets") # # => "/app/public/assets/.sprockets-manifest-abc123.json" # # Returns String filename. def find_directory_manifest(dirname, logger = Logger.new($stderr)) entries = File.directory?(dirname) ? Dir.entries(dirname) : [] manifest_entries = entries.select { |e| e =~ MANIFEST_RE } if manifest_entries.length > 1 manifest_entries.sort! logger.warn("Found multiple manifests: #{manifest_entries}. Choosing the first alphabetically: #{manifest_entries.first}") end entry = manifest_entries.first || generate_manifest_path File.join(dirname, entry) end end end sprockets-4.2.1/lib/sprockets/mime.rb000066400000000000000000000050561447572140400176100ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/encoding_utils' require 'sprockets/http_utils' require 'sprockets/utils' module Sprockets module Mime include HTTPUtils, Utils # Public: Mapping of MIME type Strings to properties Hash. # # key - MIME Type String # value - Hash # extensions - Array of extnames # charset - Default Encoding or function to detect encoding # # Returns Hash. def mime_types config[:mime_types] end # Internal: Mapping of MIME extension Strings to MIME type Strings. # # Used for internal fast lookup purposes. # # Examples: # # mime_exts['.js'] #=> 'application/javascript' # # key - MIME extension String # value - MIME Type String # # Returns Hash. def mime_exts config[:mime_exts] end # Public: Register a new mime type. # # mime_type - String MIME Type # extensions - Array of String extnames # charset - Proc/Method that detects the charset of a file. # See EncodingUtils. # # Returns nothing. def register_mime_type(mime_type, extensions: [], charset: nil) extnames = Array(extensions) charset ||= :default if mime_type.start_with?('text/') charset = EncodingUtils::CHARSET_DETECT[charset] if charset.is_a?(Symbol) self.config = hash_reassoc(config, :mime_exts) do |mime_exts| extnames.each do |extname| mime_exts[extname] = mime_type end mime_exts end self.config = hash_reassoc(config, :mime_types) do |mime_types| type = { extensions: extnames } type[:charset] = charset if charset mime_types.merge(mime_type => type) end end # Internal: Get detecter function for MIME type. # # mime_type - String MIME type # # Returns Proc detector or nil if none is available. def mime_type_charset_detecter(mime_type) if type = config[:mime_types][mime_type] if detect = type[:charset] return detect end end end # Public: Read file on disk with MIME type specific encoding. # # filename - String path # content_type - String MIME type # # Returns String file contents transcoded to UTF-8 or in its external # encoding. def read_file(filename, content_type = nil) data = File.binread(filename) if detect = mime_type_charset_detecter(content_type) detect.call(data).encode(Encoding::UTF_8, universal_newline: true) else data end end end end sprockets-4.2.1/lib/sprockets/npm.rb000066400000000000000000000026011447572140400174440ustar00rootroot00000000000000# frozen_string_literal: true require 'json' module Sprockets module Npm # Internal: Override resolve_alternates to install package.json behavior. # # load_path - String environment path # logical_path - String path relative to base # # Returns candidate filenames. def resolve_alternates(load_path, logical_path) candidates, deps = super dirname = File.join(load_path, logical_path) if directory?(dirname) filename = File.join(dirname, 'package.json') if self.file?(filename) deps << build_file_digest_uri(filename) read_package_directives(dirname, filename) do |path| if file?(path) candidates << path end end end end return candidates, deps end # Internal: Read package.json's main and style directives. # # dirname - String path to component directory. # filename - String path to package.json. # # Returns nothing. def read_package_directives(dirname, filename) package = JSON.parse(File.read(filename), create_additions: false) case package['main'] when String yield File.expand_path(package['main'], dirname) when nil yield File.expand_path('index.js', dirname) end yield File.expand_path(package['style'], dirname) if package['style'] end end end sprockets-4.2.1/lib/sprockets/path_dependency_utils.rb000066400000000000000000000050221447572140400232240ustar00rootroot00000000000000# frozen_string_literal: true require 'set' require 'sprockets/path_utils' require 'sprockets/uri_utils' module Sprockets # Internal: Related PathUtils helpers that also track all the file system # calls they make for caching purposes. All functions return a standard # return value and a Set of cache dependency URIs that can be used in the # future to see if the returned value should be invalidated from cache. # # entries_with_dependencies("app/assets/javascripts") # # => [ # # ["application.js", "projects.js", "users.js", ...] # # # # # ] # # The returned dependency set can be passed to resolve_dependencies(deps) # to check if the returned result is still fresh. In this case, entry always # returns a single path, but multiple calls should accumulate dependencies # into a single set thats saved off and checked later. # # resolve_dependencies(deps) # # => "\x01\x02\x03" # # Later, resolving the same set again will produce a different hash if # something on the file system has changed. # # resolve_dependencies(deps) # # => "\x03\x04\x05" # module PathDependencyUtils include PathUtils include URIUtils # Internal: List directory entries and return a set of dependencies that # would invalid the cached return result. # # See PathUtils#entries # # path - String directory path # # Returns an Array of entry names and a Set of dependency URIs. def entries_with_dependencies(path) return entries(path), Set.new([build_file_digest_uri(path)]) end # Internal: List directory filenames and associated Stats under a # directory. # # See PathUtils#stat_directory # # dir - A String directory # # Returns an Array of filenames and a Set of dependency URIs. def stat_directory_with_dependencies(dir) return stat_directory(dir).to_a, Set.new([build_file_digest_uri(dir)]) end # Internal: List directory filenames and associated Stats under an entire # directory tree. # # See PathUtils#stat_sorted_tree # # dir - A String directory # # Returns an Array of filenames and a Set of dependency URIs. def stat_sorted_tree_with_dependencies(dir) deps = Set.new([build_file_digest_uri(dir)]) results = stat_sorted_tree(dir).map do |path, stat| deps << build_file_digest_uri(path) if stat.directory? [path, stat] end return results, deps end end end sprockets-4.2.1/lib/sprockets/path_digest_utils.rb000066400000000000000000000024211447572140400223650ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/digest_utils' require 'sprockets/path_utils' module Sprockets # Internal: Crossover of path and digest utilities functions. module PathDigestUtils include DigestUtils, PathUtils # Internal: Compute digest for file stat. # # path - String filename # stat - File::Stat # # Returns String digest bytes. def stat_digest(path, stat) if stat.directory? # If its a directive, digest the list of filenames digest_class.digest(self.entries(path).join(','.freeze)) elsif stat.file? # If its a file, digest the contents digest_class.file(path.to_s).digest else raise TypeError, "stat was not a directory or file: #{stat.ftype}" end end # Internal: Compute digest for path. # # path - String filename or directory path. # # Returns String digest bytes or nil. def file_digest(path) if stat = self.stat(path) self.stat_digest(path, stat) end end # Internal: Compute digest for a set of paths. # # paths - Array of filename or directory paths. # # Returns String digest bytes. def files_digest(paths) self.digest(paths.map { |path| self.file_digest(path) }) end end end sprockets-4.2.1/lib/sprockets/path_utils.rb000066400000000000000000000233361447572140400210360ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets # Internal: File and path related utilities. Mixed into Environment. # # Probably would be called FileUtils, but that causes namespace annoyances # when code actually wants to reference ::FileUtils. module PathUtils extend self require 'pathname' # Public: Like `File.stat`. # # path - String file or directory path # # Returns nil if the file does not exist. def stat(path) if File.exist?(path) File.stat(path.to_s) else nil end end # Public: Like `File.file?`. # # path - String file path. # # Returns true path exists and is a file. def file?(path) if stat = self.stat(path) stat.file? else false end end # Public: Like `File.directory?`. # # path - String file path. # # Returns true path exists and is a directory. def directory?(path) if stat = self.stat(path) stat.directory? else false end end # Public: A version of `Dir.entries` that filters out `.` files and `~` # swap files. # # path - String directory path # # Returns an empty `Array` if the directory does not exist. def entries(path) if File.directory?(path) entries = Dir.entries(path, encoding: Encoding.default_internal) entries.reject! { |entry| entry.start_with?(".".freeze) || (entry.start_with?("#".freeze) && entry.end_with?("#".freeze)) || entry.end_with?("~".freeze) } entries.sort! entries else [] end end # Public: Check if path is absolute or relative. # # path - String path. # # Returns true if path is absolute, otherwise false. if File::ALT_SEPARATOR # On Windows, ALT_SEPARATOR is \ # Delegate to Pathname since the logic gets complex. def absolute_path?(path) Pathname.new(path).absolute? end else def absolute_path?(path) path.start_with?(File::SEPARATOR) end end if File::ALT_SEPARATOR SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}|#{Regexp.quote(File::ALT_SEPARATOR)}" else SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}" end # Public: Check if path is explicitly relative. # Starts with "./" or "../". # # path - String path. # # Returns true if path is relative, otherwise false. def relative_path?(path) path.match?(/^\.\.?($|#{SEPARATOR_PATTERN})/) ? true : false end # Public: Get relative path from `start` to `dest`. # # start - String start path (file or dir) # dest - String destination path # # Returns relative String path from `start` to `dest` def relative_path_from(start, dest) start, dest = Pathname.new(start), Pathname.new(dest) start = start.dirname unless start.directory? dest.relative_path_from(start).to_s end # Public: Joins path to base path. # # base - Root path # path - Extending path # # Example # # join('base/path/', '../file.js') # # => 'base/file.js' # # Returns string path starting from base and ending at path def join(base, path) (Pathname.new(base) + path).to_s end # Public: Sets pipeline for path # # path - String path # extensions - List of file extensions # pipeline - Pipeline # # Examples # # set_pipeline('path/file.js.erb', config[:mime_exts], config[:pipeline_exts], :source) # # => 'path/file.source.js.erb' # # set_pipeline('path/some.file.source.js.erb', config[:mime_exts], config[:pipeline_exts], :debug) # # => 'path/some.file.debug.js.erb' # # Returns string path with pipeline parsed in def set_pipeline(path, mime_exts, pipeline_exts, pipeline) extension, _ = match_path_extname(path, mime_exts) path.chomp!(extension) pipeline_old, _ = match_path_extname(path, pipeline_exts) path.chomp!(pipeline_old) "#{path}.#{pipeline}#{extension}" end # Internal: Get relative path for root path and subpath. # # path - String path # subpath - String subpath of path # # Returns relative String path if subpath is a subpath of path, or nil if # subpath is outside of path. def split_subpath(path, subpath) return "" if path == subpath path = File.join(path, ''.freeze) if subpath&.start_with?(path) subpath[path.length..-1] else nil end end # Internal: Detect root path and base for file in a set of paths. # # paths - Array of String paths # filename - String path of file expected to be in one of the paths. # # Returns [String root, String path] def paths_split(paths, filename) paths.each do |path| if subpath = split_subpath(path, filename) return path, subpath end end nil end # Internal: Get path's extensions. # # path - String # # Returns an Array of String extnames. def path_extnames(path) File.basename(path).scan(/\.[^.]+/) end # Internal: Match path extnames against available extensions. # # path - String # extensions - Hash of String extnames to values # # Returns [String extname, Object value] or nil nothing matched. def match_path_extname(path, extensions) basename = File.basename(path) i = basename.index('.'.freeze) while i && i < basename.length - 1 extname = basename[i..-1] if value = extensions[extname] return extname, value end i = basename.index('.'.freeze, i+1) end nil end # Internal: Match paths in a directory against available extensions. # # path - String directory # basename - String basename of target file # extensions - Hash of String extnames to values # # Examples # # exts = { ".js" => "application/javascript" } # find_matching_path_for_extensions("app/assets", "application", exts) # # => ["app/assets/application.js", "application/javascript"] # # Returns an Array of [String path, Object value] matches. def find_matching_path_for_extensions(path, basename, extensions) matches = [] entries(path).each do |entry| next unless File.basename(entry).start_with?(basename) extname, value = match_path_extname(entry, extensions) if basename == entry.chomp(extname) filename = File.join(path, entry) if file?(filename) matches << [filename, value] end end end matches end # Internal: Returns all parents for path # # path - String absolute filename or directory # root - String path to stop at (default: system root) # # Returns an Array of String paths. def path_parents(path, root = nil) root = "#{root}#{File::SEPARATOR}" if root parents = [] loop do parent = File.dirname(path) break if parent == path break if root && !path.start_with?(root) parents << path = parent end parents end # Internal: Find target basename checking upwards from path. # # basename - String filename: ".sprocketsrc" # path - String path to start search: "app/assets/javascripts/app.js" # root - String path to stop at (default: system root) # # Returns String filename or nil. def find_upwards(basename, path, root = nil) path_parents(path, root).each do |dir| filename = File.join(dir, basename) return filename if file?(filename) end nil end # Public: Stat all the files under a directory. # # dir - A String directory # # Returns an Enumerator of [path, stat]. def stat_directory(dir) return to_enum(__method__, dir) unless block_given? self.entries(dir).each do |entry| path = File.join(dir, entry) if stat = self.stat(path) yield path, stat end end nil end # Public: Recursive stat all the files under a directory. # # dir - A String directory # # Returns an Enumerator of [path, stat]. def stat_tree(dir, &block) return to_enum(__method__, dir) unless block_given? self.stat_directory(dir) do |path, stat| yield path, stat if stat.directory? stat_tree(path, &block) end end nil end # Public: Recursive stat all the files under a directory in alphabetical # order. # # dir - A String directory # # Returns an Enumerator of [path, stat]. def stat_sorted_tree(dir, &block) return to_enum(__method__, dir) unless block_given? self.stat_directory(dir).sort_by { |path, stat| stat.directory? ? "#{path}/" : path }.each do |path, stat| yield path, stat if stat.directory? stat_sorted_tree(path, &block) end end nil end # Public: Write to a file atomically. Useful for situations where you # don't want other processes or threads to see half-written files. # # Utils.atomic_write('important.file') do |file| # file.write('hello') # end # # Returns nothing. def atomic_write(filename) dirname, basename = File.split(filename) basename = [ basename, Thread.current.object_id, Process.pid, rand(1000000) ].join('.'.freeze) tmpname = File.join(dirname, basename) File.open(tmpname, 'wb+') do |f| yield f end File.rename(tmpname, filename) ensure File.delete(tmpname) if File.exist?(tmpname) end end end sprockets-4.2.1/lib/sprockets/paths.rb000066400000000000000000000040301447572140400177670ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/path_utils' require 'sprockets/utils' module Sprockets module Paths include PathUtils, Utils # Returns `Environment` root. # # All relative paths are expanded with root as its base. To be # useful set this to your applications root directory. (`Rails.root`) def root config[:root] end # Internal: Change Environment root. # # Only the initializer should change the root. def root=(path) self.config = hash_reassoc(config, :root) do File.expand_path(path) end end private :root= # Returns an `Array` of path `String`s. # # These paths will be used for asset logical path lookups. def paths config[:paths] end # Prepend a `path` to the `paths` list. # # Paths at the end of the `Array` have the least priority. def prepend_path(path) self.config = hash_reassoc(config, :paths) do |paths| path = File.expand_path(path, config[:root]).freeze paths.unshift(path) end end # Append a `path` to the `paths` list. # # Paths at the beginning of the `Array` have a higher priority. def append_path(path) self.config = hash_reassoc(config, :paths) do |paths| path = File.expand_path(path, config[:root]).freeze paths.push(path) end end # Clear all paths and start fresh. # # There is no mechanism for reordering paths, so its best to # completely wipe the paths list and reappend them in the order # you want. def clear_paths self.config = hash_reassoc(config, :paths) do |paths| paths.clear end end # Public: Iterate over every file under all load paths. # # Returns Enumerator if no block is given. def each_file return to_enum(__method__) unless block_given? paths.each do |root| stat_tree(root).each do |filename, stat| if stat.file? yield filename end end end nil end end end sprockets-4.2.1/lib/sprockets/preprocessors/000077500000000000000000000000001447572140400212375ustar00rootroot00000000000000sprockets-4.2.1/lib/sprockets/preprocessors/default_source_map.rb000066400000000000000000000030761447572140400254330ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets module Preprocessors # Private: Adds a default map to assets when one is not present # # If the input file already has a source map, it effectively returns the original # result. Otherwise it maps 1 for 1 lines original to generated. This is needed # Because other generators run after might depend on having a valid source map # available. class DefaultSourceMap def call(input) result = { data: input[:data] } map = input[:metadata][:map] filename = input[:filename] load_path = input[:load_path] lines = input[:data].lines.length basename = File.basename(filename) mime_exts = input[:environment].config[:mime_exts] pipeline_exts = input[:environment].config[:pipeline_exts] if map.nil? || map.empty? result[:map] = { "version" => 3, "file" => PathUtils.split_subpath(load_path, filename), "mappings" => default_mappings(lines), "sources" => [PathUtils.set_pipeline(basename, mime_exts, pipeline_exts, :source)], "names" => [] } else result[:map] = map end result[:map]["x_sprockets_linecount"] = lines return result end private def default_mappings(lines) if (lines == 0) "" elsif (lines == 1) "AAAA" else "AAAA;" + "AACA;"*(lines - 2) + "AACA" end end end end end sprockets-4.2.1/lib/sprockets/processing.rb000066400000000000000000000160271447572140400210350ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/file_reader' require 'sprockets/mime' require 'sprockets/processor_utils' require 'sprockets/uri_utils' require 'sprockets/utils' module Sprockets # `Processing` is an internal mixin whose public methods are exposed on # the `Environment` and `CachedEnvironment` classes. module Processing include ProcessorUtils, URIUtils, Utils def pipelines config[:pipelines] end # Registers a pipeline that will be called by `call_processor` method. def register_pipeline(name, proc = nil, &block) proc ||= block self.config = hash_reassoc(config, :pipeline_exts) do |pipeline_exts| pipeline_exts.merge(".#{name}".freeze => name.to_sym) end self.config = hash_reassoc(config, :pipelines) do |pipelines| pipelines.merge(name.to_sym => proc) end end # Preprocessors are ran before Postprocessors and Engine # processors. def preprocessors config[:preprocessors] end alias_method :processors, :preprocessors # Postprocessors are ran after Preprocessors and Engine processors. def postprocessors config[:postprocessors] end # Registers a new Preprocessor `klass` for `mime_type`. # # register_preprocessor 'text/css', Sprockets::DirectiveProcessor # # A block can be passed for to create a shorthand processor. # # register_preprocessor 'text/css' do |input| # input[:data].gsub(...) # end # def register_preprocessor(*args, &block) register_config_processor(:preprocessors, *args, &block) compute_transformers!(self.config[:registered_transformers]) end alias_method :register_processor, :register_preprocessor # Registers a new Postprocessor `klass` for `mime_type`. # # register_postprocessor 'application/javascript', Sprockets::DirectiveProcessor # # A block can be passed for to create a shorthand processor. # # register_postprocessor 'application/javascript' do |input| # input[:data].gsub(...) # end # def register_postprocessor(*args, &block) register_config_processor(:postprocessors, *args, &block) compute_transformers!(self.config[:registered_transformers]) end # Remove Preprocessor `klass` for `mime_type`. # # unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor # def unregister_preprocessor(*args) unregister_config_processor(:preprocessors, *args) compute_transformers!(self.config[:registered_transformers]) end alias_method :unregister_processor, :unregister_preprocessor # Remove Postprocessor `klass` for `mime_type`. # # unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor # def unregister_postprocessor(*args) unregister_config_processor(:postprocessors, *args) compute_transformers!(self.config[:registered_transformers]) end # Bundle Processors are ran on concatenated assets rather than # individual files. def bundle_processors config[:bundle_processors] end # Registers a new Bundle Processor `klass` for `mime_type`. # # register_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor # # A block can be passed for to create a shorthand processor. # # register_bundle_processor 'application/javascript' do |input| # input[:data].gsub(...) # end # def register_bundle_processor(*args, &block) register_config_processor(:bundle_processors, *args, &block) end # Remove Bundle Processor `klass` for `mime_type`. # # unregister_bundle_processor 'application/javascript', Sprockets::DirectiveProcessor # def unregister_bundle_processor(*args) unregister_config_processor(:bundle_processors, *args) end # Public: Register bundle metadata reducer function. # # Examples # # Sprockets.register_bundle_metadata_reducer 'application/javascript', :jshint_errors, [], :+ # # Sprockets.register_bundle_metadata_reducer 'text/css', :selector_count, 0 { |total, count| # total + count # } # # mime_type - String MIME Type. Use '*/*' applies to all types. # key - Symbol metadata key # initial - Initial memo to pass to the reduce function (default: nil) # block - Proc accepting the memo accumulator and current value # # Returns nothing. def register_bundle_metadata_reducer(mime_type, key, *args, &block) case args.size when 0 reducer = block when 1 if block_given? initial = args[0] reducer = block else initial = nil reducer = args[0].to_proc end when 2 initial = args[0] reducer = args[1].to_proc else raise ArgumentError, "wrong number of arguments (#{args.size} for 0..2)" end self.config = hash_reassoc(config, :bundle_reducers, mime_type) do |reducers| reducers.merge(key => [initial, reducer]) end end protected def resolve_processors_cache_key_uri(uri) params = parse_uri_query_params(uri[11..-1]) processors = processors_for(params[:type], params[:file_type], params[:pipeline]) processors_cache_keys(processors) end def build_processors_uri(type, file_type, pipeline) query = encode_uri_query_params( type: type, file_type: file_type, pipeline: pipeline ) "processors:#{query}" end def processors_for(type, file_type, pipeline) pipeline ||= :default if fn = config[:pipelines][pipeline.to_sym] fn.call(self, type, file_type) else raise Error, "no pipeline: #{pipeline}" end end def default_processors_for(type, file_type) bundled_processors = config[:bundle_processors][type] if bundled_processors.any? bundled_processors else self_processors_for(type, file_type) end end def self_processors_for(type, file_type) processors = [] processors.concat config[:postprocessors][type] if type != file_type && processor = config[:transformers][file_type][type] processors << processor end processors.concat config[:preprocessors][file_type] if processors.any? || mime_type_charset_detecter(type) processors << FileReader end processors end private def register_config_processor(type, mime_type, processor = nil, &block) processor ||= block self.config = hash_reassoc(config, type, mime_type) do |processors| processors.unshift(processor) processors end end def unregister_config_processor(type, mime_type, processor) self.config = hash_reassoc(config, type, mime_type) do |processors| processors.delete_if { |p| p == processor || p.class == processor } processors end end end end sprockets-4.2.1/lib/sprockets/processor_utils.rb000066400000000000000000000125121447572140400221130ustar00rootroot00000000000000# frozen_string_literal: true require 'set' module Sprockets # Functional utilities for dealing with Processor functions. # # A Processor is a general function that may modify or transform an asset as # part of the pipeline. CoffeeScript to JavaScript conversion, Minification # or Concatenation are all implemented as separate Processor steps. # # Processors maybe any object that responds to call. So procs or a class that # defines a self.call method. # # For ergonomics, processors may return a number of shorthand values. # Unfortunately, this means that processors can not compose via ordinary # function composition. The composition helpers here can help. module ProcessorUtils extend self class CompositeProcessor < Struct.new(:processor_strategy, :param, :processors) # :nodoc: SINGULAR = lambda { |param, input| ProcessorUtils.call_processor param, input } PLURAL = lambda { |param, input| ProcessorUtils.call_processors param, input } def self.create(processors) if processors.length == 1 new SINGULAR, processors.first, processors else new PLURAL, processors, processors end end def call(input) processor_strategy.call param, input end def cache_key ProcessorUtils.processors_cache_keys(processors) end end # Public: Compose processors in right to left order. # # processors - Array of processors callables # # Returns a composed Proc. def compose_processors(*processors) CompositeProcessor.create processors end # Public: Invoke list of processors in right to left order. # # The right to left order processing mirrors standard function composition. # Think about: # # bundle.call(uglify.call(coffee.call(input))) # # processors - Array of processor callables # input - Hash of input data to pass to each processor # # Returns a Hash with :data and other processor metadata key/values. def call_processors(processors, input) data = input[:data] || "" metadata = (input[:metadata] || {}).dup processors.reverse_each do |processor| result = call_processor(processor, input.merge(data: data, metadata: metadata)) data = result.delete(:data) metadata.merge!(result) end metadata.merge(data: data) end # Public: Invoke processor. # # processor - Processor callables # input - Hash of input data to pass to processor # # Returns a Hash with :data and other processor metadata key/values. def call_processor(processor, input) metadata = (input[:metadata] || {}).dup metadata[:data] = input[:data] case result = processor.call({data: "", metadata: {}}.merge(input)) when NilClass metadata when Hash metadata.merge(result) when String metadata.merge(data: result) else raise TypeError, "invalid processor return type: #{result.class}" end end # Internal: Get processor defined cached key. # # processor - Processor function # # Returns JSON serializable key or nil. def processor_cache_key(processor) processor.cache_key if processor.respond_to?(:cache_key) end # Internal: Get combined cache keys for set of processors. # # processors - Array of processor functions # # Returns Array of JSON serializable keys. def processors_cache_keys(processors) processors.map { |processor| processor_cache_key(processor) } end # Internal: Set of all "simple" value types allowed to be returned in # processor metadata. VALID_METADATA_VALUE_TYPES = Set.new([ String, Symbol, TrueClass, FalseClass, NilClass, Integer ]).freeze # Internal: Set of all nested compound metadata types that can nest values. VALID_METADATA_COMPOUND_TYPES = Set.new([ Array, Hash, Set ]).freeze # Internal: Hash of all "simple" value types allowed to be returned in # processor metadata. VALID_METADATA_VALUE_TYPES_HASH = VALID_METADATA_VALUE_TYPES.each_with_object({}) do |type, hash| hash[type] = true end.freeze # Internal: Hash of all nested compound metadata types that can nest values. VALID_METADATA_COMPOUND_TYPES_HASH = VALID_METADATA_COMPOUND_TYPES.each_with_object({}) do |type, hash| hash[type] = true end.freeze # Internal: Set of all allowed metadata types. VALID_METADATA_TYPES = (VALID_METADATA_VALUE_TYPES + VALID_METADATA_COMPOUND_TYPES).freeze # Internal: Validate returned result of calling a processor pipeline and # raise a friendly user error message. # # result - Metadata Hash returned from call_processors # # Returns result or raises a TypeError. def validate_processor_result!(result) if !result.instance_of?(Hash) raise TypeError, "processor metadata result was expected to be a Hash, but was #{result.class}" end if !result[:data].instance_of?(String) raise TypeError, "processor :data was expected to be a String, but as #{result[:data].class}" end result.each do |key, value| if !key.instance_of?(Symbol) raise TypeError, "processor metadata[#{key.inspect}] expected to be a Symbol" end end result end end end sprockets-4.2.1/lib/sprockets/resolve.rb000066400000000000000000000265551447572140400203470ustar00rootroot00000000000000# frozen_string_literal: true require 'set' require 'sprockets/http_utils' require 'sprockets/path_dependency_utils' require 'sprockets/uri_utils' module Sprockets module Resolve include HTTPUtils, PathDependencyUtils, URIUtils # Public: Find Asset URI for given a logical path by searching the # environment's load paths. # # resolve("application.js") # # => "file:///path/to/app/javascripts/application.js?type=application/javascript" # # An accept content type can be given if the logical path doesn't have a # format extension. # # resolve("application", accept: "application/javascript") # # => "file:///path/to/app/javascripts/application.coffee?type=application/javascript" # # The String Asset URI is returned or nil if no results are found. def resolve(path, load_paths: config[:paths], accept: nil, pipeline: nil, base_path: nil) paths = load_paths if valid_asset_uri?(path) uri, deps = resolve_asset_uri(path) elsif absolute_path?(path) filename, type, deps = resolve_absolute_path(paths, path, accept) elsif relative_path?(path) filename, type, path_pipeline, deps, index_alias = resolve_relative_path(paths, path, base_path, accept) else filename, type, path_pipeline, deps, index_alias = resolve_logical_path(paths, path, accept) end if filename uri = build_asset_uri(filename, type: type, pipeline: pipeline || path_pipeline, index_alias: index_alias) end return uri, deps end # Public: Same as resolve() but raises a FileNotFound exception instead of # nil if no assets are found. def resolve!(path, **kargs) uri, deps = resolve(path, **kargs) unless uri message = +"couldn't find file '#{path}'" if relative_path?(path) && kargs[:base_path] load_path, _ = paths_split(config[:paths], kargs[:base_path]) message << " under '#{load_path}'" end message << " with type '#{kargs[:accept]}'" if kargs[:accept] load_paths = kargs[:load_paths] || config[:paths] message << "\nChecked in these paths: \n #{ load_paths.join("\n ") }" raise FileNotFound, message end return uri, deps end protected # Internal: Finds an asset given a URI # # uri - String. Contains file:// scheme, absolute path to # file. # e.g. "file:///Users/schneems/sprockets/test/fixtures/default/gallery.js?type=application/javascript" # # Returns Array. Contains a String uri and Set of dependencies def resolve_asset_uri(uri) filename, _ = URIUtils.parse_asset_uri(uri) return uri, Set.new( [URIUtils.build_file_digest_uri(filename)] ) end # Internal: Finds a file in a set of given paths # # paths - Array of Strings. # filename - String containing absolute path to a file including extension. # e.g. "/Users/schneems/sprockets/test/fixtures/asset/application.js" # accept - String. A Quality value incoded set of # mime types that we are looking for. Can be nil. # e.g. "application/javascript" or "text/css, */*" # # Returns Array. Filename, type, path_pipeline, deps, index_alias def resolve_absolute_path(paths, filename, accept) deps = Set.new filename = File.expand_path(filename) # Ensure path is under load paths return nil, nil, deps unless PathUtils.paths_split(paths, filename) _, mime_type = PathUtils.match_path_extname(filename, config[:mime_exts]) type = resolve_transform_type(mime_type, accept) return nil, nil, deps if accept && !type return nil, nil, deps unless file?(filename) deps << URIUtils.build_file_digest_uri(filename) return filename, type, deps end # Internal: Finds a relative file in a set of given paths # # paths - Array of Strings. # path - String. A relative filename with or without extension # e.g. "./jquery" or "../foo.js" # dirname - String. Base path where we start looking for the given file. # accept - String. A Quality value incoded set of # mime types that we are looking for. Can be nil. # e.g. "application/javascript" or "text/css, */*" # # Returns Array. Filename, type, path_pipeline, deps, index_alias def resolve_relative_path(paths, path, dirname, accept) filename = File.expand_path(path, dirname) load_path, _ = PathUtils.paths_split(paths, dirname) if load_path && logical_path = PathUtils.split_subpath(load_path, filename) resolve_logical_path([load_path], logical_path, accept) else return nil, nil, nil, Set.new end end # Internal: Finds a file in a set of given paths # # paths - Array of Strings. # logical_path - String. A filename with extension # e.g. "coffee/foo.js" or "foo.js" # accept - String. A Quality value incoded set of # mime types that we are looking for. Can be nil. # e.g. "application/javascript" or "text/css, */*" # # Finds a file on the given paths. # # Returns Array. Filename, type, path_pipeline, deps, index_alias def resolve_logical_path(paths, logical_path, accept) extname, mime_type = PathUtils.match_path_extname(logical_path, config[:mime_exts]) logical_name = logical_path.chomp(extname) extname, pipeline = PathUtils.match_path_extname(logical_name, config[:pipeline_exts]) logical_name = logical_name.chomp(extname) parsed_accept = parse_accept_options(mime_type, accept) transformed_accepts = expand_transform_accepts(parsed_accept) filename, mime_type, deps, index_alias = resolve_under_paths(paths, logical_name, transformed_accepts) if filename deps << build_file_digest_uri(filename) type = resolve_transform_type(mime_type, parsed_accept) return filename, type, pipeline, deps, index_alias else return nil, nil, nil, deps end end # Internal: Finds a file in a set of given paths # # paths - Array of Strings. # logical_name - String. A filename without extension # e.g. "application" or "coffee/foo" # accepts - Array of array containing mime/version pairs # e.g. [["application/javascript", 1.0]] # # Finds a file with the same name as `logical_name` or "index" inside # of the `logical_name` directory that matches a valid mime-type/version from # `accepts`. # # Returns Array. Filename, type, dependencies, and index_alias def resolve_under_paths(paths, logical_name, accepts) deps = Set.new return nil, nil, deps if accepts.empty? # TODO: Allow new path resolves to be registered @resolvers ||= [ method(:resolve_main_under_path), method(:resolve_alts_under_path), method(:resolve_index_under_path) ] mime_exts = config[:mime_exts] paths.each do |load_path| candidates = [] @resolvers.each do |fn| result = fn.call(load_path, logical_name, mime_exts) candidates.concat(result[0]) deps.merge(result[1]) end candidate = HTTPUtils.find_best_q_match(accepts, candidates) do |c, matcher| match_mime_type?(c[:type] || "application/octet-stream", matcher) end return candidate[:filename], candidate[:type], deps, candidate[:index_alias] if candidate end return nil, nil, deps end # Internal: Finds candidate files on a given path # # load_path - String. An absolute path to a directory # logical_name - String. A filename without extension # e.g. "application" or "coffee/foo" # mime_exts - Hash of file extensions and their mime types # e.g. {".xml.builder"=>"application/xml+builder"} # # Finds files that match a given `logical_name` with an acceptable # mime type that is included in `mime_exts` on the `load_path`. # # Returns Array. First element is an Array of hashes or empty, second is a String def resolve_main_under_path(load_path, logical_name, mime_exts) dirname = File.dirname(File.join(load_path, logical_name)) candidates = self.find_matching_path_for_extensions(dirname, File.basename(logical_name), mime_exts) candidates.map! do |c| { filename: c[0], type: c[1] } end return candidates, [ URIUtils.build_file_digest_uri(dirname) ] end # Internal: Finds candidate index files in a given path # # load_path - String. An absolute path to a directory # logical_name - String. A filename without extension # e.g. "application" or "coffee/foo" # mime_exts - Hash of file extensions and their mime types # e.g. {".xml.builder"=>"application/xml+builder"} # # Looking in the given `load_path` this method will find all files under the `logical_name` directory # that are named `index` and have a matching mime type in `mime_exts`. # # Returns Array. First element is an Array of hashes or empty, second is a String def resolve_index_under_path(load_path, logical_name, mime_exts) dirname = File.join(load_path, logical_name) if self.directory?(dirname) candidates = self.find_matching_path_for_extensions(dirname, "index".freeze, mime_exts) else candidates = [] end candidates.map! do |c| { filename: c[0], type: c[1], index_alias: compress_from_root(c[0].sub(/\/index(\.[^\/]+)$/, '\1')) } end return candidates, [ URIUtils.build_file_digest_uri(dirname) ] end def resolve_alts_under_path(load_path, logical_name, mime_exts) filenames, deps = self.resolve_alternates(load_path, logical_name) filenames.map! do |fn| _, mime_type = PathUtils.match_path_extname(fn, mime_exts) { filename: fn, type: mime_type } end return filenames, deps end # Internal: Converts mimetype into accept Array # # - mime_type - String, optional. e.g. "text/html" # - explicit_type - String, optional. e.g. "application/javascript" # # When called with an explicit_type and a mime_type, only a mime_type # that matches the given explicit_type will be accepted. # # Returns Array of Array # # [["application/javascript", 1.0]] # [["*/*", 1.0]] # [] def parse_accept_options(mime_type, explicit_type) if mime_type return [[mime_type, 1.0]] if explicit_type.nil? return [[mime_type, 1.0]] if HTTPUtils.parse_q_values(explicit_type).any? { |accept, _| HTTPUtils.match_mime_type?(mime_type, accept) } return [] end accepts = HTTPUtils.parse_q_values(explicit_type) accepts << ['*/*'.freeze, 1.0] if accepts.empty? return accepts end def resolve_alternates(load_path, logical_name) return [], Set.new end end end sprockets-4.2.1/lib/sprockets/sass_cache_store.rb000066400000000000000000000013461447572140400221670ustar00rootroot00000000000000# frozen_string_literal: true require 'sass' module Sprockets class SassProcessor # Internal: Cache wrapper for Sprockets cache adapter. class CacheStore < ::Sass::CacheStores::Base VERSION = '1' def initialize(cache, version) @cache, @version = cache, "#{VERSION}/#{version}" end def _store(key, version, sha, contents) @cache.set("#{@version}/#{version}/#{key}/#{sha}", contents, true) end def _retrieve(key, version, sha) @cache.get("#{@version}/#{version}/#{key}/#{sha}", true) end def path_to(key) key end end end # Deprecated: Use Sprockets::SassProcessor::CacheStore instead. SassCacheStore = SassProcessor::CacheStore end sprockets-4.2.1/lib/sprockets/sass_compressor.rb000066400000000000000000000030541447572140400221020ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/digest_utils' require 'sprockets/source_map_utils' module Sprockets # Public: Sass CSS minifier. # # To accept the default options # # environment.register_bundle_processor 'text/css', # Sprockets::SassCompressor # # Or to pass options to the Sass::Engine class. # # environment.register_bundle_processor 'text/css', # Sprockets::SassCompressor.new({ ... }) # class SassCompressor VERSION = '1' # Public: Return singleton instance with default options. # # Returns SassCompressor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key def initialize(options = {}) @options = { syntax: :scss, cache: false, read_cache: false, style: :compressed }.merge(options).freeze @cache_key = "#{self.class.name}:#{Autoload::Sass::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze end def call(input) css, map = Autoload::Sass::Engine.new( input[:data], @options.merge(filename: input[:filename]) ).render_with_sourcemap('') css = css.sub("/*# sourceMappingURL= */\n", '') map = SourceMapUtils.format_source_map(JSON.parse(map.to_json(css_uri: '')), input) map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map) { data: css, map: map } end end end sprockets-4.2.1/lib/sprockets/sass_functions.rb000066400000000000000000000001701447572140400217120ustar00rootroot00000000000000# frozen_string_literal: true # Deprecated: Require sprockets/sass_processor instead require 'sprockets/sass_processor' sprockets-4.2.1/lib/sprockets/sass_importer.rb000066400000000000000000000001701447572140400215430ustar00rootroot00000000000000# frozen_string_literal: true # Deprecated: Require sprockets/sass_processor instead require 'sprockets/sass_processor' sprockets-4.2.1/lib/sprockets/sass_processor.rb000066400000000000000000000212641447572140400217300ustar00rootroot00000000000000# frozen_string_literal: true require 'rack/utils' require 'sprockets/autoload' require 'sprockets/source_map_utils' require 'uri' module Sprockets # Processor engine class for the SASS/SCSS compiler. Depends on the `sass` gem. # # For more information see: # # https://github.com/sass/sass # https://github.com/rails/sass-rails # class SassProcessor autoload :CacheStore, 'sprockets/sass_cache_store' # Internal: Defines default sass syntax to use. Exposed so the ScssProcessor # may override it. def self.syntax :sass end # Public: Return singleton instance with default options. # # Returns SassProcessor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key # Public: Initialize template with custom options. # # options - Hash # cache_version - String custom cache version. Used to force a cache # change after code changes are made to Sass Functions. # def initialize(options = {}, &block) @cache_version = options[:cache_version] @cache_key = "#{self.class.name}:#{VERSION}:#{Autoload::Sass::VERSION}:#{@cache_version}".freeze @importer_class = options[:importer] || Sass::Importers::Filesystem @sass_config = options[:sass_config] || {} @functions = Module.new do include Functions include options[:functions] if options[:functions] class_eval(&block) if block_given? end end def call(input) context = input[:environment].context_class.new(input) engine_options = merge_options({ filename: input[:filename], syntax: self.class.syntax, cache_store: build_cache_store(input, @cache_version), load_paths: context.environment.paths.map { |p| @importer_class.new(p.to_s) }, importer: @importer_class.new(Pathname.new(context.filename).to_s), sprockets: { context: context, environment: input[:environment], dependencies: context.metadata[:dependencies] } }) engine = Autoload::Sass::Engine.new(input[:data], engine_options) css, map = Utils.module_include(Autoload::Sass::Script::Functions, @functions) do engine.render_with_sourcemap('') end css = css.sub("\n/*# sourceMappingURL= */\n", '') map = SourceMapUtils.format_source_map(JSON.parse(map.to_json(css_uri: '')), input) map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map) # Track all imported files sass_dependencies = Set.new([input[:filename]]) engine.dependencies.map do |dependency| sass_dependencies << dependency.options[:filename] context.metadata[:dependencies] << URIUtils.build_file_digest_uri(dependency.options[:filename]) end context.metadata.merge(data: css, sass_dependencies: sass_dependencies, map: map) end private # Public: Build the cache store to be used by the Sass engine. # # input - the input hash. # version - the cache version. # # Override this method if you need to use a different cache than the # Sprockets cache. def build_cache_store(input, version) CacheStore.new(input[:cache], version) end def merge_options(options) defaults = @sass_config.dup if load_paths = defaults.delete(:load_paths) options[:load_paths] += load_paths end options.merge!(defaults) options end # Public: Functions injected into Sass context during Sprockets evaluation. # # This module may be extended to add global functionality to all Sprockets # Sass environments. Though, scoping your functions to just your environment # is preferred. # # module Sprockets::SassProcessor::Functions # def asset_path(path, options = {}) # end # end # module Functions # Public: Generate a url for asset path. # # Default implementation is deprecated. Currently defaults to # Context#asset_path. # # Will raise NotImplementedError in the future. Users should provide their # own base implementation. # # Returns a Sass::Script::String. def asset_path(path, options = {}) path = path.value path, _, query, fragment = URI.split(path)[5..8] path = sprockets_context.asset_path(path, options) query = "?#{query}" if query fragment = "##{fragment}" if fragment Autoload::Sass::Script::String.new("#{path}#{query}#{fragment}", :string) end # Public: Generate a asset url() link. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def asset_url(path, options = {}) Autoload::Sass::Script::String.new("url(#{asset_path(path, options).value})") end # Public: Generate url for image path. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def image_path(path) asset_path(path, type: :image) end # Public: Generate a image url() link. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def image_url(path) asset_url(path, type: :image) end # Public: Generate url for video path. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def video_path(path) asset_path(path, type: :video) end # Public: Generate a video url() link. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def video_url(path) asset_url(path, type: :video) end # Public: Generate url for audio path. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def audio_path(path) asset_path(path, type: :audio) end # Public: Generate a audio url() link. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def audio_url(path) asset_url(path, type: :audio) end # Public: Generate url for font path. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def font_path(path) asset_path(path, type: :font) end # Public: Generate a font url() link. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def font_url(path) asset_url(path, type: :font) end # Public: Generate url for javascript path. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def javascript_path(path) asset_path(path, type: :javascript) end # Public: Generate a javascript url() link. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def javascript_url(path) asset_url(path, type: :javascript) end # Public: Generate url for stylesheet path. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def stylesheet_path(path) asset_path(path, type: :stylesheet) end # Public: Generate a stylesheet url() link. # # path - Sass::Script::String URL path # # Returns a Sass::Script::String. def stylesheet_url(path) asset_url(path, type: :stylesheet) end # Public: Generate a data URI for asset path. # # path - Sass::Script::String logical asset path # # Returns a Sass::Script::String. def asset_data_url(path) url = sprockets_context.asset_data_uri(path.value) Autoload::Sass::Script::String.new("url(" + url + ")") end protected # Public: The Environment. # # Returns Sprockets::Environment. def sprockets_environment options[:sprockets][:environment] end # Public: Mutatable set of dependencies. # # Returns a Set. def sprockets_dependencies options[:sprockets][:dependencies] end # Deprecated: Get the Context instance. Use APIs on # sprockets_environment or sprockets_dependencies directly. # # Returns a Context instance. def sprockets_context options[:sprockets][:context] end end end class ScssProcessor < SassProcessor def self.syntax :scss end end # Deprecated: Use Sprockets::SassProcessor::Functions instead. SassFunctions = SassProcessor::Functions end sprockets-4.2.1/lib/sprockets/sassc_compressor.rb000066400000000000000000000030711447572140400222440ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/source_map_utils' module Sprockets # Public: Sass CSS minifier. # # To accept the default options # # environment.register_bundle_processor 'text/css', # Sprockets::SasscCompressor # # Or to pass options to the Sass::Engine class. # # environment.register_bundle_processor 'text/css', # Sprockets::SasscCompressor.new({ ... }) # class SasscCompressor # Public: Return singleton instance with default options. # # Returns SasscCompressor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def initialize(options = {}) @options = { syntax: :scss, style: :compressed, source_map_contents: false, omit_source_map_url: true, }.merge(options).freeze end def call(input) # SassC requires the template to be modifiable input_data = input[:data].frozen? ? input[:data].dup : input[:data] engine = Autoload::SassC::Engine.new(input_data, @options.merge(filename: input[:filename], source_map_file: "#{input[:filename]}.map")) css = engine.render.sub(/^\n^\/\*# sourceMappingURL=.*\*\/$/m, '') begin map = SourceMapUtils.format_source_map(JSON.parse(engine.source_map), input) map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map) rescue SassC::NotRenderedError map = input[:metadata][:map] end { data: css, map: map } end end end sprockets-4.2.1/lib/sprockets/sassc_processor.rb000066400000000000000000000201661447572140400220730ustar00rootroot00000000000000# frozen_string_literal: true require 'rack/utils' require 'sprockets/autoload' require 'sprockets/source_map_utils' require 'uri' module Sprockets # Processor engine class for the SASS/SCSS compiler. Depends on the `sassc` gem. # # For more information see: # # https://github.com/sass/sassc-ruby # https://github.com/sass/sassc-rails # class SasscProcessor # Internal: Defines default sass syntax to use. Exposed so the ScsscProcessor # may override it. def self.syntax :sass end # Public: Return singleton instance with default options. # # Returns SasscProcessor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key def initialize(options = {}, &block) @cache_version = options[:cache_version] @cache_key = "#{self.class.name}:#{VERSION}:#{Autoload::SassC::VERSION}:#{@cache_version}".freeze @importer_class = options[:importer] @sass_config = options[:sass_config] || {} @functions = Module.new do include Functions include options[:functions] if options[:functions] class_eval(&block) if block_given? end end def call(input) context = input[:environment].context_class.new(input) options = engine_options(input, context) engine = Autoload::SassC::Engine.new(input[:data], options) css = Utils.module_include(Autoload::SassC::Script::Functions, @functions) do engine.render.sub(/^\n^\/\*# sourceMappingURL=.*\*\/$/m, '') end begin map = SourceMapUtils.format_source_map(JSON.parse(engine.source_map), input) map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map) engine.dependencies.each do |dependency| context.metadata[:dependencies] << URIUtils.build_file_digest_uri(dependency.filename) end rescue SassC::NotRenderedError map = input[:metadata][:map] end context.metadata.merge(data: css, map: map) end private def merge_options(options) defaults = @sass_config.dup if load_paths = defaults.delete(:load_paths) options[:load_paths] += load_paths end options.merge!(defaults) options end # Public: Functions injected into Sass context during Sprockets evaluation. # # This module may be extended to add global functionality to all Sprockets # Sass environments. Though, scoping your functions to just your environment # is preferred. # # module Sprockets::SasscProcessor::Functions # def asset_path(path, options = {}) # end # end # module Functions # Public: Generate a url for asset path. # # Default implementation is deprecated. Currently defaults to # Context#asset_path. # # Will raise NotImplementedError in the future. Users should provide their # own base implementation. # # Returns a SassC::Script::Value::String. def asset_path(path, options = {}) path = path.value path, _, query, fragment = URI.split(path)[5..8] path = sprockets_context.asset_path(path, options) query = "?#{query}" if query fragment = "##{fragment}" if fragment Autoload::SassC::Script::Value::String.new("#{path}#{query}#{fragment}", :string) end # Public: Generate a asset url() link. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def asset_url(path, options = {}) Autoload::SassC::Script::Value::String.new("url(#{asset_path(path, options).value})") end # Public: Generate url for image path. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def image_path(path) asset_path(path, type: :image) end # Public: Generate a image url() link. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def image_url(path) asset_url(path, type: :image) end # Public: Generate url for video path. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def video_path(path) asset_path(path, type: :video) end # Public: Generate a video url() link. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def video_url(path) asset_url(path, type: :video) end # Public: Generate url for audio path. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def audio_path(path) asset_path(path, type: :audio) end # Public: Generate a audio url() link. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def audio_url(path) asset_url(path, type: :audio) end # Public: Generate url for font path. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def font_path(path) asset_path(path, type: :font) end # Public: Generate a font url() link. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def font_url(path) asset_url(path, type: :font) end # Public: Generate url for javascript path. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def javascript_path(path) asset_path(path, type: :javascript) end # Public: Generate a javascript url() link. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def javascript_url(path) asset_url(path, type: :javascript) end # Public: Generate url for stylesheet path. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def stylesheet_path(path) asset_path(path, type: :stylesheet) end # Public: Generate a stylesheet url() link. # # path - SassC::Script::Value::String URL path # # Returns a SassC::Script::Value::String. def stylesheet_url(path) asset_url(path, type: :stylesheet) end # Public: Generate a data URI for asset path. # # path - SassC::Script::Value::String logical asset path # # Returns a SassC::Script::Value::String. def asset_data_url(path) url = sprockets_context.asset_data_uri(path.value) Autoload::SassC::Script::Value::String.new("url(" + url + ")") end protected # Public: The Environment. # # Returns Sprockets::Environment. def sprockets_environment options[:sprockets][:environment] end # Public: Mutatable set of dependencies. # # Returns a Set. def sprockets_dependencies options[:sprockets][:dependencies] end # Deprecated: Get the Context instance. Use APIs on # sprockets_environment or sprockets_dependencies directly. # # Returns a Context instance. def sprockets_context options[:sprockets][:context] end end def engine_options(input, context) merge_options({ filename: input[:filename], syntax: self.class.syntax, load_paths: input[:environment].paths, importer: @importer_class, source_map_contents: false, source_map_file: "#{input[:filename]}.map", omit_source_map_url: true, sprockets: { context: context, environment: input[:environment], dependencies: context.metadata[:dependencies] } }) end end class ScsscProcessor < SasscProcessor def self.syntax :scss end end end sprockets-4.2.1/lib/sprockets/server.rb000066400000000000000000000231131447572140400201610ustar00rootroot00000000000000# frozen_string_literal: true require 'set' require 'time' require 'rack' module Sprockets # `Server` is a concern mixed into `Environment` and # `CachedEnvironment` that provides a Rack compatible `call` # interface and url generation helpers. module Server # Supported HTTP request methods. ALLOWED_REQUEST_METHODS = ['GET', 'HEAD'].to_set.freeze # :stopdoc: if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3") X_CASCADE = "X-Cascade" VARY = "Vary" else X_CASCADE = "x-cascade" VARY = "vary" end # :startdoc: # `call` implements the Rack 1.x specification which accepts an # `env` Hash and returns a three item tuple with the status code, # headers, and body. # # Mapping your environment at a url prefix will serve all assets # in the path. # # map "/assets" do # run Sprockets::Environment.new # end # # A request for `"/assets/foo/bar.js"` will search your # environment for `"foo/bar.js"`. def call(env) start_time = Time.now.to_f time_elapsed = lambda { ((Time.now.to_f - start_time) * 1000).to_i } unless ALLOWED_REQUEST_METHODS.include? env['REQUEST_METHOD'] return method_not_allowed_response end msg = "Served asset #{env['PATH_INFO']} -" # Extract the path from everything after the leading slash full_path = Rack::Utils.unescape(env['PATH_INFO'].to_s.sub(/^\//, '')) path = full_path unless path.valid_encoding? return bad_request_response(env) end # Strip fingerprint if fingerprint = path_fingerprint(path) path = path.sub("-#{fingerprint}", '') end # URLs containing a `".."` are rejected for security reasons. if forbidden_request?(path) return forbidden_response(env) end if fingerprint if_match = fingerprint elsif env['HTTP_IF_MATCH'] if_match = env['HTTP_IF_MATCH'][/"(\w+)"$/, 1] end if env['HTTP_IF_NONE_MATCH'] if_none_match = env['HTTP_IF_NONE_MATCH'][/"(\w+)"$/, 1] end # Look up the asset. asset = find_asset(path) # Fallback to looking up the asset with the full path. # This will make assets that are hashed with webpack or # other js bundlers work consistently between production # and development pipelines. if asset.nil? && (asset = find_asset(full_path)) if_match = asset.etag if fingerprint fingerprint = asset.etag end if asset.nil? status = :not_found elsif fingerprint && asset.etag != fingerprint status = :not_found elsif if_match && asset.etag != if_match status = :precondition_failed elsif if_none_match && asset.etag == if_none_match status = :not_modified else status = :ok end case status when :ok logger.info "#{msg} 200 OK (#{time_elapsed.call}ms)" ok_response(asset, env) when :not_modified logger.info "#{msg} 304 Not Modified (#{time_elapsed.call}ms)" not_modified_response(env, if_none_match) when :not_found logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)" not_found_response(env) when :precondition_failed logger.info "#{msg} 412 Precondition Failed (#{time_elapsed.call}ms)" precondition_failed_response(env) end rescue Exception => e logger.error "Error compiling asset #{path}:" logger.error "#{e.class.name}: #{e.message}" case File.extname(path) when ".js" # Re-throw JavaScript asset exceptions to the browser logger.info "#{msg} 500 Internal Server Error\n\n" return javascript_exception_response(e) when ".css" # Display CSS asset exceptions in the browser logger.info "#{msg} 500 Internal Server Error\n\n" return css_exception_response(e) else raise end end private def forbidden_request?(path) # Prevent access to files elsewhere on the file system # # http://example.org/assets/../../../etc/passwd # path.include?("..") || absolute_path?(path) || path.include?("://") end def head_request?(env) env['REQUEST_METHOD'] == 'HEAD' end # Returns a 200 OK response tuple def ok_response(asset, env) if head_request?(env) [ 200, headers(env, asset, 0), [] ] else [ 200, headers(env, asset, asset.length), asset ] end end # Returns a 304 Not Modified response tuple def not_modified_response(env, etag) [ 304, cache_headers(env, etag), [] ] end # Returns a 400 Forbidden response tuple def bad_request_response(env) if head_request?(env) [ 400, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0" }, [] ] else [ 400, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "11" }, [ "Bad Request" ] ] end end # Returns a 403 Forbidden response tuple def forbidden_response(env) if head_request?(env) [ 403, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0" }, [] ] else [ 403, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "9" }, [ "Forbidden" ] ] end end # Returns a 404 Not Found response tuple def not_found_response(env) if head_request?(env) [ 404, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0", X_CASCADE => "pass" }, [] ] else [ 404, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "9", X_CASCADE => "pass" }, [ "Not found" ] ] end end def method_not_allowed_response [ 405, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "18" }, [ "Method Not Allowed" ] ] end def precondition_failed_response(env) if head_request?(env) [ 412, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0", X_CASCADE => "pass" }, [] ] else [ 412, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "19", X_CASCADE => "pass" }, [ "Precondition Failed" ] ] end end # Returns a JavaScript response that re-throws a Ruby exception # in the browser def javascript_exception_response(exception) err = "#{exception.class.name}: #{exception.message}\n (in #{exception.backtrace[0]})" body = "throw Error(#{err.inspect})" [ 200, { Rack::CONTENT_TYPE => "application/javascript", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [ body ] ] end # Returns a CSS response that hides all elements on the page and # displays the exception def css_exception_response(exception) message = "\n#{exception.class.name}: #{exception.message}" backtrace = "\n #{exception.backtrace.first}" body = <<-CSS html { padding: 18px 36px; } head { display: block; } body { margin: 0; padding: 0; } body > * { display: none !important; } head:after, body:before, body:after { display: block !important; } head:after { font-family: sans-serif; font-size: large; font-weight: bold; content: "Error compiling CSS asset"; } body:before, body:after { font-family: monospace; white-space: pre-wrap; } body:before { font-weight: bold; content: "#{escape_css_content(message)}"; } body:after { content: "#{escape_css_content(backtrace)}"; } CSS [ 200, { Rack::CONTENT_TYPE => "text/css; charset=utf-8", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [ body ] ] end # Escape special characters for use inside a CSS content("...") string def escape_css_content(content) content. gsub('\\', '\\\\005c '). gsub("\n", '\\\\000a '). gsub('"', '\\\\0022 '). gsub('/', '\\\\002f ') end def cache_headers(env, etag) headers = {} # Set caching headers headers[Rack::CACHE_CONTROL] = +"public" headers[Rack::ETAG] = %("#{etag}") # If the request url contains a fingerprint, set a long # expires on the response if path_fingerprint(env["PATH_INFO"]) headers[Rack::CACHE_CONTROL] << ", max-age=31536000, immutable" # Otherwise set `must-revalidate` since the asset could be modified. else headers[Rack::CACHE_CONTROL] << ", must-revalidate" headers[VARY] = "Accept-Encoding" end headers end def headers(env, asset, length) headers = {} # Set content length header headers[Rack::CONTENT_LENGTH] = length.to_s # Set content type header if type = asset.content_type # Set charset param for text/* mime types if type.start_with?("text/") && asset.charset type += "; charset=#{asset.charset}" end headers[Rack::CONTENT_TYPE] = type end headers.merge(cache_headers(env, asset.etag)) end # Gets ETag fingerprint. # # "foo-0aa2105d29558f3eb790d411d7d8fb66.js" # # => "0aa2105d29558f3eb790d411d7d8fb66" # def path_fingerprint(path) path[/-([0-9a-zA-Z]{7,128})\.[^.]+\z/, 1] end end end sprockets-4.2.1/lib/sprockets/source_map_processor.rb000066400000000000000000000043171447572140400231140ustar00rootroot00000000000000# frozen_string_literal: true require 'set' module Sprockets # The purpose of this class is to generate a source map file # that can be read and understood by browsers. # # When a file is passed in it will have a `application/js-sourcemap+json` # or `application/css-sourcemap+json` mime type. The filename will be # match the original asset. The original asset is loaded. As it # gets processed by Sprockets it will acquire all information # needed to build a source map file in the `asset.to_hash[:metadata][:map]` # key. # # The output is an asset with a properly formatted source map file: # # { # "version": 3, # "sources": ["foo.js"], # "names": [ ], # "mappings": "AAAA,GAAIA" # } # class SourceMapProcessor def self.call(input) links = Set.new(input[:metadata][:links]) env = input[:environment] uri, _ = env.resolve!(input[:filename], accept: self.original_content_type(input[:content_type])) asset = env.load(uri) map = asset.metadata[:map] # TODO: Because of the default piplene hack we have to apply dependencies # from compiled asset to the source map, otherwise the source map cache # will never detect the changes from directives dependencies = Set.new(input[:metadata][:dependencies]) dependencies.merge(asset.metadata[:dependencies]) map["file"] = PathUtils.split_subpath(input[:load_path], input[:filename]) sources = map["sections"] ? map["sections"].map { |s| s["map"]["sources"] }.flatten : map["sources"] sources.each do |source| source = PathUtils.join(File.dirname(map["file"]), source) uri, _ = env.resolve!(source) links << uri end json = JSON.generate(map) { data: json, links: links, dependencies: dependencies } end def self.original_content_type(source_map_content_type, error_when_not_found: true) case source_map_content_type when "application/js-sourcemap+json" "application/javascript" when "application/css-sourcemap+json" "text/css" else fail(source_map_content_type) if error_when_not_found source_map_content_type end end end end sprockets-4.2.1/lib/sprockets/source_map_utils.rb000066400000000000000000000335541447572140400222420ustar00rootroot00000000000000# frozen_string_literal: true require 'json' require 'sprockets/path_utils' module Sprockets module SourceMapUtils extend self # Public: Transpose source maps into a standard format # # NOTE: Does not support index maps # # version => 3 # file => logical path # sources => relative from filename # # Unnecessary attributes are removed # # Example # # map # #=> { # # "version" => 3, # # "file" => "stdin", # # "sourceRoot" => "", # # "sourceContents" => "blah blah blah", # # "sources" => [/root/logical/path.js], # # "names" => [..], # #} # format_source_map(map, input) # #=> { # # "version" => 3, # # "file" => "logical/path.js", # # "sources" => ["path.js"], # # "names" => [..], # #} def format_source_map(map, input) filename = input[:filename] load_path = input[:load_path] load_paths = input[:environment].config[:paths] mime_exts = input[:environment].config[:mime_exts] pipeline_exts = input[:environment].config[:pipeline_exts] file = PathUtils.split_subpath(load_path, filename) { "version" => 3, "file" => file, "mappings" => map["mappings"], "sources" => map["sources"].map do |source| source = URIUtils.split_file_uri(source)[2] if source.start_with? "file://" source = PathUtils.join(File.dirname(filename), source) unless PathUtils.absolute_path?(source) _, source = PathUtils.paths_split(load_paths, source) source = PathUtils.relative_path_from(file, source) PathUtils.set_pipeline(source, mime_exts, pipeline_exts, :source) end, "names" => map["names"] } end # Public: Concatenate two source maps. # # For an example, if two js scripts are concatenated, the individual source # maps for those files can be concatenated to map back to the originals. # # Examples # # script3 = "#{script1}#{script2}" # map3 = concat_source_maps(map1, map2) # # a - Source map hash # b - Source map hash # # Returns a new source map hash. def concat_source_maps(a, b) return a || b unless a && b a = make_index_map(a) b = make_index_map(b) offset = 0 if a["sections"].count != 0 && !a["sections"].last["map"]["mappings"].empty? last_line_count = a["sections"].last["map"].delete("x_sprockets_linecount") offset += last_line_count || 1 last_offset = a["sections"].last["offset"]["line"] offset += last_offset end a["sections"] += b["sections"].map do |section| { "offset" => section["offset"].merge({ "line" => section["offset"]["line"] + offset }), "map" => section["map"].merge({ "sources" => section["map"]["sources"].map do |source| PathUtils.relative_path_from(a["file"], PathUtils.join(File.dirname(b["file"]), source)) end }) } end a end # Public: Converts source map to index map # # Example: # # map # # => { # "version" => 3, # "file" => "..", # "mappings" => "AAAA;AACA;..;AACA", # "sources" => [..], # "names" => [..] # } # make_index_map(map) # # => { # "version" => 3, # "file" => "..", # "sections" => [ # { # "offset" => { "line" => 0, "column" => 0 }, # "map" => { # "version" => 3, # "file" => "..", # "mappings" => "AAAA;AACA;..;AACA", # "sources" => [..], # "names" => [..] # } # } # ] # } def make_index_map(map) return map if map.key? "sections" { "version" => map["version"], "file" => map["file"], "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => map } ] } end # Public: Combine two separate source map transformations into a single # mapping. # # Source transformations may happen in discrete steps producing separate # source maps. These steps can be combined into a single mapping back to # the source. # # For an example, CoffeeScript may transform a file producing a map. Then # Uglifier processes the result and produces another map. The CoffeeScript # map can be combined with the Uglifier map so the source lines of the # minified output can be traced back to the original CoffeeScript file. # # Returns a source map hash. def combine_source_maps(first, second) return second unless first _first = decode_source_map(first) _second = decode_source_map(second) new_mappings = [] _second[:mappings].each do |m| first_line = bsearch_mappings(_first[:mappings], m[:original]) new_mappings << first_line.merge(generated: m[:generated]) if first_line end _first[:mappings] = new_mappings encode_source_map(_first) end # Public: Decompress source map # # Example: # # decode_source_map(map) # # => { # version: 3, # file: "..", # mappings: [ # { source: "..", generated: [0, 0], original: [0, 0], name: ".."}, .. # ], # sources: [..], # names: [..] # } # # map - Source map hash (v3 spec) # # Returns an uncompressed source map hash def decode_source_map(map) return nil unless map mappings, sources, names = [], [], [] if map["sections"] map["sections"].each do |s| mappings += decode_source_map(s["map"])[:mappings].each do |m| m[:generated][0] += s["offset"]["line"] m[:generated][1] += s["offset"]["column"] end sources |= s["map"]["sources"] names |= s["map"]["names"] end else mappings = decode_vlq_mappings(map["mappings"], sources: map["sources"], names: map["names"]) sources = map["sources"] names = map["names"] end { version: 3, file: map["file"], mappings: mappings, sources: sources, names: names } end # Public: Compress source map # # Example: # # encode_source_map(map) # # => { # "version" => 3, # "file" => "..", # "mappings" => "AAAA;AACA;..;AACA", # "sources" => [..], # "names" => [..] # } # # map - Source map hash (uncompressed) # # Returns a compressed source map hash according to source map spec v3 def encode_source_map(map) return nil unless map { "version" => map[:version], "file" => map[:file], "mappings" => encode_vlq_mappings(map[:mappings], sources: map[:sources], names: map[:names]), "sources" => map[:sources], "names" => map[:names] } end # Public: Compare two source map offsets. # # Compatible with Array#sort. # # a - Array [line, column] # b - Array [line, column] # # Returns -1 if a < b, 0 if a == b and 1 if a > b. def compare_source_offsets(a, b) diff = a[0] - b[0] diff = a[1] - b[1] if diff == 0 if diff < 0 -1 elsif diff > 0 1 else 0 end end # Public: Search Array of mappings for closest offset. # # mappings - Array of mapping Hash objects # offset - Array [line, column] # # Returns mapping Hash object. def bsearch_mappings(mappings, offset, from = 0, to = mappings.size - 1) mid = (from + to) / 2 if from > to return from < 1 ? nil : mappings[from-1] end case compare_source_offsets(offset, mappings[mid][:generated]) when 0 mappings[mid] when -1 bsearch_mappings(mappings, offset, from, mid - 1) when 1 bsearch_mappings(mappings, offset, mid + 1, to) end end # Public: Decode VLQ mappings and match up sources and symbol names. # # str - VLQ string from 'mappings' attribute # sources - Array of Strings from 'sources' attribute # names - Array of Strings from 'names' attribute # # Returns an Array of Mappings. def decode_vlq_mappings(str, sources: [], names: []) mappings = [] source_id = 0 original_line = 1 original_column = 0 name_id = 0 vlq_decode_mappings(str).each_with_index do |group, index| generated_column = 0 generated_line = index + 1 group.each do |segment| generated_column += segment[0] generated = [generated_line, generated_column] if segment.size >= 4 source_id += segment[1] original_line += segment[2] original_column += segment[3] source = sources[source_id] original = [original_line, original_column] else # TODO: Research this case next end if segment[4] name_id += segment[4] name = names[name_id] end mapping = {source: source, generated: generated, original: original} mapping[:name] = name if name mappings << mapping end end mappings end # Public: Encode mappings Hash into a VLQ encoded String. # # mappings - Array of Hash mapping objects # sources - Array of String sources (default: mappings source order) # names - Array of String names (default: mappings name order) # # Returns a VLQ encoded String. def encode_vlq_mappings(mappings, sources: nil, names: nil) sources ||= mappings.map { |m| m[:source] }.uniq.compact names ||= mappings.map { |m| m[:name] }.uniq.compact sources_index = Hash[sources.each_with_index.to_a] names_index = Hash[names.each_with_index.to_a] source_id = 0 source_line = 1 source_column = 0 name_id = 0 by_lines = mappings.group_by { |m| m[:generated][0] } ary = (1..(by_lines.keys.max || 1)).map do |line| generated_column = 0 (by_lines[line] || []).map do |mapping| group = [] group << mapping[:generated][1] - generated_column group << sources_index[mapping[:source]] - source_id group << mapping[:original][0] - source_line group << mapping[:original][1] - source_column group << names_index[mapping[:name]] - name_id if mapping[:name] generated_column = mapping[:generated][1] source_id = sources_index[mapping[:source]] source_line = mapping[:original][0] source_column = mapping[:original][1] name_id = names_index[mapping[:name]] if mapping[:name] group end end vlq_encode_mappings(ary) end # Public: Base64 VLQ encoding # # Adopted from ConradIrwin/ruby-source_map # https://github.com/ConradIrwin/ruby-source_map/blob/master/lib/source_map/vlq.rb # # Resources # # http://en.wikipedia.org/wiki/Variable-length_quantity # https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit # https://github.com/mozilla/source-map/blob/master/lib/source-map/base64-vlq.js # VLQ_BASE_SHIFT = 5 VLQ_BASE = 1 << VLQ_BASE_SHIFT VLQ_BASE_MASK = VLQ_BASE - 1 VLQ_CONTINUATION_BIT = VLQ_BASE BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('') BASE64_VALUES = (0...64).inject({}) { |h, i| h[BASE64_DIGITS[i]] = i; h } # Public: Encode a list of numbers into a compact VLQ string. # # ary - An Array of Integers # # Returns a VLQ String. def vlq_encode(ary) result = [] ary.each do |n| vlq = n < 0 ? ((-n) << 1) + 1 : n << 1 loop do digit = vlq & VLQ_BASE_MASK vlq >>= VLQ_BASE_SHIFT digit |= VLQ_CONTINUATION_BIT if vlq > 0 result << BASE64_DIGITS[digit] break unless vlq > 0 end end result.join end # Public: Decode a VLQ string. # # str - VLQ encoded String # # Returns an Array of Integers. def vlq_decode(str) result = [] shift = 0 value = 0 i = 0 while i < str.size do digit = BASE64_VALUES[str[i]] raise ArgumentError unless digit continuation = (digit & VLQ_CONTINUATION_BIT) != 0 digit &= VLQ_BASE_MASK value += digit << shift if continuation shift += VLQ_BASE_SHIFT else result << ((value & 1) == 1 ? -(value >> 1) : value >> 1) value = shift = 0 end i += 1 end result end # Public: Encode a mapping array into a compact VLQ string. # # ary - Two dimensional Array of Integers. # # Returns a VLQ encoded String separated by , and ;. def vlq_encode_mappings(ary) ary.map { |group| group.map { |segment| vlq_encode(segment) }.join(',') }.join(';') end # Public: Decode a VLQ string into mapping numbers. # # str - VLQ encoded String # # Returns an two dimensional Array of Integers. def vlq_decode_mappings(str) mappings = [] str.split(';').each_with_index do |group, index| mappings[index] = [] group.split(',').each do |segment| mappings[index] << vlq_decode(segment) end end mappings end end end sprockets-4.2.1/lib/sprockets/transformers.rb000066400000000000000000000133471447572140400214100ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/http_utils' require 'sprockets/processor_utils' require 'sprockets/utils' module Sprockets module Transformers include HTTPUtils, ProcessorUtils, Utils # Public: Two level mapping of a source mime type to a target mime type. # # environment.transformers # # => { 'text/coffeescript' => { # 'application/javascript' => ConvertCoffeeScriptToJavaScript # } # } # def transformers config[:transformers] end Transformer = Struct.new :from, :to, :proc # Public: Register a transformer from and to a mime type. # # from - String mime type # to - String mime type # proc - Callable block that accepts an input Hash. # # Examples # # register_transformer 'text/coffeescript', 'application/javascript', # ConvertCoffeeScriptToJavaScript # # register_transformer 'image/svg+xml', 'image/png', ConvertSvgToPng # # Returns nothing. def register_transformer(from, to, proc) self.config = hash_reassoc(config, :registered_transformers) do |transformers| transformers << Transformer.new(from, to, proc) end compute_transformers!(self.config[:registered_transformers]) end # Internal: Register transformer for existing type adding a suffix. # # types - Array of existing mime type Strings # type_format - String suffix formatting string # extname - String extension to append # processor - Callable block that accepts an input Hash. # # Returns nothing. def register_transformer_suffix(types, type_format, extname, processor) Array(types).each do |type| extensions, charset = mime_types[type].values_at(:extensions, :charset) parts = type.split('/') suffix_type = type_format.sub('\1', parts[0]).sub('\2', parts[1]) extensions = extensions.map { |ext| "#{ext}#{extname}" } register_mime_type(suffix_type, extensions: extensions, charset: charset) register_transformer(suffix_type, type, processor) end end # Internal: Resolve target mime type that the source type should be # transformed to. # # type - String from mime type # accept - String accept type list (default: '*/*') # # Examples # # resolve_transform_type('text/plain', 'text/plain') # # => 'text/plain' # # resolve_transform_type('image/svg+xml', 'image/png, image/*') # # => 'image/png' # # resolve_transform_type('text/css', 'image/png') # # => nil # # Returns String mime type or nil is no type satisfied the accept value. def resolve_transform_type(type, accept) find_best_mime_type_match(accept || '*/*', [type].compact + config[:transformers][type].keys) end # Internal: Expand accept type list to include possible transformed types. # # parsed_accepts - Array of accept q values # # Examples # # expand_transform_accepts([['application/javascript', 1.0]]) # # => [['application/javascript', 1.0], ['text/coffeescript', 0.8]] # # Returns an expanded Array of q values. def expand_transform_accepts(parsed_accepts) accepts = [] parsed_accepts.each do |(type, q)| accepts.push([type, q]) config[:inverted_transformers][type].each do |subtype| accepts.push([subtype, q * 0.8]) end end accepts end # Internal: Compose multiple transformer steps into a single processor # function. # # transformers - Two level Hash of a source mime type to a target mime type # types - Array of mime type steps # # Returns Processor. def compose_transformers(transformers, types, preprocessors, postprocessors) if types.length < 2 raise ArgumentError, "too few transform types: #{types.inspect}" end processors = types.each_cons(2).map { |src, dst| unless processor = transformers[src][dst] raise ArgumentError, "missing transformer for type: #{src} to #{dst}" end processor } compose_transformer_list processors, preprocessors, postprocessors end private def compose_transformer_list(transformers, preprocessors, postprocessors) processors = [] transformers.each do |processor| processors.concat postprocessors[processor.from] processors << processor.proc processors.concat preprocessors[processor.to] end if processors.size > 1 compose_processors(*processors.reverse) elsif processors.size == 1 processors.first end end def compute_transformers!(registered_transformers) preprocessors = self.config[:preprocessors] postprocessors = self.config[:postprocessors] transformers = Hash.new { {} } inverted_transformers = Hash.new { Set.new } incoming_edges = registered_transformers.group_by(&:from) registered_transformers.each do |t| traversals = dfs_paths([t]) { |k| incoming_edges.fetch(k.to, []) } traversals.each do |nodes| src, dst = nodes.first.from, nodes.last.to processor = compose_transformer_list nodes, preprocessors, postprocessors transformers[src] = {} unless transformers.key?(src) transformers[src][dst] = processor inverted_transformers[dst] = Set.new unless inverted_transformers.key?(dst) inverted_transformers[dst] << src end end self.config = hash_reassoc(config, :transformers) { transformers } self.config = hash_reassoc(config, :inverted_transformers) { inverted_transformers } end end end sprockets-4.2.1/lib/sprockets/uglifier_compressor.rb000066400000000000000000000033471447572140400227440ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/digest_utils' require 'sprockets/source_map_utils' module Sprockets # Public: Uglifier/Uglify compressor. # # To accept the default options # # environment.register_bundle_processor 'application/javascript', # Sprockets::UglifierCompressor # # Or to pass options to the Uglifier class. # # environment.register_bundle_processor 'application/javascript', # Sprockets::UglifierCompressor.new(comments: :copyright) # class UglifierCompressor VERSION = '3' # Public: Return singleton instance with default options. # # Returns UglifierCompressor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key def initialize(options = {}) options[:comments] ||= :none @options = options @cache_key = "#{self.class.name}:#{Autoload::Uglifier::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze end def call(input) case Autoload::Uglifier::VERSION.to_i when 1 raise "uglifier 1.x is no longer supported, please upgrade to 2.x or newer" when 2 input_options = { source_filename: input[:filename] } else input_options = { source_map: { filename: input[:filename] } } end uglifier = Autoload::Uglifier.new(@options.merge(input_options)) js, map = uglifier.compile_with_map(input[:data]) map = SourceMapUtils.format_source_map(JSON.parse(map), input) map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map) { data: js, map: map } end end end sprockets-4.2.1/lib/sprockets/unloaded_asset.rb000066400000000000000000000121761447572140400216540ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/uri_utils' require 'sprockets/uri_tar' module Sprockets # Internal: Used to parse and store the URI to an unloaded asset # Generates keys used to store and retrieve items from cache class UnloadedAsset # Internal: Initialize object for generating cache keys # # uri - A String containing complete URI to a file including scheme # and full path such as # "file:///Path/app/assets/js/app.js?type=application/javascript" # env - The current "environment" that assets are being loaded into. # We need it so we know where the +root+ (directory where Sprockets # is being invoked). We also need it for the `file_digest` method, # since, for some strange reason, memoization is provided by # overriding methods such as `stat` in the `PathUtils` module. # # Returns UnloadedAsset. def initialize(uri, env) @uri = uri.to_s @env = env @compressed_path = URITar.new(uri, env).compressed_path @params = nil # lazy loaded @filename = nil # lazy loaded end attr_reader :compressed_path, :uri # Internal: Full file path without schema # # This returns a string containing the full path to the asset without the schema. # Information is loaded lazily since we want `UnloadedAsset.new(dep, self).relative_path` # to be fast. Calling this method the first time allocates an array and a hash. # # Example # # If the URI is `file:///Full/path/app/assets/javascripts/application.js"` then the # filename would be `"/Full/path/app/assets/javascripts/application.js"` # # Returns a String. def filename unless @filename load_file_params end @filename end # Internal: Hash of param values # # This information is generated and used internally by Sprockets. # Known keys include `:type` which stores the asset's mime-type, `:id` which is a fully resolved # digest for the asset (includes dependency digest as opposed to a digest of only file contents) # and `:pipeline`. Hash may be empty. # # Example # # If the URI is `file:///Full/path/app/assets/javascripts/application.js"type=application/javascript` # Then the params would be `{type: "application/javascript"}` # # Returns a Hash. def params unless @params load_file_params end @params end # Internal: Key of asset # # Used to retrieve an asset from the cache based on "compressed" path to asset. # A "compressed" path can either be relative to the root of the project or an # absolute path. # # Returns a String. def asset_key "asset-uri:#{compressed_path}" end # Public: Dependency History key # # Used to retrieve an array of "histories" each of which contains a set of stored dependencies # for a given asset path and filename digest. # # A dependency can refer to either an asset e.g. index.js # may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect # compilation, such as the VERSION of Sprockets (i.e. the environment) and what "processors" # are used. # # For example a history array with one Set of dependencies may look like: # # [["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css", # "file-digest:///Full/path/app/assets/stylesheets/application.css", # "processors:type=text/css&file_type=text/css&pipeline=self", # "file-digest:///Full/path/app/assets/stylesheets"]] # # This method of asset lookup is used to ensure that none of the dependencies have been modified # since last lookup. If one of them has, the key will be different and a new entry must be stored. # # URI dependencies are later converted to "compressed" paths # # Returns a String. def dependency_history_key "asset-uri-cache-dependencies:#{compressed_path}:#{ @env.file_digest(filename) }" end # Internal: Digest key # # Used to retrieve a string containing the "compressed" path to an asset based on # a digest. The digest is generated from dependencies stored via information stored in # the `dependency_history_key` after each of the "dependencies" is "resolved". # For example "environment-version" may be resolved to "environment-1.0-3.2.0" # for version "3.2.0" of Sprockets # # Returns a String. def digest_key(digest) "asset-uri-digest:#{compressed_path}:#{digest}" end # Internal: File digest key # # The digest for a given file won't change if the path and the stat time hasn't changed # We can save time by not re-computing this information and storing it in the cache # # Returns a String. def file_digest_key(stat) "file_digest:#{compressed_path}:#{stat}" end private # Internal: Parses uri into filename and params hash # # Returns Array with filename and params hash def load_file_params @filename, @params = URIUtils.parse_asset_uri(uri) end end end sprockets-4.2.1/lib/sprockets/uri_tar.rb000066400000000000000000000053011447572140400203170ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/path_utils' module Sprockets # Internal: used to "expand" and "compress" values for storage class URITar attr_reader :scheme, :root, :path # Internal: Initialize object for compression or expansion # # uri - A String containing URI that may or may not contain the scheme # env - The current "environment" that assets are being loaded into. def initialize(uri, env) @root = env.root @env = env uri = uri.to_s if uri.include?("://".freeze) @scheme, _, @path = uri.partition("://".freeze) @scheme << "://".freeze else @scheme = "".freeze @path = uri end end # Internal: Converts full uri to a "compressed" uri # # If a uri is inside of an environment's root it will # be shortened to be a relative path. # # If a uri is outside of the environment's root the original # uri will be returned. # # Returns String def compress scheme + compressed_path end # Internal: Tells us if we are using an absolute path # # Nix* systems start with a `/` like /Users/schneems. # Windows systems start with a drive letter than colon and slash # like C:/Schneems. def absolute_path? PathUtils.absolute_path?(path) end # Internal: Convert a "compressed" uri to an absolute path # # If a uri is inside of the environment's root it will not # start with a slash for example: # # file://this/is/a/relative/path # # If a uri is outside the root, it will start with a slash: # # file:///This/is/an/absolute/path # # Returns String def expand if absolute_path? # Stored path was absolute, don't add root scheme + path else if scheme.empty? File.join(root, path) else # We always want to return an absolute uri, # make sure the path starts with a slash. scheme + File.join("/".freeze, root, path) end end end # Internal: Returns "compressed" path # # If the input uri is relative to the environment root # it will return a path relative to the environment root. # Otherwise an absolute path will be returned. # # Only path information is returned, and not scheme. # # Returns String def compressed_path # windows if !@root.start_with?("/".freeze) && path.start_with?("/".freeze) consistent_root = "/".freeze + @root else consistent_root = @root end if compressed_path = PathUtils.split_subpath(consistent_root, path) compressed_path else path end end end end sprockets-4.2.1/lib/sprockets/uri_utils.rb000066400000000000000000000123331447572140400206740ustar00rootroot00000000000000# frozen_string_literal: true require 'uri' module Sprockets # Internal: Asset URI related parsing utilities. Mixed into Environment. # # An Asset URI identifies the compiled Asset result. It shares the file: # scheme and requires an absolute path. # # Other query parameters # # type - String output content type. Otherwise assumed from file extension. # This maybe different than the extension if the asset is transformed # from one content type to another. For an example .coffee -> .js. # # id - Unique fingerprint of the entire asset and all its metadata. Assets # will only have the same id if they serialize to an identical value. # # pipeline - String name of pipeline. # module URIUtils extend self # Internal: Parse URI into component parts. # # uri - String uri # # Returns Array of components. def split_uri(uri) URI.split(uri) end # Internal: Join URI component parts into String. # # Returns String. def join_uri(scheme, userinfo, host, port, registry, path, opaque, query, fragment) URI::Generic.new(scheme, userinfo, host, port, registry, path, opaque, query, fragment).to_s end # Internal: Parse file: URI into component parts. # # uri - String uri # # Returns [scheme, host, path, query]. def split_file_uri(uri) scheme, _, host, _, _, path, _, query, _ = URI.split(uri) path = URI::Generic::DEFAULT_PARSER.unescape(path) path.force_encoding(Encoding::UTF_8) # Hack for parsing Windows "/C:/Users/IEUser" paths if File::ALT_SEPARATOR && path[2] == ':' path = path[1..-1] end [scheme, host || '', path, query] end # Internal: Join file: URI component parts into String. # # Returns String. def join_file_uri(scheme, host, path, query) str = +"#{scheme}://" str << host if host path = "/#{path}" unless path.start_with?("/".freeze) str << URI::Generic::DEFAULT_PARSER.escape(path) str << "?#{query}" if query str end # Internal: Check if String is a valid Asset URI. # # str - Possible String asset URI. # # Returns true or false. def valid_asset_uri?(str) # Quick prefix check before attempting a full parse str.start_with?("file://".freeze) && parse_asset_uri(str) ? true : false rescue URI::InvalidURIError false end # Internal: Parse Asset URI. # # Examples # # parse("file:///tmp/js/application.coffee?type=application/javascript") # # => "/tmp/js/application.coffee", {type: "application/javascript"} # # uri - String asset URI # # Returns String path and Hash of symbolized parameters. def parse_asset_uri(uri) scheme, _, path, query = split_file_uri(uri) unless scheme == 'file' raise URI::InvalidURIError, "expected file:// scheme: #{uri}" end return path, parse_uri_query_params(query) end # Internal: Build Asset URI. # # Examples # # build("/tmp/js/application.coffee", type: "application/javascript") # # => "file:///tmp/js/application.coffee?type=application/javascript" # # path - String file path # params - Hash of optional parameters # # Returns String URI. def build_asset_uri(path, params = {}) join_file_uri("file", nil, path, encode_uri_query_params(params)) end # Internal: Parse file-digest dependency URI. # # Examples # # parse("file-digest:/tmp/js/application.js") # # => "/tmp/js/application.js" # # uri - String file-digest URI # # Returns String path. def parse_file_digest_uri(uri) scheme, _, path, _ = split_file_uri(uri) unless scheme == 'file-digest'.freeze raise URI::InvalidURIError, "expected file-digest scheme: #{uri}" end path end # Internal: Build file-digest dependency URI. # # Examples # # build("/tmp/js/application.js") # # => "file-digest:/tmp/js/application.js" # # path - String file path # # Returns String URI. def build_file_digest_uri(path) join_file_uri('file-digest'.freeze, nil, path, nil) end # Internal: Serialize hash of params into query string. # # params - Hash of params to serialize # # Returns String query or nil if empty. def encode_uri_query_params(params) query = [] params.each do |key, value| case value when Integer query << "#{key}=#{value}" when String, Symbol query << "#{key}=#{URI::Generic::DEFAULT_PARSER.escape(value.to_s)}" when TrueClass query << "#{key}" when FalseClass, NilClass else raise TypeError, "unexpected type: #{value.class}" end end "#{query.join('&'.freeze)}" if query.any? end # Internal: Parse query string into hash of params # # query - String query string # # Return Hash of params. def parse_uri_query_params(query) query.to_s.split('&'.freeze).reduce({}) do |h, p| k, v = p.split('='.freeze, 2) v = URI::Generic::DEFAULT_PARSER.unescape(v) if v h[k.to_sym] = v || true h end end end end sprockets-4.2.1/lib/sprockets/utils.rb000066400000000000000000000127621447572140400200230ustar00rootroot00000000000000# frozen_string_literal: true require 'set' module Sprockets # Internal: Utils, we didn't know where else to put it! Functions may # eventually be shuffled into more specific drawers. module Utils extend self # Internal: Check if object can safely be .dup'd. # # Similar to ActiveSupport #duplicable? check. # # obj - Any Object # # Returns false if .dup would raise a TypeError, otherwise true. def duplicable?(obj) case obj when NilClass, FalseClass, TrueClass, Symbol, Numeric false else true end end # Internal: Duplicate and store key/value on new frozen hash. # # Separated for recursive calls, always use hash_reassoc(hash, *keys). # # hash - Hash # key - Object key # # Returns Hash. def hash_reassoc1(hash, key) hash = hash.dup if hash.frozen? old_value = hash[key] old_value = old_value.dup if duplicable?(old_value) new_value = yield old_value new_value.freeze if duplicable?(new_value) hash.store(key, new_value) hash.freeze end # Internal: Duplicate and store key/value on new frozen hash. # # Similar to Hash#store for nested frozen hashes. # # hash - Hash # key_a - Object key. Use multiple keys for nested hashes. # key_b - Object key. Use multiple keys for nested hashes. # block - Receives current value at key. # # Examples # # config = {paths: ["/bin", "/sbin"]}.freeze # new_config = hash_reassoc(config, :paths) do |paths| # paths << "/usr/local/bin" # end # # Returns duplicated frozen Hash. def hash_reassoc(hash, key_a, key_b = nil, &block) if key_b hash_reassoc1(hash, key_a) do |value| hash_reassoc(value, key_b, &block) end else hash_reassoc1(hash, key_a, &block) end end WHITESPACE_ORDINALS = {0x0A => "\n", 0x20 => " ", 0x09 => "\t"} private_constant :WHITESPACE_ORDINALS # Internal: Check if string has a trailing semicolon. # # str - String # # Returns true or false. def string_end_with_semicolon?(str) i = str.size - 1 while i >= 0 c = str[i].ord i -= 1 next if WHITESPACE_ORDINALS[c] return c === 0x3B end true end # Internal: Accumulate asset source to buffer and append a trailing # semicolon if necessary. # # buf - String buffer to append to # source - String source to append # # Returns buf String. def concat_javascript_sources(buf, source) return buf if source.bytesize <= 0 buf << source # If the source contains non-ASCII characters, indexing on it becomes O(N). # This will lead to O(N^2) performance in string_end_with_semicolon?, so we should use 32 bit encoding to make sure indexing stays O(1) source = source.encode(Encoding::UTF_32LE) unless source.ascii_only? return buf if string_end_with_semicolon?(source) # If the last character in the string was whitespace, # such as a newline, then we want to put the semicolon # before the whitespace. Otherwise append a semicolon. if whitespace = WHITESPACE_ORDINALS[source[-1].ord] buf[-1] = ";#{whitespace}" else buf << ";" end buf end MODULE_INCLUDE_MUTEX = Mutex.new private_constant :MODULE_INCLUDE_MUTEX # Internal: Inject into target module for the duration of the block. # # mod - Module # # Returns result of block. def module_include(base, mod) MODULE_INCLUDE_MUTEX.synchronize do old_methods = {} mod.instance_methods.each do |sym| old_methods[sym] = base.instance_method(sym) if base.method_defined?(sym) end mod.instance_methods.each do |sym| method = mod.instance_method(sym) if base.method_defined?(sym) base.send(:alias_method, sym, sym) end base.send(:define_method, sym, method) end yield ensure mod.instance_methods.each do |sym| base.send(:undef_method, sym) if base.method_defined?(sym) end old_methods.each do |sym, method| base.send(:define_method, sym, method) end end end # Internal: Post-order Depth-First search algorithm. # # Used for resolving asset dependencies. # # initial - Initial Array of nodes to traverse. # block - # node - Current node to get children of # # Returns a Set of nodes. def dfs(initial) nodes, seen = Set.new, Set.new stack = Array(initial).reverse while node = stack.pop if seen.include?(node) nodes.add(node) else seen.add(node) stack.push(node) stack.concat(Array(yield node).reverse) end end nodes end # Internal: Post-order Depth-First search algorithm that gathers all paths # along the way. # # TODO: Rename function. # # path - Initial Array node path # block - # node - Current node to get children of # # Returns an Array of node Arrays. def dfs_paths(path) paths = [] stack = [path] seen = Set.new while path = stack.pop seen.add(path.last) paths << path children = yield path.last children.reverse_each do |node| stack.push(path + [node]) unless seen.include?(node) end end paths end end end sprockets-4.2.1/lib/sprockets/utils/000077500000000000000000000000001447572140400174665ustar00rootroot00000000000000sprockets-4.2.1/lib/sprockets/utils/gzip.rb000066400000000000000000000063441447572140400207730ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets module Utils class Gzip # Private: Generates a gzipped file based off of reference asset. # # ZlibArchiver.call(file, source, mtime) # # Compresses a given `source` using stdlib Zlib algorithm # writes contents to the `file` passed in. Sets `mtime` of # written file to passed in `mtime` module ZlibArchiver def self.call(file, source, mtime) gz = Zlib::GzipWriter.new(file, Zlib::BEST_COMPRESSION) gz.mtime = mtime gz.write(source) gz.close File.utime(mtime, mtime, file.path) end end # Private: Generates a gzipped file based off of reference asset. # # ZopfliArchiver.call(file, source, mtime) # # Compresses a given `source` using the zopfli gem # writes contents to the `file` passed in. Sets `mtime` of # written file to passed in `mtime` module ZopfliArchiver def self.call(file, source, mtime) compressed_source = Autoload::Zopfli.deflate(source, format: :gzip, mtime: mtime) file.write(compressed_source) file.close nil end end attr_reader :content_type, :source, :charset, :archiver # Private: Generates a gzipped file based off of reference file. def initialize(asset, archiver: ZlibArchiver) @content_type = asset.content_type @source = asset.source @charset = asset.charset @archiver = archiver end # What non-text mime types should we compress? This list comes from: # https://www.fastly.com/blog/new-gzip-settings-and-deciding-what-compress COMPRESSABLE_MIME_TYPES = { "application/vnd.ms-fontobject" => true, "application/x-font-opentype" => true, "application/x-font-ttf" => true, "image/x-icon" => true, "image/svg+xml" => true } # Private: Returns whether or not an asset can be compressed. # # We want to compress any file that is text based. # You do not want to compress binary # files as they may already be compressed and running them # through a compression algorithm would make them larger. # # Return Boolean. def can_compress? # The "charset" of a mime type is present if the value is # encoded text. We can check this value to see if the asset # can be compressed. # # We also check against our list of non-text compressible mime types @charset || COMPRESSABLE_MIME_TYPES.include?(@content_type) end # Private: Opposite of `can_compress?`. # # Returns Boolean. def cannot_compress? !can_compress? end # Private: Generates a gzipped file based off of reference asset. # # Compresses the target asset's contents and puts it into a file with # the same name plus a `.gz` extension in the same folder as the original. # Does not modify the target asset. # # Returns nothing. def compress(file, target) mtime = Sprockets::PathUtils.stat(target).mtime archiver.call(file, source, mtime) nil end end end end sprockets-4.2.1/lib/sprockets/version.rb000066400000000000000000000001071447572140400203360ustar00rootroot00000000000000# frozen_string_literal: true module Sprockets VERSION = "4.2.1" end sprockets-4.2.1/lib/sprockets/yui_compressor.rb000066400000000000000000000025121447572140400217350ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets/autoload' require 'sprockets/digest_utils' module Sprockets # Public: YUI compressor. # # To accept the default options # # environment.register_bundle_processor 'application/javascript', # Sprockets::YUICompressor # # Or to pass options to the YUI::JavaScriptCompressor class. # # environment.register_bundle_processor 'application/javascript', # Sprockets::YUICompressor.new(munge: true) # class YUICompressor VERSION = '1' # Public: Return singleton instance with default options. # # Returns YUICompressor object. def self.instance @instance ||= new end def self.call(input) instance.call(input) end def self.cache_key instance.cache_key end attr_reader :cache_key def initialize(options = {}) @options = options @cache_key = "#{self.class.name}:#{Autoload::YUI::Compressor::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze end def call(input) data = input[:data] case input[:content_type] when 'application/javascript' Autoload::YUI::JavaScriptCompressor.new(@options).compress(data) when 'text/css' Autoload::YUI::CssCompressor.new(@options).compress(data) else data end end end end sprockets-4.2.1/sprockets.gemspec000066400000000000000000000036411447572140400171310ustar00rootroot00000000000000$:.unshift File.expand_path("../lib", __FILE__) require "sprockets/version" Gem::Specification.new do |s| s.name = "sprockets" s.version = Sprockets::VERSION s.summary = "Rack-based asset packaging system" s.description = "Sprockets is a Rack-based asset packaging system that concatenates and serves JavaScript, CoffeeScript, CSS, Sass, and SCSS." s.license = "MIT" s.files = Dir["README.md", "CHANGELOG.md", "MIT-LICENSE", "lib/**/*.rb"] s.executables = ["sprockets"] s.add_dependency "rack", ">= 2.2.4", "< 4" s.add_dependency "concurrent-ruby", "~> 1.0" s.add_development_dependency "m", ">= 0" s.add_development_dependency "babel-transpiler", "~> 0.6" s.add_development_dependency "closure-compiler", "~> 1.1" s.add_development_dependency "coffee-script-source", "~> 1.6" s.add_development_dependency "coffee-script", "~> 2.2" s.add_development_dependency "eco", "~> 1.0" s.add_development_dependency "ejs", "~> 1.0" s.add_development_dependency "execjs", "~> 2.0" unless RUBY_PLATFORM.include?('java') s.add_development_dependency "jsminc", "~> 1.1" end s.add_development_dependency "timecop", "~> 0.9.1" s.add_development_dependency "minitest", "~> 5.0" s.add_development_dependency "nokogiri", "~> 1.3" s.add_development_dependency "rack-test", "~> 2.0.0" s.add_development_dependency "rake", "~> 12.0" s.add_development_dependency "sass", "~> 3.4" s.add_development_dependency "sassc", "~> 2.0" s.add_development_dependency "uglifier", ">= 2.3" s.add_development_dependency "yui-compressor", "~> 0.12" unless RUBY_PLATFORM.include?('java') s.add_development_dependency "zopfli", "~> 0.0.4" end s.add_development_dependency "rubocop-performance", "~> 1.3" s.required_ruby_version = '>= 2.5.0' s.authors = ["Sam Stephenson", "Joshua Peek"] s.email = ["sstephenson@gmail.com", "josh@joshpeek.com"] s.homepage = "https://github.com/rails/sprockets" end sprockets-4.2.1/test/000077500000000000000000000000001447572140400145225ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/000077500000000000000000000000001447572140400163735ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/000077500000000000000000000000001447572140400175125ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/POW.png000066400000000000000000001236451447572140400207000ustar00rootroot00000000000000PNG  IHDR,K9tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp pmPLTEjjjlll ޼pppzzy}}}uuuܹddcى􁁁+++[[[SSSKKKBBB333<<<$$$rrr뤤렠nnnԘ ǀ||{xxwppnnnl___rrptttlljggfOOOWWWHHG777///(((g-IIDATx콋[X>@IQ0Y5QT6rDDZѢ>ޟyj=_>vi:3׬^{kmuM/#_/Rͯj~巖_oef~=jK7#(sK7՜EEEMj" K'DUEZTt$A<'j4:pɊ(x~G~ 3y;m :~W\RA7t{ ÐYT0*zV aTiNFw wHoF%ڭ.u^P]P3ڳa TA7Tc5#"x&ͿپKLST>² T=T,ҽ*i hF&>ͯl$+2'_k¦Pdx&)e ,5Z-20$uQ8,!L|tg+~U#0_A{IDނdl]uvFk2߽?(jʜzT_nײمE'uK5烘L&߷eK?FBO"fW0[[Cd .X4 bɐKG-!c3'dN>׫bMS,1י7^:)aYSPS}TC^Qc*tuZyrA e{xEm2„#F.0Sa穊ȵ]5! hॲxej2,i"V{xj&Y^ga@pUm , b,f٣op'p7:_k(xnk|X"0e>Jj9+30ґ׾^f.=s[YsIT{><1Xͨt6ܻ}:'.&Go:fSci80b/( |`Օᘷ!V$U^i>3vڕw&߶gbkS9#OU | jՠSuz66hß6_w) W媩HZDX0$IUv2 GdC'=6F9Fe zxpVbLJ Q8um>8pvNk؂-skͪ돈,"oƘ Jeq|d߲ {F9Z{T=Ea] ' UiNM.O_xY*6C(ӄj"~b>UᙵTUc0=! 2\or>geP*(ź (kk)7)(`6:cWodqb7'ۦEmܝP/d/V͖@qoC;W 2T,Trļ ZK=A ?b|=Lfnb č|x!ف|ܰ ߣ!)·l^'Cn_7 Ѓ ? s?[C@^V:®nX\f؆;>(}U $o|-Fz#FpGی4›։BS[og!؆86?|H9w z5&MxVU`G8$]sUЄЯv[ HG{1;WdR`j"WR8,坦@oz8s x@RT,ZX)˲^BQESTb86|'hM?OmcWOsUѝkwlogq` %8!DQ*# C @0!R V T0 ژ @w}3];\\arsUӬB;{pNp`G]o)1w my[~{=ҹ`Bܶ=C|sw:蝋udٺڝRtYˮ݅iZnơ-by̐Vhz0vfp}uU="WY߀ޒij2uגiWX'C zk孾>oy;O/3@;.ț o7t$ CZRS'\@|3+y瀧gTO>vZ~Z}Li/23 "E4›G o: ?0K`oHc*k鱚r,=Bi*6R+9 D\*$eD1W]m঻XbY t|OyG/Vv u|%3<e![7/Lu sA8bne6Ab0ͲaD4gYjy xsȲ3U?2۟p@B/a"ץj v̈́Pg># :g7г<9&B[LO ?t>;zwEH& ӑ-3]# |D `HT$]Hj 'fQ8m [mli9}+t݉kJ' Y[>gl"q o/fX' (vmض6;ml~ۮS*sk<#X$MBL xEXm'wQTښ1{.#shl9:B7*cӴ{v-g̟Lx-m@hۀ́0aŸ1έ-tfvvFIF8i6Ge4^X3:kb51?Y5do5HU3<3 V3ƖE1`t<'@ Ba|0S&$Vba0=.]>/\tu)V\OKׅF%ݛ|pmaR4V]Iš!Mŋ T=MbqLlwŰpfQgcv&ۼ!&0%¥e4${ՠE W.i[Dhg!1G;Jw ^%El ,oj(&*Ĺ!imwv47 f›%:z6%`;6yJۯޯ>`uW%tPhLFph<^|FIpDROUTtI\/Q h٤TOhb=nZ6aؑ f`*Ithq:Fs~$D[~{e$J&OwW*og:)6pחs}(_XGܗD0qosڶP9%w_ vsN8gݞ[ߒ϶F]3K}3s 1ITVWDPq syݞz ۠pن@zpǁl$p;뺳U[;|Slr%ݼuIp`MF8 @xFh\O`Tmr_=t/[ {D`{FzJ}cʯ%gV_[a:Ge߀LoVWQ`0Iϱ~/T6zI@ 89acGwwΤgEz=p̾A}P?2Xz6k'Fvj|8*sO}%xKa mD>*2l8nx|h[zȷ{x픧fY~m^ϱ{;A(=p x6Z@ۡB-<*h=SU HST9{l\*PEƯo1n/cE0Ÿ"r~]Ax;vmB[+x!xt7k{Å¥m`!W(E(vglmwbvuwk i8?o?`jA'1"v77]_?{7CH |v ZUfܹ϶yOϗ! *qhg4mWw[/8Ȅʇa }ݴai/H,xnXf_ٸ %Ɩ-loApwvmK=[?g "" Lr[REҨ2u=^FVxde]4B9hG!77}G ]uv*kM|(Y^Eވ*MFnr06i#b@o,g=sø{/ 7~ܠAck6ModHAD$U:4MߊĞU, 0\P9u ťer0x̲, ozXvZ#n?._6aX>ZN/Ev?HWi?$;75uxvvX6Cc=pv{)ve=se}=hTE7RUHD:6Cu_CJ!w5zRgx&=cA=!7}~|uٳ~R!UE$֢6&]f `ai:֜_I߇mvέpMD3[Eo)Daw`400[N7B]U8a ^0,D57j l~[Yݮr|9!Bs&3cL@n6VAehnBRa4xk<ڙ@HϻԄ i#?Qiz q{<Ʊ~a^F aBE'{U&ZjֹBƘ"8xj of@=nu'̄tIUb+oSOQqYlb:J8}!h)"HkDCJv0Z%a/U @5ׄ7yu~ o^vK$qQ􄢉 TCMXVz  M(dR7Gq)kTf-fvJcR= ]mv?b)agb8c@'ԊM9Eoe:-g˾A7O|ecvq`*]3OnSWܰ\G! >mGbw(=lF흧۴-z꽟{Q(e%,Ih-*Q XO#93䂌"t56 _x@`!|vx|w'omm葥jIxqZdQsxd4M x=;WڼL܆D/ƳмwIfUx ekHxyfT6AO~'b!ȫۄ2,@+2zь͞A/M(F&|W?hosaQaMx3f H}NMgs@x3eDxs1]z 9<({,}Y {WE-0Ҹ;m#a R; ͆Fгio66m4/-[ 0MaO $aj:0Ȱ GS\pT5j -J [\'+1&6(P?{]qZ .tUE͌zL*ãSa[_}}FrVQ@ v (l&2H6&4 Ꮻ<'U y6Z 1m숻SĒIMKd):j+$!K0hƅyޜ B(VnNQ8Q5NSJ7ϞĪ0^"jQ T&J*@Hsf$NLȏ7Kd #'.jRĒ7L,eMx3H73Ɛ>ah 3(wT^ &h:$ Ah" g 8(j и o~@*g KuRb/?M8 b,2x( `U3!ܟ9|7K@wY3)9 gMTj x YuGPc#RZMVPh(&4ȿ,h//7D>߼@on7m`n1lm]gtN;ڌĶ{s4:r>M`ݦn8BBX;]kz 9YՑsƉ4i@fD/qB#D ?A5q~2J?hC֗Oӊpqt-a; ,4ÒA r 4f|hװN:@͜CxOo3]%FO O5FǛ+s3W"5f/B=l4sUtИ;A/0HO AhB[p4AEgeD;sP97Tn#(+oTx JS'qHvGص(?6iP]KQg8cJ+1jb[5N"eCpҕu*x,UT?JMvov}!zP}*v(|Ç+34`8##=bU0Ûb^5/&T2XV 8LQ QA8µ}x60Xm|?en% Wr5$Ti\?(9~P5tRp(s|}#)pBh8Ni;onhV._.`jpƘzӤ8lll.TΓǏyoL%dj, EB`iEx8}7(ik|ѵh{ݶ#~&㏲naOS}={y6xK"xEZTi>[&3&X*G wtf9 _$fF1h07!;u u2A|WSjo n›{%JIX/oKƽ-] ؍;[ێBg4Bn `;:[VVϷ熆:SSD-9lxi6Bf)ā=#@،1DY#Gs尭=ok裂$pXRWNM ѳ]{sp>%aN%P!FہM Y ۴9s~ar RR$P'+Ffmޠ;5Y t k;Zp₩!2(,@PT]W^2gVA@V-p"D'R۔ȑzi"T3/D^K"StWE"|A r㷓|nR VQ$o9;23C2;TRfd)!qn J&&Ʒx Paq.K3)/[o`[xYSq\U QV#4qԿ$Cs,~&|J£OKQS/.``)jf¹; xXYֻFQ!E1Yj#&eadȔȧɬxv潭-˂9߯z'p!J*9 v\ʶ.t9#6EJq{uGclИL4|.ʽu(L%;"Ř ,p:رjr2]KTqhs"3WpzLL? !pQhogXԩ#_#зP.OωT֪|O#ޜJ}zcOyj8׍}T{uA%xXj1t za|yIAl8'AY+Dt֞_|NRzbU%Y/Sk5»~MbkTx7ڞijqD,KT'amk%9b8Mn}Ѱ146gr@9YfagނR_Aߢ48amL$|}Xu8m,<.negfAp(rKدՉ'2s3@ުˁ̂HvԁU v4[XEGdIR9}P{ ö}k)u۳agล=q'i-Mz.ސD5#!iಆ/2(3uB՜M .OosOw!:os|xo#B =q~Xc/ DHN??Gk"f %#kWx#+)=kyWRmF3#X'mܺl76Aw]S*fl@Q 9.1ubHj%8dNgsniw+?i$~{KPEzM/tpN׌9CcNOw/'{gݲSIH\jyDg!z=dLco}˅8X*׈h8>CzLZLQ1ɝhLoooI o4XWVsJb\ 6\o)|}j%<?y1ȸ_37/P=IsߝBlԂqp櫟GTlvg]ࠫ~0_Ϳԫv#DDF /+NbŊ 'M[ŕ L5H~_Eu}9j$bv=e>琰i_cm@xs/#0ß7~3+Ҩds $qu=L\EՈ Z/ʅAwrϦ;Nm›#|}v.$T'-42oˏa\iLT=+2- Ͽ,,9S_}j`udRu}asw8ѴG2j96ڴhpCLvMfCU2KlNdj£R BX37Y?b›%_Є7@p}7]w7e<MU+ a4֪'~D&)C0k2!&n+sVZeտW^%skgoANM7MQN]cl>kqx4dj$gNQ1(ϾxIs`Mn9 xHC^cg-`›k;(OapcQ@f9h[B1]K.j_s kT=':Y]yhzXv2nFtcDy{d9%ivDǜt]tG^ޅF hxTExzY8?S  i>׏N46 z[T]@vlfBDE -jRЂ/@[WI,BKž8wh|\LwcSTu"u14bO47e23pcuA$5D2+otQ"X>T{Z](#y/dBw֊]N ]M`,u 6&___kaVLTLdq[u(X)εBNWߋR1ڗUd}B_Ӕ~N쾨+U!UUTؤͦJVFm8w` LΓ5+b9BtF곰ə'dq##UHldEL .KjV4V\Pȸ鮷@nQm5){8ji\\SULNg\~CI ;sNfO6|G̢-k#[Z|ypfT&SiG.2>9asI@I:ܐLfiEΕC0&fjQeE&wͺ ̩YbYwG NN1+{z q~ LYژj_|i vU3KXHt'%TL;;26#2",WXA@bN 9AXg*m3Tc\ O@yGvT˲XbaR#Z5xy kG̋_fʇ,]`/9o`ݓb'1kOca^>$n,ʻ䡈c^Y-ObV+J&z)y;`dOQ7y_CXVjYF*554QѸ-Vo^|5, rݤ5' nH.}C B !14Q k ;s̞Evp=\p@FyuZCR"kM{eKd7]m)&^dDF)*r˨/7F@dZ"S(hƠ ^c UwBzf=e: RcluZPrF,0"!?f[cK"C 15RGg 59xJ03` I_;"C a%v |Pg"Ef.vVly?ԓ`ޗ9J&W[HYDUƁL5{`gtYXxITcs6)ޤR3O=a4T6"fᡃVWOz#<*>0W:C"DQ8AMT׺Yod~;VfU.x}Yąz(.W4K?O59'4 :]88.9! 5ikx,RcK!!?bѓm، $_>\4`I]zk,>ۦpfV/+SR}WlB@g堏=ZtnjEY0Lw5nFkp Lg`!/"ZM5ē w }KaJ7Gotk2-޴s^|}l!j%^GԕG>X ,Y*24W_cOس).ezV ܼZ}S) q\-kl1|TUƞ]x\XUR0-rX S 岌'v;/ Ւ9Xa/ 6y(ijbu] T3٭C{Heq2)j'9i2 jH>xU -su&l cA+fdDhAI0 ly1iac`I=mgQh'ʭw"%.H:^.3n@P;zWͣɛH70w],۝^E<|sזᶨHsY1ų'hŗ?ϒU NPB->Ym A /7<5s:x5-[u Nxfg2l `{ęsނ 2S ,"' ,2w[u}=|o䬅f>"/Ջ3 [0r][OsUC '[ "+Dkŀ QL3|д¿Q"ZJaոY iÄjU[qX5vGFh82>dϋ+A}}ԻYrLG*|]8Gٕ +%O z!Hu_9t[d= T,hn?Iab_nɝ8>ZDNQ[1դ&u r'psW}%~}"z35:W1UCBE%y<3$mӤeXZ!džjBDMޒ ֑::xpSV,A('@N>iYLwdQT M>~]+aןV ?MIЄvwxk3ɲ H6hh C'vpbA<}U;B&^ہ~JJ:QMIG܊ ۞M?S3 S 9㑏et 9uI3hҟUM71 `y {mC70EMU)yȪyBOj߈$>0vsykHo<>7w`4m22UN4u߮Zs[:$< 7[Z膰W-82 !W,^I98WdCl>=º{v=o|C[.ྷ8}I9̈k4XdTSV[xT@\0jEaI,]`hq#w0E+}t AzpO^[:[FcDe:oQT#c! ӱX,QzPZxfmquv*Qoh$4"}}~Ot 9a:^$: X~ؾtLIVǔi: e,Z?_MtPDdg9FjtvZc?Jf qRoV'}l[gS2ͮө 'cidF$Tb^LYOgP_FHF5_;;~홗o|XS)x ߜ֍8>G~h g& ~)pRE蚠0;S 2D*YdD.y3_~Y|]?,\lg.fËo̖8Y 6PMCz*Z՛W/za Mdh̾k] 0hR.L۲Ju4<%Sy|R&kn=8^X ( jk;pk'rEV&ao87岄D l~InةgG!A e^:"W$5"N]w\3Ul\ĩZ`4uGl:_Q5 UPT6-2}>_tjA*Շ&'p{ RY,(|äCx!tfG-k5bO>bi~.(g$fFcki Ɂ$ Д̀2.XxzC͇|X!;4WBΐ1V.W/)b ]7hAkkxs'/2wlmxHyEfZi:W6=r:\b]}-p/K|PGt/x02 -k[TXlDM3fSA"@|``p(*zPIJm]})Y+vi{Szo9ѸFT9 䎅bec{Ƭ~M38]"*)ԵD0LrK( -G9_0͐_s&Q5fVc*\۾٠U*Uf䧻Yjl*fxjcWI5vr ,,z)-dz,'+Ps(mYd$6=Ȳ;,p/6$ZmԧT:/r wF7TMjDj/[L cuK+K2yx`@: ! <EL, yƲ8,zٛmAm++hOyX7 KEE]i̹*,ΰh;r<BÖAHwHqaUhK}_kvom ؠ+5,J"'_wm0&{猿93ڮ~ߪzުd@ I@}'ԇ %f,hiP'q&k䝋}41s6 /LyscV u73y4Dd<]ͪ^ut45_$ob^ lzQ@K/Dwj5||G˜Kh,Fa:%gJ-@Zn"ʻ4-]!x3гPrޢ߇Z)rHo{07w9[2oXWoYt?dBk^kl&Jy~ .~>+8e]q Q6>ۭwbYLfؗ\Q4ɕH61Mk7u9phsޡMUC5M#EB %Z+ĩ 3*l\pq8.&)TࡅC K#%"(lAQHrda6MisΆ- s_rㅸRJ,',_E34 \X'[4M}&o[^Z+ei& ×I? $WmͧtH9}1Qh72qK%=*9c[.P,O|\9 b!W MCI|TXګĿnd,XcF %<Θi⣏t@L 84+nhY*FS vw ؅Yo  ̳1SvXXӐ|n'lJ-~&nӳ)Xs z sU!'> 6ՂE#NW#v 6]q7M @SFIȕeگWCˍ&d4F3wi&wXn| /=§J4M}Jh|TS_=y` \EWNPm:BvRu=#|V2zY K2g~u_")adc=OIC6H,gq>bW-2Y=OyYa`AXB.`y r3`{X5W_͎f%8c'`!u+] e 囕T;>@Z]tznIx'IiQWEbPN;QzO0$1]_52:ſe&Az)kfCo6y>)7Jh˨xoӛE*~ؐZ/ei19Nq%Lwfx|9fx1h((ZEn9uBВgw?s()?cuiꒈ.KKv3HmRS4ЭjeIRgdH5KA-lrwYCz3~DkҒIK=s~LRһ )d7<Vtv20dztK~qB$*pNWEJ fL Φ& -/S*9} X):>TPQ*6F䎺nI^lH61iY!@_x%fC k\uoU|xh4 uߗIjql ?6MH:rx3LJΉNJP Xyo: gD=v,RP`N_m`P[jjUk3|yi[ﳩաWXMCA~Q;i_2np 6I ? <$〶= ~JXkGV tn (?mςh\3i40,z3fЛo|Ebry wN1r41KXrY^s& :NQϲ%GY>("v,km MC+%й՚fͰІct+C"y=+j: qǁpޤl_ЛICEpے\p`ޡ7 I&L"P*퀛fd4o#rJ؜DXz_*I4LƻyBiQ@Nw ϰB)vNX嗹6gi;%Fo\X,FFo+6UkS|^$[HS+]>GZ֡#Qn<.}U#1 յ,XwBkY:R*zZVEF ykfJ\qǺ42k?~4ZEׂ6k%T! 6oM4frTzqqמ*kiB@0FNZZ8r$CvSoR?nIj3ed:xdDV 9o%*T]^F͡(=~49k)[=#~Çs>fNGJi7l/j9Sx?p_!5,_q@&㱉FWub<&:-jY!Yhms;$wk!FKs7GirpónirXҮRE< fb[72W>Uo>hDzKEٰzsF]%;RRow!ab.{]E4Yq@uKjbIA\bciu"iN5H<<;b~`vDK'pE.`I&81lyQm!޽ DoSiZB̑"lÕﻦFR^t:d=.ǗD8Pt[865ih7P^d!PQcV3ڗ^4;WA媊&IKi_|m)>(?+?Ж64D\@BY"ы]SYF\QWkKH󔼯 L3ÍM=pF@vhQ_]!B#m oY&'?'_m[ov8~׶wG=q-ҟ,}4V$f^lim8a$7MjeIӰNI$%iI+<&3S8%xJi Z[rR\8`WB?C|o5~DhUZMf$AvLM{&߈48YWgX3-[j"H$u-c h{83@l)%daM9sxlƊ#De(|V`#G̸[ $r Tl8lxܘw~ ĠlW`Ko#m ygQ@!Hq"x&%Qcy/(?cٍ^9x8>7gՇ,b=ˆjn\3ATJ>|f#wH놜PԢn4oB QD?Mz=2o.>TKw{UM$XiFOԆpG '`+ɤDpIhXx_D }#kD%r(HhiɖhS1%Ngj,4ײd4uJ>Л76FL[ <83Wӂ& ^ =J. q&WVrP/dՐbauCaPXQ]_(_!Z r" As #~-c:B ^ӳ՞$!1{(fMA@=t Z⹩Q|3#7z5h 9n[٫c+sju,[WmOcEܪ .TYˤhkp"ug$uX#çٔX.iKwVG2RsРgHE7׎kP]UơZF"[67HEʓiRGDiR 5Id4ul)+B#{͉ةa|2 pN!yNo0KX`]IQOb*iAU C~5G&߽\8Ms6ۭ&Z`[;4g5{?$cN7zhb~bncb7u 񑦵LyIMQ԰wBDZzE b.y]* 0,#bEJĮG WJ$j;{ML~G$啵g:J$i ĹstXPh:kEśv8xewl{l;Ht^JȮ1ҫPO&n lx(R@9رa>9 uX=ƭUJ:!Y&GMJ({ zvYgG.i"+_mev@vcymmYVILowkӗdȫ% 7_ / ט& dӬ҃꾀n7c^Gz33~xhMD=AYg#$+oU6Ud4uzu[4TZڬ|PWYY$?ջ$FŸ2XT )27/Vh aaяe!F0—GxPE)ɝJ ;!dF57}5WҏDEfGpRi Üb€7\2\ZZe;]'-8\e2Iz9NfGz/L{2Et!NߘI:oKL3eD$.[|smn V:)v~E< Q֞=6H\|״x]4:8^`ch,G3%\4 II#oc\֘OOtjj?~OwZϯ5۱AiC#,%l5/d&&CWkRI3 '}m{oѳ7S4<_s WvBʴ?yڿXc6D^3zshaW-!`~hMwΕ-쇷hvtG%?ʻ1o'&XPo>74juCVU__8=kMU_􏲊R5ͻ}+'8i F <ݳk""BQ\e`*lh=h` ;[s`s&,iJ跸t璷e'GY,18֘ >8z$`ҹ/j s3\FC'9 8A#$3y_>`m&Ϫֶb&foeި0gy 4r&]57UZG; .{مwUXY *n5zy(H" ]d+]_Ci=]$9rCQu$bsKEuNf޹~O?ec9ЛtO&m<`t_-{/y OETOIA zA혧|f#D(]hc&zp/}^b{ s\C_sAMfŗdJgioZ+Cj 3ZAr*8]-A$_}ez)_/Bt"Evlħ=Ch';󷅼hW쯛q)?˫y}L; X7zuE]oZ*a>Rl$Y>KŖ^pvc}h.G1Qs1K<Kn` |ٸD<"6'P50So+Y~bci95_#v`sX`.Ȫk&|mx4X']&uTsx1"]o's C_yӒL\QߎQq_7 F͒ AHn)Vwe <r3vʏ!@y6tXXQ1N`4fC1enm`'[T OC Phš(ٗW##WA {> 3(˲EzӪ}WIM?Hnb' !p9yݍ⾧zQWپPHS& |$EۗVB87}Z;/QMs@c3҈髋nk0UlVV6#۞ƚz;^%ie>揅GMyo5 hc̺k: /2796 ZSkLcia+g>^8=;m3!O)i=5e.8 udn}y.'e7ڠLF] p+h 5Lcȝb өRI!:Ib[zyUJrﯚ&C4{y-M1qMlF3hDz8ٖs p l~pf)ϑgN;9-jc&9L08ۃ ^h-ka?}YC>@QNuCӱE액1r@EuZ85ڬz.`VmVv1񘋺h0z3`suZ!\y$+:g=һ[4ޙu^Az:tCݾr+1YzпWLDvo RjHr UAa[ æ\gbm*d&4`:n'Vi<.2BD3h1Z6>I5lhYЛs(=rܫe33A悅k۲–z*\V7Ul 5'0C]2qv nd_q-C Ov4F$ʼn_0u'n&A9 nvj .}$ {ЛMB1y &E!NuMMVTʙF ;x~+^ 80`CoNi^47q.0\9 Iz@\\eaWߵt 83:g6(;%6!-yBkpXH*Gސߠ"F2o>Gm'zݓRy4 yd(綰ZUd4YE,]$3;$>k0:Y*cWA@q_`1wOxG˜O#Pa=1 \m#p͛-̝ᛁr٨qojp~ӖS~M]/f7'|"Xm 6~6$|9crK7UZK-Iaͬc);U!ԡ7MH7 YAz37vDMBoZ`CoFS ꔾ%[ U+J_޹4FfGf IeI׽zϢ=se^`P F~vOrU3;u+}~ߝ&~W;9Rݞz"_ύ/>;j4SJvC?gMvyrܨ鶚>psG0 ڜWݕfml{AVI7#7cGۃA}pͱhfncM> lo6czcs;>pLxn"H|6,Fܪi.3Fyn x;u3[j_#LM伾X̺'& _sF%FB3X3^N}D͘#L8A9VSG.x49}| 5Zw8:\Ӕnx6Jڭi|QC*=`T r]7ymz3ڡ7( Ku"|tT[/ xjHoXhT[!f!nH@ ^rw=j7; " tIPTuiIa>c'Lw[6ARU-ٛyCEnwP,jzJڛmsEVe& ;rj\3gGgA?r7uif WMfsFU_qFZ3:4T]_Qq[3[Ztc9u{ș5:=cfh,k&X@u{uVUmӢc0|w]8MD'"hiK {ҍٰIӦ)< RvhgMk9v́` V30䞰 fmh$, @٦7y qHoބdf4]NIg6f`K k> ̥3 \~)Ѵ^l-` ؑ뼄 \"hA,B6*)- nA&1A U\RExr)%K I𝩨S~4uVƕ ٛ2|Gِi .>g16uf Xt` HH7پ#}>qic.OuD6ty! f=?0PLTͩ, #ۡ3~+g.^EVt"aܺ1+A BvD7#5n-謤i%˫貾 Fc{~y9\>\>"ɁxVd2 >1Cϭix=1|uJ+gGg|xrZZ^\xVi4Wdi?w CHh& `psgǹpLJsF%.0llC!7ِKP ܐ>zqJHpBnP񗋡^u;S +7![e fPE`t^K_C:p/vߐ[ѫ:~EP 3m%TO{nENzS%镝LA"V/k=y.u5 ` I1خ%?9 QԚyV󉚥pmL\6̫M*aTs [1Z\vÃñ9SP`}FflҎBd'UOࣵ/ 1R;AuS+H``/{JU2 psaq:ׄDIVy0Z5͖@2Y~,*amʃD#%<,Žm+*% 3L(_`}낡(f3e8KQFhHPRAk P Q8bu@a\@mQ3ru /=RU8e{ak奤DVX>ij,lak/[ƭbއ%ILZA3tµ-C2a쬐ꄦB> ~rJi S7i +v/ßoT44$f3#;Ja4 2dK5,` ;Lfb1_Z0OJTQ>)9LC~i LB11|< Uؔ(" :D칄Cm[ =Ic˖P4#RC됫%]MdM#nцEt#idmB;<3R@}K{C[5@f"`tw:6u#(~ u,ۿccnl`onh:#;VLm7$29c(*;v?)X${;c0"ѝ*so,"bN 2}H_1'IОWϟ44V$Q[)%i+=,/K$R'O Fd ILqM{B:W6Z .'vyӆJԥ/l*J^-2Ο6Dk]z^]ʉ ߏڽ:SՍԪ~nWtF7Q&sW|Wpwm7SCop}3R9 pM,ax˰wؠj0uju^MUoI'= e SsLUrRnU_$?Y__@ E'MɧK[Vrmv~X&ӏjöT<Ф. xj10`k..1MdTbd5h8!m4pe婰as3R]5r+T]AiiZzfTFʰH Cƫ>BMܰ3W(se;f"Z(E@D׻0N7u{d@-= kCCnTT]ֳ}3rV7eZ*EֆTvy,jqϻ]l 2}b(~.&)BK0izIsP5DR1%?4=]oHȢŦ i-W8^.ԁ9NMwKQS  QgR3OPq,׽k] j1[97L-fy&^a|TQF8NAk<dT=5c= pf ejY,bxʐ=`z)3$(qOӶVuzfO '/_2QMglXQ{)69eil{xm3JfNEk)'6 >rv6O |SN犨(I5%k sA&/OhYoWB:=T-nZꭾRd7y ҏL\ki`» Х6bLYtgpqҏ{l3A٪ 3j?`#:lG7/=|x]`.>\|_ێ^Z~YulO1-f W9燫̬ǜ9̼2`m8L|T+mO\I&pצ\%ӨC?mϮFx9Au蔢CR`W*yL׼8!+m`iMELݪo(mUe Wz^*m?ffw6j(7LPύ }nTc,ъh* 4JH/g$~4? kL$j5=:F`֥W7Cf HĕAkJ_8y;'m65Ngżq94[)7}l`=H[1;QHbHjj%jN,jav^5fsnhkKS%ZBH-o^Q?[Wz˿i664Jr֩.famz#EQQd4 ޜ'`jO2"֖nFoRSh+\_י!f4>7]p P&/+I-WB۾C߬/iX٣4F+XcMoDFz&CFqф[f4gh0RT-r:Fhv7!͟ ١7hh|(?EyjЛ:'9XFoNFЛ|r4ÈfI^]' Fxp_7.^{:U1"ڏl*CG27Fvw-< {9/"WrE}ĨccS޷ћŜwŘO -ŰCq'b E 7BEWjq xT%epoi6-КJA9rE]nfG6pA]%fS6M$\gԅeOܶF|:qkR:z>H(hQXUo9v2zX,.u{tlZ75F:δ$69IzPxY.5zSc420.қA'z 6 7gQr<#y 6omzGQp뜧a4w6"ʤdى;g _ o\5g4z&\A ίO߰uTY{k{nh C $y7t+BoR` ^\s /焖V|\ɴi,87W?ӛ FfP@ǜ%$G+eM9ɷMh2%2MH|?UdGraqWf'#z62yvŝcHż>uIb٬INU=^z+~O,7{k]x8N$( K53kUXuWG7\ۙ8kqƼGgJOC1cTA2q" @*htu5tGMoFMxf&7lI4MNWQz=Y֕hh;bŠ'zϪ5q 1Qab]b)'қoDsT4 y[TZE B;i%\JoJϨ >O ~Oz5`^Pg*u^u$)E"1,&13f ϲJ FP6 ?qdiTlҤKw2}fϩ\ћ<Ģs߁5q*X4${~Ko`3zSc4hcZ0 ?t8?\g5zseiMg;j7ۀ ,5v>\]47 12̐Ǵ_{ eIPk?kIg)~e{# ;9 &CزO緝i ' cu͘Q-}$Uʥ~i)y+7C.]FF_CrkwX%/=U|yEelmբia>~f.e@Z0}mkl4^ ϳ@M/bf/8dcu{YYIh˫]>B3:eFjD9r83ѽ>>oQFUz9\Z5z*Ju" ^&p t4s1;[ICi]cxڱ_G}.kRQ.D"dX<+h6;1xj2bԚpIsSM#N"HijD{'X+DO9^qglw|>^Yf2b<5F.7 r2A5vq+3?[1F3E|lfۿBi0͞^jʦ8 8x#y'o!qnRk@JS&{%8fkIJ &eiA:6Cn@rƶ~X]{P[~޻.{y6(}FTQ!30yfZg޹=7G7kφXγ5^3?V>o~N4(kX,N1%(rWgyHެ.ZJ$, sfnU?'`5_*튁a'ds]x6zV˶Kgk;u?pJ;Gp4L'{Y4U Mkve;P5%8lG`es&7a؏΅ {e|kŹİ ..| f |~G%&pMl4 j@g/y"H̚Dɕƥ-4Ꮝqp0Z_M(phRh[2(t'ij gcU$dnYnkX_]=2%E4k:{NId9 <% depend_on(File.expand_path('../dependencies/a.js', __FILE__)) %> <% depend_on(File.expand_path('../dependencies/b.js', __FILE__)) %> <% depend_on(File.expand_path('../dependencies/c.js', __FILE__)) %> sprockets-4.2.1/test/fixtures/asset/application.css000066400000000000000000000000101447572140400225160ustar00rootroot00000000000000#app {} sprockets-4.2.1/test/fixtures/asset/application.js000066400000000000000000000001551447572140400223540ustar00rootroot00000000000000// =require "project" // =require "users" document.on('dom:loaded', function() { $('search').focus(); }); sprockets-4.2.1/test/fixtures/asset/bar-utf8.css000066400000000000000000000000321447572140400216470ustar00rootroot00000000000000@charset "UTF-8"; .bar {} sprockets-4.2.1/test/fixtures/asset/blue.png000066400000000000000000000001251447572140400211450ustar00rootroot00000000000000PNG  IHDR(4PLTE;k| IDAT[c`b@OhIENDB`sprockets-4.2.1/test/fixtures/asset/charset.css000066400000000000000000000000521447572140400216520ustar00rootroot00000000000000//= require foo-utf8 //= require bar-utf8 sprockets-4.2.1/test/fixtures/asset/circle/000077500000000000000000000000001447572140400207535ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/circle/a.js000066400000000000000000000000341447572140400215260ustar00rootroot00000000000000//= require circle/b var A; sprockets-4.2.1/test/fixtures/asset/circle/b.js000066400000000000000000000000341447572140400215270ustar00rootroot00000000000000//= require circle/c var B; sprockets-4.2.1/test/fixtures/asset/circle/c.js000066400000000000000000000000341447572140400215300ustar00rootroot00000000000000//= require circle/a var C; sprockets-4.2.1/test/fixtures/asset/circle_depend_on_asset/000077500000000000000000000000001447572140400241655ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/circle_depend_on_asset/a.js000066400000000000000000000000671447572140400247460ustar00rootroot00000000000000//= depend_on_asset circle_depend_on_asset/b.js var A; sprockets-4.2.1/test/fixtures/asset/circle_depend_on_asset/b.js000066400000000000000000000000671447572140400247470ustar00rootroot00000000000000//= depend_on_asset circle_depend_on_asset/a.js var B; sprockets-4.2.1/test/fixtures/asset/circle_link/000077500000000000000000000000001447572140400217705ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/circle_link/a.js000066400000000000000000000000411447572140400225410ustar00rootroot00000000000000//= link circle_link/b.js var A; sprockets-4.2.1/test/fixtures/asset/circle_link/b.js000066400000000000000000000000411447572140400225420ustar00rootroot00000000000000//= link circle_link/a.js var B; sprockets-4.2.1/test/fixtures/asset/circle_link_directory/000077500000000000000000000000001447572140400240545ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/circle_link_directory/a.js000066400000000000000000000000361447572140400246310ustar00rootroot00000000000000//= link_directory ./b var A; sprockets-4.2.1/test/fixtures/asset/circle_link_directory/b/000077500000000000000000000000001447572140400242755ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/circle_link_directory/b/b.js000066400000000000000000000000361447572140400250530ustar00rootroot00000000000000//= link_directory ../ var A; sprockets-4.2.1/test/fixtures/asset/circle_link_tree/000077500000000000000000000000001447572140400230075ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/circle_link_tree/a.js000066400000000000000000000000311447572140400235570ustar00rootroot00000000000000//= link_tree ./b var A; sprockets-4.2.1/test/fixtures/asset/circle_link_tree/b/000077500000000000000000000000001447572140400232305ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/circle_link_tree/b/b.js000066400000000000000000000000311447572140400240010ustar00rootroot00000000000000//= link_tree ../ var A; sprockets-4.2.1/test/fixtures/asset/coffee_asset.coffee000066400000000000000000000000261447572140400233070ustar00rootroot00000000000000#= require "users.js" sprockets-4.2.1/test/fixtures/asset/default_mime_type.js000066400000000000000000000000271447572140400235430ustar00rootroot00000000000000// =require "noformat" sprockets-4.2.1/test/fixtures/asset/dependencies/000077500000000000000000000000001447572140400221405ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/dependencies/a.js000066400000000000000000000000141447572140400227110ustar00rootroot00000000000000var A = {}; sprockets-4.2.1/test/fixtures/asset/dependencies/b.js000066400000000000000000000000141447572140400227120ustar00rootroot00000000000000var B = {}; sprockets-4.2.1/test/fixtures/asset/dependencies/c.js000066400000000000000000000000141447572140400227130ustar00rootroot00000000000000var C = {}; sprockets-4.2.1/test/fixtures/asset/dependency_paths.js.erb000066400000000000000000000001571447572140400241370ustar00rootroot00000000000000<% depend_on('dependencies/a.js') %> <% depend_on('dependencies/b.js') %> <% depend_on('dependencies/c.js') %> sprockets-4.2.1/test/fixtures/asset/es6_asset.es6000066400000000000000000000000271447572140400220240ustar00rootroot00000000000000// =require "users.js" sprockets-4.2.1/test/fixtures/asset/filename.js.erb000066400000000000000000000000421447572140400223730ustar00rootroot00000000000000var filename = "<%= __FILE__ %>"; sprockets-4.2.1/test/fixtures/asset/foo-utf8.css000066400000000000000000000000321447572140400216660ustar00rootroot00000000000000@charset "UTF-8"; .foo {} sprockets-4.2.1/test/fixtures/asset/index_alias/000077500000000000000000000000001447572140400217725ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/index_alias/directory/000077500000000000000000000000001447572140400237765ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/index_alias/directory/alias/000077500000000000000000000000001447572140400250675ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/index_alias/directory/alias/index.js000066400000000000000000000000121447572140400265250ustar00rootroot00000000000000alert(1); sprockets-4.2.1/test/fixtures/asset/index_alias/directory/file.js000066400000000000000000000000241447572140400252470ustar00rootroot00000000000000//= require ./alias sprockets-4.2.1/test/fixtures/asset/index_alias/require.js000066400000000000000000000001011447572140400237740ustar00rootroot00000000000000//= require ./directory/alias/index //= require ./directory/file sprockets-4.2.1/test/fixtures/asset/index_alias/require_tree.js000066400000000000000000000000351447572140400250210ustar00rootroot00000000000000//= require_tree ./directory sprockets-4.2.1/test/fixtures/asset/jquery.tmpl.min.js000066400000000000000000000000141447572140400231170ustar00rootroot00000000000000var jQuery; sprockets-4.2.1/test/fixtures/asset/link/000077500000000000000000000000001447572140400204475ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/all/000077500000000000000000000000001447572140400212175ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/all/README.md000066400000000000000000000000071447572140400224730ustar00rootroot00000000000000# Tree sprockets-4.2.1/test/fixtures/asset/link/all/b.css000066400000000000000000000000401447572140400221440ustar00rootroot00000000000000/* b.css */ b { display: none } sprockets-4.2.1/test/fixtures/asset/link/all/b.js.erb000066400000000000000000000000201447572140400225350ustar00rootroot00000000000000ok("b.js.erb"); sprockets-4.2.1/test/fixtures/asset/link/all/b/000077500000000000000000000000001447572140400214405ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/all/b/c.js000066400000000000000000000000161447572140400222150ustar00rootroot00000000000000ok("b/c.js"); sprockets-4.2.1/test/fixtures/asset/link/all/b/c/000077500000000000000000000000001447572140400216625ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/all/b/c/d.js000066400000000000000000000000361447572140400224420ustar00rootroot00000000000000//= link ../c ok("b/c/d.js"); sprockets-4.2.1/test/fixtures/asset/link/all/b/c/e.js000066400000000000000000000000201447572140400224340ustar00rootroot00000000000000ok("b/c/e.js"); sprockets-4.2.1/test/fixtures/asset/link/all/d/000077500000000000000000000000001447572140400214425ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/all/d/c.coffee000066400000000000000000000000231447572140400230300ustar00rootroot00000000000000ok "d/c.js.coffee" sprockets-4.2.1/test/fixtures/asset/link/all/d/e.js000066400000000000000000000000161447572140400222210ustar00rootroot00000000000000ok("d/e.js"); sprockets-4.2.1/test/fixtures/asset/link/all_with_require.js000066400000000000000000000002741447572140400243470ustar00rootroot00000000000000//= link ./all/README.md //= link ./all/b.css //= link ./all/b.js.erb //= link ./all/b/c.js //= link ./all/b/c/d.js //= link ./all/b/c/e.js //= link ./all/d/c.coffee //= link ./all/d/e.js sprockets-4.2.1/test/fixtures/asset/link/all_with_require_directory.js000066400000000000000000000000311447572140400264220ustar00rootroot00000000000000//= link_directory ./all sprockets-4.2.1/test/fixtures/asset/link/all_with_require_directory_as_js.js000066400000000000000000000000601447572140400276030ustar00rootroot00000000000000//= link_directory ./all application/javascript sprockets-4.2.1/test/fixtures/asset/link/all_with_require_tree.js000066400000000000000000000000241447572140400253570ustar00rootroot00000000000000//= link_tree ./all sprockets-4.2.1/test/fixtures/asset/link/alpha/000077500000000000000000000000001447572140400215345ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/alpha/a.js000066400000000000000000000000241447572140400223060ustar00rootroot00000000000000//= link ./b var a; sprockets-4.2.1/test/fixtures/asset/link/alpha/b.js000066400000000000000000000000241447572140400223070ustar00rootroot00000000000000//= link ./c var b; sprockets-4.2.1/test/fixtures/asset/link/alpha/c.js000066400000000000000000000000071447572140400223110ustar00rootroot00000000000000var c; sprockets-4.2.1/test/fixtures/asset/link/asset_uri.css.erb000066400000000000000000000001401447572140400237210ustar00rootroot00000000000000<% uri = resolve("POW.png") %> .logo { background: url(<%= link_asset(uri).digest_path %>); } sprockets-4.2.1/test/fixtures/asset/link/directory/000077500000000000000000000000001447572140400224535ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/directory/application.js000066400000000000000000000000361447572140400253130ustar00rootroot00000000000000//= link_directory . var App; sprockets-4.2.1/test/fixtures/asset/link/directory/bar.js000066400000000000000000000000111447572140400235450ustar00rootroot00000000000000var Bar; sprockets-4.2.1/test/fixtures/asset/link/directory/foo.js000066400000000000000000000000111447572140400235640ustar00rootroot00000000000000var Foo; sprockets-4.2.1/test/fixtures/asset/link/require_tree_alpha.js000066400000000000000000000000261447572140400246430ustar00rootroot00000000000000//= link_tree ./alpha sprockets-4.2.1/test/fixtures/asset/link/require_tree_alpha_as_js.js000066400000000000000000000000311447572140400260160ustar00rootroot00000000000000//= link_tree ./alpha js sprockets-4.2.1/test/fixtures/asset/link/tree/000077500000000000000000000000001447572140400214065ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/tree/application.js000066400000000000000000000000311447572140400242410ustar00rootroot00000000000000//= link_tree . var App; sprockets-4.2.1/test/fixtures/asset/link/tree/bar.js000066400000000000000000000000111447572140400225000ustar00rootroot00000000000000var Bar; sprockets-4.2.1/test/fixtures/asset/link/tree/foo.js000066400000000000000000000000111447572140400225170ustar00rootroot00000000000000var Foo; sprockets-4.2.1/test/fixtures/asset/link/with_logical_path/000077500000000000000000000000001447572140400241305ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/with_logical_path/a/000077500000000000000000000000001447572140400243505ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/with_logical_path/a/a.js000066400000000000000000000000041447572140400251200ustar00rootroot00000000000000a() sprockets-4.2.1/test/fixtures/asset/link/with_logical_path/require_tree_with_logical_path.js000066400000000000000000000000201447572140400327120ustar00rootroot00000000000000//= link_tree a sprockets-4.2.1/test/fixtures/asset/link/with_logical_path/require_tree_with_nonexistent_path.js000066400000000000000000000000301447572140400336570ustar00rootroot00000000000000//= link_tree ./missing sprockets-4.2.1/test/fixtures/asset/link/without_argument/000077500000000000000000000000001447572140400240545ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/link/without_argument/a.js000066400000000000000000000000051447572140400246250ustar00rootroot00000000000000a(); sprockets-4.2.1/test/fixtures/asset/link/without_argument/b.js000066400000000000000000000000051447572140400246260ustar00rootroot00000000000000b(); sprockets-4.2.1/test/fixtures/asset/link/without_argument/require_tree_without_argument.js000066400000000000000000000000161447572140400325670ustar00rootroot00000000000000//= link_tree sprockets-4.2.1/test/fixtures/asset/log.txt000066400000000000000000000000061447572140400210300ustar00rootroot00000000000000Hello sprockets-4.2.1/test/fixtures/asset/mismatch.js000066400000000000000000000000321447572140400216500ustar00rootroot00000000000000// =require "project.css" sprockets-4.2.1/test/fixtures/asset/multi_line_comment.js000066400000000000000000000000341447572140400237300ustar00rootroot00000000000000/******/ function foo() { } sprockets-4.2.1/test/fixtures/asset/multiple.js000066400000000000000000000000571447572140400217050ustar00rootroot00000000000000// =require "project" // =require "project.js" sprockets-4.2.1/test/fixtures/asset/noengine.js000066400000000000000000000000271447572140400216510ustar00rootroot00000000000000// =require "users.js" sprockets-4.2.1/test/fixtures/asset/noformat.coffee000066400000000000000000000000171447572140400225060ustar00rootroot00000000000000"CoffeeScript" sprockets-4.2.1/test/fixtures/asset/one.css000066400000000000000000000000101447572140400207740ustar00rootroot00000000000000.one {} sprockets-4.2.1/test/fixtures/asset/project.css000066400000000000000000000000141447572140400216650ustar00rootroot00000000000000.project {} sprockets-4.2.1/test/fixtures/asset/project.js.erb000066400000000000000000000000551447572140400222650ustar00rootroot00000000000000var Project = { find: function(id) { } };sprockets-4.2.1/test/fixtures/asset/relative/000077500000000000000000000000001447572140400213255ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/relative/require.js000066400000000000000000000000271447572140400233360ustar00rootroot00000000000000//= require ../project sprockets-4.2.1/test/fixtures/asset/relative/require_other_load_path.js000066400000000000000000000000421447572140400265470ustar00rootroot00000000000000//= require ../../default/gallery sprockets-4.2.1/test/fixtures/asset/relative/require_outside_path.js000066400000000000000000000000421447572140400261030ustar00rootroot00000000000000//= require ../../default/gallery sprockets-4.2.1/test/fixtures/asset/require_manifest.js.erb000066400000000000000000000005161447572140400241630ustar00rootroot00000000000000<% link_asset "application.js" %> <% link_asset "application.css" %> <% link_asset "POW.png" %> define("application.js", <%= environment["application.js"].digest_path.inspect %>) define("application.css", <%= environment["application.css"].digest_path.inspect %>) define("POW.png", <%= environment["POW.png"].digest_path.inspect %>) sprockets-4.2.1/test/fixtures/asset/require_manifest2.js.erb000066400000000000000000000004601447572140400242430ustar00rootroot00000000000000//= link application.js //= link application.css //= link POW.png define("application.js", <%= environment["application.js"].digest_path.inspect %>) define("application.css", <%= environment["application.css"].digest_path.inspect %>) define("POW.png", <%= environment["POW.png"].digest_path.inspect %>) sprockets-4.2.1/test/fixtures/asset/require_self.css000066400000000000000000000001141447572140400227050ustar00rootroot00000000000000/* *= require tree/all/b *= require_self *= require project */ body {} sprockets-4.2.1/test/fixtures/asset/require_self_twice.css000066400000000000000000000001351447572140400241030ustar00rootroot00000000000000/* *= require tree/all/b *= require_self *= require project *= require_self */ body {} sprockets-4.2.1/test/fixtures/asset/required_assets.js000066400000000000000000000001211447572140400232440ustar00rootroot00000000000000//= require dependencies/a //= require dependencies/b //= require dependencies/c sprockets-4.2.1/test/fixtures/asset/semicolons/000077500000000000000000000000001447572140400216655ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/semicolons/bar.js000066400000000000000000000000101447572140400227560ustar00rootroot00000000000000var Bar sprockets-4.2.1/test/fixtures/asset/semicolons/index.js000066400000000000000000000000561447572140400233330ustar00rootroot00000000000000//= require ./bar (function() { var Foo }) sprockets-4.2.1/test/fixtures/asset/source.js.map000066400000000000000000000414071447572140400221320ustar00rootroot00000000000000{ version: 3, file: "application.js", mappings: "ACDA;;ACAA;AAAA,MAAA,u+BAAA;IAAA;;;;;;EAAA,SAAA,GAAA;;EAAA,SAAA,GAAA;;EAAA,sBAAA,GAAA;;EAAA,WAAA,GAAA;;EAAA,YAAA,GAAA;;EAAA,YAAA,GAAA;;EAAA,OAAA,GAAA;;EAAA,GAAA,GAAA;;EAAA,MAAA,GAAA;IAAA,aAAA,EAAA,oBAAA;IAAA,KAAA,EAAA,YAAA;IAAA,OAAA,EAAA,cAAA;IAAA,MAAA,EAAA,aAAA;IAAA,MAAA,EAAA,aAAA;IAAA,IAAA,EAAA,WAAA;IAAA,OAAA,EAAA,cAAA;IAAA,aAAA,EAAA,oBAAA;IAAA,MAAA,EAAA,aAAA;;;EAAA,KAAA,GAAA,SAAA,GAAA;AAAA,QAAA;IAAA,GAAA,GAAA,IAAA,YAAA,CAAA,GAAA;IAAA,eAAA,CAAA;IAAA,gBAAA,CAAA;;MAAA,WAAA,CAAA,KAAA,CAAA;;IAAA,IAAA,sBAAA,IAAA,CAAA,UAAA,GAAA,kBAAA,CAAA,GAAA,CAAA,QAAA,CAAA,CAAA;MAAA,YAAA,CAAA,UAAA;aAAA,gBAAA,CAAA,GAAA,EAAA,IAAA,EAAA,KAAA,EAAA;KAAA,MAAA;aAAA,gBAAA,CAAA,GAAA,EAAA,mBAAA,EAAA;;EAAA;;EAAA,kBAAA,GAAA,SAAA,GAAA;AAAA,QAAA;IAAA,UAAA,GAAA,SAAA,CAAA,GAAA;IAAA,IAAA,UAAA,IAAA,CAAA,UAAA,CAAA,uBAAA;aAAA,WAAA;;EAAA;;EAAA,qBAAA,GAAA,SAAA,MAAA;;MAAA,SAAA;;WAAA,sBAAA,GAAA;EAAA;;EAAA,iBAAA,GAAA,SAAA,MAAA;;MAAA,SAAA;;IAAA,IAAA,CAAA,yBAAA;AAAA,aAAA;;IAAA,IAAA,MAAA;mCAAA,cAAA,cAAA,IAAA,WAAA,CAAA,MAAA,EAAA;KAAA,MAAA;;QAAA,WAAA,CAAA,SAAA,CAAA;;aAAA,WAAA,GAAA,KAAA;;EAAA;;EAAA,gBAAA,GAAA,SAAA,GAAA,EAAA,cAAA,EAAA,eAAA;;MAAA,kBAAA;;IAAA,YAAA,CAAA,MAAA,CAAA,KAAA,EAAA;MAAA,GAAA,EAAA,GAAA,CAAA,QAAA;KAAA;;MAAA,GAAA,CAAA,KAAA,CAAA;;IAAA,GAAA,GAAA,IAAA;IAAA,GAAA,CAAA,IAAA,CAAA,KAAA,EAAA,GAAA,CAAA,+BAAA,CAAA,CAAA,EAAA,IAAA;IAAA,GAAA,CAAA,gBAAA,CAAA,QAAA,EAAA,mDAAA;IAAA,GAAA,CAAA,gBAAA,CAAA,eAAA,EAAA,OAAA;IAAA,GAAA,CAAA,MAAA,GAAA,SAAA;AAAA,UAAA;MAAA,YAAA,CAAA,MAAA,CAAA,OAAA,EAAA;QAAA,GAAA,EAAA,GAAA,CAAA,QAAA;OAAA;MAAA,IAAA,GAAA,GAAA,eAAA,CAAA,CAAA;QAAA,aAAA,CAAA,GAAA;QAAA,oBAAA,CAAA;QAAA,UAAA,aAAA,mBAAA,CAAA,GAAA,CAAA;QAAA,mCAAA,CAAA;;UAAA;;eAAA,YAAA,CAAA,MAAA,CAAA,IAAA,EAAA;OAAA,MAAA;eAAA,QAAA,CAAA,QAAA,CAAA,IAAA,GAAA,mBAAA,CAAA,CAAA,IAAA,GAAA,CAAA,SAAA;;IAAA;IAAA,IAAA,WAAA,IAAA,eAAA;MAAA,GAAA,CAAA,UAAA,GAAA,CAAA,SAAA,KAAA;eAAA,SAAA,KAAA;AAAA,cAAA;UAAA,OAAA,GAAA,KAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,MAAA,GAAA,KAAA,CAAA,KAAA,GAAA,GAAA,GAAA,WAAA,CAAA,KAAA,GAAA,CAAA,GAAA,GAAA,WAAA,CAAA,KAAA,CAAA,GAAA;iBAAA,WAAA,CAAA,SAAA,CAAA,OAAA;QAAA;MAAA,CAAA,CAAA,CAAA,IAAA,EAAA;;IAAA,GAAA,CAAA,SAAA,GAAA,SAAA;aAAA,GAAA,GAAA;IAAA;IAAA,GAAA,CAAA,OAAA,GAAA,SAAA;aAAA,QAAA,CAAA,QAAA,CAAA,IAAA,GAAA,GAAA,CAAA;IAAA;WAAA,GAAA,CAAA,IAAA,CAAA;EAAA;;EAAA,YAAA,GAAA,SAAA,UAAA;;MAAA,GAAA,CAAA,KAAA,CAAA;;IAAA,UAAA,CAAA,UAAA,CAAA,KAAA,EAAA,UAAA,CAAA,IAAA;IAAA,oBAAA,CAAA,UAAA;WAAA,YAAA,CAAA,MAAA,CAAA,OAAA;EAAA;;EAAA,gBAAA,GAAA,SAAA;AAAA,QAAA;IAAA,eAAA,GAAA,IAAA,YAAA,CAAA,YAAA,CAAA,GAAA;IAAA,SAAA,CAAA,eAAA,CAAA,QAAA,CAAA,GAAA;MAAA,GAAA,EAAA,eAAA,CAAA,QAAA;MAAA,IAAA,EAAA,QAAA,CAAA,IAAA;MAAA,KAAA,EAAA,QAAA,CAAA,KAAA;MAAA,SAAA,EAAA,MAAA,CAAA,WAAA;MAAA,SAAA,EAAA,MAAA,CAAA,WAAA;MAAA,QAAA,EAAA,IAAA,IAAA,CAAA,CAAA,CAAA,OAAA,CAAA,CAAA;MAAA,uBAAA,EAAA,4DAAA;;WAAA,oBAAA,CAAA,SAAA;EAAA;;EAAA,WAAA,GAAA,SAAA,IAAA;;MAAA,OAAA;;IAAA,IAAA,SAAA,CAAA,IAAA,CAAA,IAAA,CAAA;aAAA,SAAA,GAAA,QAAA,CAAA,IAAA,EAAA;;EAAA;;EAAA,oBAAA,GAAA,SAAA,KAAA;AAAA,QAAA;IAAA,aAAA,GAAA,MAAA,CAAA,IAAA,CAAA,SAAA;IAAA,qBAAA,GAAA,aAAA,CAAA,GAAA,CAAA,SAAA,GAAA;aAAA,SAAA,CAAA,GAAA,CAAA,CAAA;IAAA,CAAA,CAAA,CAAA,IAAA,CAAA,SAAA,CAAA,EAAA,CAAA;aAAA,CAAA,GAAA;IAAA,CAAA;AAAA;SAAA,+CAAA;;YAAA,SAAA,CAAA,GAAA,CAAA,CAAA,QAAA,IAAA,qBAAA,CAAA,KAAA;;;MAAA,YAAA,CAAA,MAAA,CAAA,MAAA,EAAA,SAAA,CAAA,GAAA,CAAA;mBAAA,OAAA,SAAA,CAAA,GAAA;AAAA;;EAAA;;EAAA,UAAA,GAAA,SAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,UAAA;IAAA,YAAA,CAAA,MAAA,CAAA,aAAA;IAAA,QAAA,CAAA,KAAA,GAAA;IAAA,QAAA,CAAA,eAAA,CAAA,YAAA,CAAA,IAAA,EAAA,QAAA,CAAA,IAAA;IAAA,IAAA,iBAAA;MAAA,SAAA,CAAA,MAAA,CAAA,SAAA,EAAA;;IAAA,mBAAA,CAAA;IAAA,IAAA,UAAA;MAAA,iBAAA,CAAA,EAAA;;IAAA,YAAA,GAAA,MAAA,CAAA,OAAA,CAAA;;MAAA,WAAA,CAAA,IAAA,CAAA;;IAAA,YAAA,CAAA,MAAA,CAAA,MAAA;WAAA,YAAA,CAAA,MAAA,CAAA,MAAA;EAAA;;EAAA,iBAAA,GAAA,SAAA;AAAA,QAAA;IAAA,OAAA,GAAA,KAAA,CAAA,SAAA,CAAA,KAAA,CAAA,IAAA,CAAA,QAAA,CAAA,IAAA,CAAA,gBAAA,CAAA,4CAAA,CAAA;AAAA,SAAA,yCAAA;;mBAAA,MAAA,CAAA,KAAA,KAAA,EAAA,IAAA,GAAA,KAAA;;;MAAA,IAAA,GAAA,QAAA,CAAA,aAAA,CAAA,QAAA;AAAA;AAAA,WAAA,wCAAA;;QAAA,IAAA,CAAA,YAAA,CAAA,IAAA,CAAA,IAAA,EAAA,IAAA,CAAA,KAAA;AAAA;MAAA,IAAA,CAAA,MAAA,CAAA,YAAA,CAAA,OAAA,CAAA;QAAA,IAAA,CAAA,KAAA,GAAA,MAAA;;MAAA,IAAA,CAAA,WAAA,CAAA,QAAA,CAAA,cAAA,CAAA,MAAA,CAAA,SAAA,CAAA;MAAA,oBAAA,UAAA,EAAA,qBAAA;MAAA,UAAA,CAAA,WAAA,CAAA,MAAA;MAAA,UAAA,CAAA,YAAA,CAAA,IAAA,EAAA,WAAA;AAAA;EAAA;;EAAA,kBAAA,GAAA,SAAA,IAAA;IAAA,IAAA,CAAA,SAAA,GAAA,IAAA,CAAA,SAAA,CAAA,OAAA,CAAA,iCAAA,EAAA,EAAA;WAAA;EAAA;;EAAA,mBAAA,GAAA,SAAA;AAAA,QAAA;IAAA,gBAAA,GAAA,CAAA,IAAA,GAAA,QAAA,CAAA,gBAAA,CAAA,uCAAA,CAAA,CAAA,CAAA,IAAA,CAAA,MAAA,GAAA,CAAA;IAAA,IAAA,gBAAA,IAAA,QAAA,CAAA,aAAA,KAAA,gBAAA;aAAA,gBAAA,CAAA,KAAA,CAAA,EAAA;;EAAA;;EAAA,aAAA,GAAA,SAAA,GAAA;IAAA,IAAA,CAAA,GAAA,GAAA,IAAA,YAAA,CAAA,GAAA,CAAA,CAAA,CAAA,QAAA,KAAA,OAAA;aAAA,MAAA,CAAA,OAAA,CAAA,SAAA,CAAA;QAAA,UAAA,EAAA,IAAA;QAAA,GAAA,EAAA,GAAA,CAAA,QAAA;OAAA,EAAA,EAAA,EAAA,GAAA,CAAA,QAAA,EAAA;;EAAA;;EAAA,oBAAA,GAAA,SAAA;AAAA,QAAA;IAAA,IAAA,QAAA,GAAA,GAAA,CAAA,iBAAA,CAAA,qBAAA,CAAA;MAAA,QAAA,GAAA,IAAA,YAAA,CAAA,QAAA;MAAA,aAAA,GAAA,QAAA,CAAA,SAAA,CAAA,CAAA,GAAA,QAAA,CAAA,QAAA,CAAA,IAAA,GAAA;aAAA,MAAA,CAAA,OAAA,CAAA,YAAA,CAAA,MAAA,CAAA,OAAA,CAAA,KAAA,EAAA,EAAA,EAAA,QAAA,CAAA,IAAA,GAAA,aAAA,EAAA;;EAAA;;EAAA,mBAAA,GAAA,SAAA;AAAA,QAAA;IAAA,IAAA,wDAAA,IAAA,CAAA,IAAA,YAAA,CAAA,QAAA,CAAA,CAAA,CAAA,WAAA,CAAA,CAAA;aAAA,SAAA;;EAAA;;EAAA,eAAA,GAAA,SAAA;WAAA,OAAA,GAAA,QAAA,CAAA,QAAA,CAAA;EAAA;;EAAA,kBAAA,GAAA,SAAA;WAAA,MAAA,CAAA,OAAA,CAAA,YAAA,CAAA;MAAA,UAAA,EAAA,IAAA;MAAA,GAAA,EAAA,QAAA,CAAA,QAAA,CAAA,IAAA;KAAA,EAAA,EAAA,EAAA,QAAA,CAAA,QAAA,CAAA,IAAA;EAAA;;EAAA,oBAAA,GAAA,SAAA;WAAA,YAAA,GAAA,MAAA,CAAA,OAAA,CAAA;EAAA;;EAAA,mCAAA,GAAA,SAAA;AAAA,QAAA;IAAA,IAAA,SAAA,CAAA,SAAA,CAAA,KAAA,CAAA,SAAA,CAAA,IAAA,CAAA,CAAA,GAAA,GAAA,IAAA,YAAA,CAAA,CAAA,SAAA,CAAA,CAAA;MAAA,MAAA,CAAA,OAAA,CAAA,YAAA,CAAA,YAAA,EAAA,EAAA,EAAA,GAAA,CAAA,WAAA,CAAA,CAAA;aAAA,QAAA,CAAA,QAAA,CAAA,IAAA,GAAA,GAAA,CAAA,KAAA;;EAAA;;EAAA,oBAAA,GAAA,SAAA,IAAA;WAAA,MAAA,CAAA,QAAA,CAAA,IAAA,CAAA,SAAA,EAAA,IAAA,CAAA,SAAA;EAAA;;EAAA,mBAAA,GAAA,SAAA;IAAA,IAAA,QAAA,CAAA,QAAA,CAAA,IAAA;aAAA,QAAA,CAAA,QAAA,CAAA,IAAA,GAAA,QAAA,CAAA,QAAA,CAAA,KAAA;KAAA,MAAA;aAAA,MAAA,CAAA,QAAA,CAAA,CAAA,EAAA,CAAA,EAAA;;EAAA;;EAAA,KAAA,GAAA,SAAA,QAAA;AAAA,QAAA;IAAA,IAAA,kBAAA,IAAA,OAAA,QAAA,KAAA,QAAA;AAAA,aAAA,SAAA;;IAAA,IAAA,GAAA,IAAA,QAAA,CAAA,WAAA,CAAA;AAAA,SAAA,eAAA;;MAAA,IAAA,CAAA,GAAA,CAAA,GAAA,KAAA,CAAA,KAAA;AAAA;WAAA;EAAA;;EAAA,SAAA,GAAA,SAAA,IAAA;AAAA,QAAA;IAAA,KAAA,6EAAA,CAAA,CAAA,CAAA,CAAA,WAAA,CAAA,WAAA,IAAA;IAAA,QAAA,CAAA,MAAA,GAAA,IAAA,GAAA;WAAA;EAAA;;EAAA,YAAA,GAAA,SAAA,IAAA,EAAA,IAAA;AAAA,QAAA;IAAA,IAAA,OAAA,SAAA,KAAA,WAAA;MAAA,KAAA,CAAA,IAAA,CAAA,QAAA,EAAA,IAAA,EAAA,IAAA,EAAA,IAAA,EAAA;;IAAA,KAAA,GAAA,QAAA,CAAA,WAAA,CAAA,QAAA;IAAA,IAAA,IAAA;MAAA,KAAA,CAAA,IAAA,GAAA,KAAA;;IAAA,KAAA,CAAA,SAAA,CAAA,IAAA,EAAA,IAAA,EAAA,IAAA;WAAA,QAAA,CAAA,aAAA,CAAA,KAAA;EAAA;;EAAA,mBAAA,GAAA,SAAA,GAAA;WAAA,CAAA,YAAA,CAAA,MAAA,CAAA,aAAA,EAAA;MAAA,GAAA,EAAA,GAAA;KAAA;EAAA;;EAAA,eAAA,GAAA,SAAA;AAAA,QAAA;IAAA,mBAAA,GAAA,SAAA;AAAA,UAAA;aAAA,CAAA,GAAA,WAAA,GAAA,CAAA,OAAA,OAAA,GAAA,GAAA;IAAA;IAAA,YAAA,GAAA,SAAA;AAAA,UAAA;aAAA,+DAAA,IAAA,WAAA,CAAA,KAAA,CAAA,iEAAA;IAAA;IAAA,kBAAA,GAAA,SAAA,GAAA;AAAA,UAAA;AAAA;AAAA;WAAA,qCAAA;;YAAA;uBAAA,IAAA,CAAA,YAAA,CAAA,KAAA,CAAA,IAAA,IAAA,CAAA,YAAA,CAAA,MAAA;;AAAA;;IAAA;IAAA,aAAA,GAAA,SAAA,GAAA;AAAA,UAAA;MAAA,iBAAA,eAAA,kBAAA,CAAA,QAAA;MAAA,aAAA,GAAA,kBAAA,CAAA,GAAA;aAAA,aAAA,CAAA,MAAA,KAAA,YAAA,CAAA,MAAA,IAAA,YAAA,CAAA,aAAA,EAAA,YAAA,CAAA,CAAA,MAAA,KAAA,YAAA,CAAA;IAAA;IAAA,YAAA,GAAA,SAAA,CAAA,EAAA,CAAA;AAAA,UAAA;MAAA,IAAA,CAAA,CAAA,MAAA,GAAA,CAAA,CAAA,MAAA;QAAA,MAAA,CAAA,CAAA,EAAA,CAAA,CAAA,EAAA,UAAA,EAAA,WAAA;;AAAA;WAAA,mCAAA;;YAAA,aAAA,CAAA,EAAA,KAAA;uBAAA;;AAAA;;IAAA;IAAA,IAAA,CAAA,mBAAA,CAAA,CAAA,IAAA,YAAA,CAAA,CAAA;MAAA,GAAA,GAAA,cAAA,CAAA,GAAA,CAAA,YAAA;MAAA,IAAA,GAAA,IAAA,CAAA,aAAA,CAAA,GAAA,CAAA;AAAA,eAAA,IAAA;OAAA;;EAAA;;EAAA,mBAAA,GAAA,SAAA,GAAA;AAAA,QAAA;IAAA,KAAA,GAAA,GAAA,CAAA,aAAA,CAAA,OAAA;WAAA,iBAAA,KAAA,CAAA,oBAAA,EAAA,kBAAA,CAAA,GAAA,CAAA,aAAA,CAAA,MAAA,CAAA,CAAA,EAAA,SAAA,CAAA,GAAA,CAAA,GAAA,CAAA,CAAA,KAAA,EAAA,YAAA;EAAA;;EAAA,SAAA,GAAA;IAAA,GAAA,EAAA,SAAA,GAAA;AAAA,UAAA;;QAAA,MAAA;;aAAA;QAAA,IAAA,EAAA,GAAA,GAAA,GAAA,CAAA,aAAA,CAAA,yBAAA,CAAA;QAAA,KAAA,yDAAA,GAAA,CAAA,aAAA,4BAAA;;IAAA,CAAA;IAAA,MAAA,EAAA,SAAA,MAAA;AAAA,UAAA;MAAA,OAAA,GAAA,IAAA,CAAA,GAAA,CAAA;MAAA,IAAA,uBAAA,IAAA,gBAAA,IAAA,OAAA,CAAA,KAAA,KAAA,MAAA;eAAA,OAAA,CAAA,IAAA,CAAA,YAAA,CAAA,SAAA,EAAA,MAAA,EAAA;;IAAA,CAAA;;;EAAA,cAAA,GAAA,SAAA,IAAA;AAAA,QAAA;IAAA,GAAA,GAAA,QAAA,CAAA,eAAA,CAAA,SAAA,CAAA;IAAA,GAAA,CAAA,SAAA,GAAA;IAAA,GAAA,CAAA,IAAA,GAAA,GAAA,CAAA,aAAA,CAAA,MAAA;IAAA,GAAA,CAAA,IAAA,GAAA,GAAA,CAAA,aAAA,CAAA,MAAA;WAAA;EAAA;;EAAA;IAAA,sBAAA,SAAA;MAAA,IAAA,CAAA,+BAAA,YAAA,QAAA,CAAA,QAAA,CAAA;MAAA,IAAA,IAAA,CAAA,QAAA,CAAA,WAAA,KAAA,YAAA;AAAA,eAAA,IAAA,CAAA,SAAA;;MAAA,IAAA,CAAA,MAAA,CAAA;IAAA;;2BAAA,WAAA,GAAA,SAAA;aAAA,IAAA,CAAA,IAAA,CAAA,OAAA,CAAA,IAAA,CAAA,IAAA,EAAA,EAAA,CAAA,CAAA,OAAA,CAAA,GAAA,EAAA,EAAA;IAAA;;2BAAA,+BAAA,GAAA,SAAA;aAAA,IAAA,CAAA,WAAA,CAAA;IAAA;;2BAAA,SAAA,GAAA,SAAA;aAAA,IAAA,CAAA,IAAA,CAAA,MAAA,KAAA;IAAA;;2BAAA,WAAA,GAAA,SAAA;aAAA,IAAA,CAAA,MAAA,KAAA,CAAA,IAAA,YAAA,CAAA,CAAA;IAAA;;2BAAA,MAAA,GAAA,SAAA;AAAA,UAAA;MAAA,qBAAA,IAAA,CAAA,OAAA,IAAA,CAAA,OAAA,QAAA,CAAA,aAAA,CAAA,GAAA,CAAA,CAAA,CAAA,IAAA,GAAA,IAAA,CAAA;MAAA,MAAA,IAAA,CAAA,IAAA,EAAA,IAAA,CAAA,WAAA,IAAA,EAAA,IAAA,CAAA,eAAA,QAAA,EAAA,IAAA,CAAA,WAAA,IAAA,EAAA,IAAA,CAAA,eAAA,QAAA,EAAA,IAAA,CAAA,WAAA,IAAA,EAAA,IAAA,CAAA,eAAA,QAAA,EAAA,IAAA,CAAA,aAAA,MAAA,EAAA,IAAA,CAAA,WAAA;MAAA,IAAA,CAAA,MAAA,GAAA,CAAA,IAAA,CAAA,QAAA,EAAA,IAAA,EAAA,IAAA,CAAA,QAAA,CAAA,CAAA,IAAA,CAAA,EAAA;MAAA,IAAA,IAAA,CAAA,IAAA,CAAA,MAAA,KAAA,CAAA;QAAA,IAAA,CAAA,MAAA,IAAA,GAAA,GAAA,IAAA,CAAA,KAAA;;MAAA,IAAA,CAAA,QAAA,GAAA,CAAA,IAAA,CAAA,QAAA,EAAA,IAAA,CAAA,MAAA,EAAA,IAAA,CAAA,IAAA,CAAA,CAAA,IAAA,CAAA,EAAA;aAAA,IAAA,CAAA,QAAA,GAAA,IAAA,CAAA;IAAA;;;;;;EAAA;;;IAAA,IAAA,CAAA,eAAA,GAAA,CAAA,MAAA;;IAAA,IAAA,CAAA,eAAA,GAAA,SAAA;AAAA,UAAA;MAAA;AAAA,WAAA,4CAAA;;QAAA,IAAA,CAAA,eAAA,CAAA,IAAA,CAAA,SAAA;AAAA;aAAA,IAAA,CAAA;IAAA;;IAAA,cAAA,KAAA;MAAA,IAAA,CAAA,OAAA;MAAA,IAAA,IAAA,CAAA,IAAA,CAAA,WAAA,KAAA,IAAA;AAAA,eAAA,IAAA,CAAA,KAAA;;MAAA,IAAA,CAAA,QAAA,GAAA,IAAA,CAAA,IAAA,CAAA;MAAA,IAAA,CAAA,eAAA,GAAA,IAAA,CAAA;MAAA,IAAA,CAAA,IAAA,GAAA,IAAA,CAAA,IAAA,CAAA,SAAA,CAAA,KAAA;MAAA,uCAAA,SAAA;IAAA;;mBAAA,YAAA,GAAA,SAAA;aAAA,IAAA,CAAA,WAAA,CAAA,CAAA,IAAA,IAAA,CAAA,SAAA,CAAA,CAAA,IAAA,IAAA,CAAA,QAAA,CAAA,CAAA,IAAA,IAAA,CAAA,OAAA,CAAA,CAAA,IAAA,IAAA,CAAA,OAAA,CAAA;IAAA;;mBAAA,SAAA,GAAA,SAAA;aAAA,CAAA,IAAA,CAAA,IAAA,CAAA,MAAA,GAAA,CAAA,IAAA,IAAA,CAAA,IAAA,CAAA,MAAA,CAAA,IAAA,CAAA,IAAA,CAAA,MAAA,GAAA,CAAA,CAAA,KAAA,GAAA,CAAA,IAAA,CAAA,IAAA,CAAA,WAAA,CAAA,CAAA,KAAA,CAAA,IAAA,YAAA,CAAA,CAAA,WAAA,CAAA,CAAA;IAAA;;mBAAA,QAAA,GAAA,SAAA;aAAA,IAAA,CAAA,QAAA,CAAA,KAAA,CAAA,YAAA,CAAA,IAAA,CAAA,IAAA,CAAA,QAAA,CAAA,KAAA,CAAA,IAAA,MAAA,CAAA,QAAA,GAAA,CAAA,IAAA,CAAA,eAAA,CAAA,IAAA,CAAA,GAAA,CAAA,CAAA,GAAA,KAAA,EAAA,GAAA,CAAA;IAAA;;mBAAA,OAAA,GAAA,SAAA;AAAA,UAAA;MAAA,IAAA,GAAA,IAAA,CAAA;AAAA,aAAA,CAAA,CAAA,MAAA,IAAA,IAAA,KAAA,QAAA,CAAA;QAAA,MAAA,GAAA;QAAA,IAAA,GAAA,IAAA,CAAA;MAAA;aAAA;IAAA;;mBAAA,OAAA,GAAA,SAAA;aAAA,IAAA,CAAA,IAAA,CAAA,MAAA,CAAA,MAAA,KAAA;IAAA;;;;KAAA;;EAAA;IAAA,KAAA,CAAA,kBAAA,GAAA,SAAA,KAAA;MAAA,IAAA,CAAA,KAAA,CAAA,gBAAA;QAAA,QAAA,CAAA,mBAAA,CAAA,OAAA,EAAA,KAAA,CAAA,MAAA,EAAA,KAAA;eAAA,QAAA,CAAA,gBAAA,CAAA,OAAA,EAAA,KAAA,CAAA,MAAA,EAAA,KAAA,EAAA;;IAAA;;IAAA,KAAA,CAAA,MAAA,GAAA,SAAA,KAAA;aAAA,IAAA,KAAA,CAAA,KAAA;IAAA;;IAAA,eAAA,MAAA;MAAA,IAAA,CAAA,QAAA;MAAA,IAAA,IAAA,CAAA,KAAA,CAAA,gBAAA;AAAA,eAAA;;MAAA,IAAA,CAAA,YAAA,CAAA;MAAA,IAAA,IAAA,CAAA,mBAAA,CAAA,CAAA;QAAA,IAAA,CAAA,mBAAA,CAAA,IAAA,CAAA,IAAA,CAAA,QAAA,CAAA;UAAA,KAAA,CAAA,IAAA,CAAA,IAAA,CAAA,IAAA,EAAA;;QAAA,IAAA,CAAA,KAAA,CAAA,cAAA,CAAA,EAAA;;IAAA;;oBAAA,YAAA,GAAA,SAAA;AAAA,UAAA;MAAA,IAAA,GAAA,IAAA,CAAA,KAAA,CAAA;AAAA,aAAA,CAAA,CAAA,CAAA,IAAA,CAAA,UAAA,IAAA,IAAA,CAAA,QAAA,KAAA,GAAA,CAAA;QAAA,IAAA,GAAA,IAAA,CAAA;MAAA;MAAA,IAAA,IAAA,CAAA,QAAA,KAAA,GAAA,IAAA,IAAA,CAAA,IAAA,CAAA,MAAA,KAAA,CAAA;eAAA,IAAA,CAAA,IAAA,GAAA,IAAA,IAAA,CAAA,IAAA,EAAA;;IAAA;;oBAAA,mBAAA,GAAA,SAAA;aAAA,mBAAA,IAAA,CAAA,CAAA,IAAA,CAAA,IAAA,CAAA,YAAA,CAAA,CAAA,IAAA,IAAA,CAAA,iBAAA,CAAA,CAAA;IAAA;;oBAAA,iBAAA,GAAA,SAAA;aAAA,IAAA,CAAA,KAAA,CAAA,KAAA,GAAA,CAAA,IAAA,IAAA,CAAA,KAAA,CAAA,OAAA,IAAA,IAAA,CAAA,KAAA,CAAA,OAAA,IAAA,IAAA,CAAA,KAAA,CAAA,QAAA,IAAA,IAAA,CAAA,KAAA,CAAA;IAAA;;;;;;EAAA;AAAA,QAAA;;IAAA,SAAA,GAAA;;IAAA,qBAAA,eAAA;MAAA,IAAA,CAAA,kBAAA;;MAAA,IAAA,CAAA,KAAA,GAAA;MAAA,IAAA,CAAA,OAAA,GAAA;MAAA,IAAA,CAAA,KAAA,GAAA;MAAA,IAAA,CAAA,OAAA,GAAA;MAAA,IAAA,CAAA,OAAA,CAAA;IAAA;;0BAAA,OAAA,GAAA,SAAA;MAAA,IAAA,CAAA,OAAA,GAAA,QAAA,CAAA,aAAA,CAAA,IAAA,CAAA,eAAA;MAAA,IAAA,CAAA,OAAA,CAAA,SAAA,CAAA,GAAA,CAAA,SAAA;MAAA,IAAA,CAAA,YAAA,GAAA,QAAA,CAAA,aAAA,CAAA,OAAA;MAAA,QAAA,CAAA,IAAA,CAAA,WAAA,CAAA,IAAA,CAAA,YAAA;aAAA,IAAA,CAAA,YAAA,CAAA;IAAA;;0BAAA,SAAA,GAAA,SAAA;MAAA,IAAA,CAAA,OAAA,CAAA,SAAA,CAAA,MAAA,CAAA,SAAA;aAAA,QAAA,CAAA,IAAA,CAAA,WAAA,CAAA,IAAA,CAAA,YAAA;IAAA;;0BAAA,KAAA,GAAA,SAAA;aAAA,IAAA,CAAA,SAAA,CAAA,CAAA;IAAA;;0BAAA,SAAA,GAAA,SAAA,KAAA;AAAA,UAAA;MAAA,IAAA,CAAA,KAAA,UAAA,IAAA,CAAA,MAAA,OAAA,IAAA,GAAA,CAAA;QAAA,IAAA,CAAA,KAAA,GAAA;QAAA,IAAA,CAAA,YAAA,CAAA;QAAA,IAAA,IAAA,CAAA,KAAA,KAAA,GAAA;iBAAA,IAAA,CAAA,YAAA,CAAA,EAAA;SAAA,MAAA,IAAA,IAAA,CAAA,KAAA,GAAA,CAAA;iBAAA,IAAA,CAAA,aAAA,CAAA,EAAA;SAAA;;IAAA;;0BAAA,IAAA,GAAA,SAAA;MAAA,IAAA,IAAA,CAAA,KAAA,GAAA,CAAA;QAAA,IAAA,CAAA,SAAA,CAAA,GAAA;eAAA,IAAA,CAAA,MAAA,CAAA,EAAA;;IAAA;;0BAAA,MAAA,GAAA,SAAA;AAAA,UAAA;MAAA,eAAA,GAAA,IAAA,CAAA;MAAA,UAAA,CAAA,CAAA,SAAA,KAAA;eAAA,SAAA;UAAA,KAAA,CAAA,OAAA,GAAA;iBAAA,KAAA,CAAA,YAAA,CAAA;QAAA;MAAA,CAAA,CAAA,CAAA,IAAA,CAAA,EAAA,IAAA,CAAA,KAAA,GAAA,CAAA;aAAA,UAAA,CAAA,CAAA,SAAA,KAAA;eAAA,SAAA;UAAA,KAAA,CAAA,KAAA,GAAA;UAAA,KAAA,CAAA,OAAA,GAAA;iBAAA,KAAA,CAAA,UAAA,CAAA,CAAA,EAAA,SAAA;mBAAA,KAAA,CAAA,YAAA,CAAA,IAAA;UAAA,CAAA;QAAA;MAAA,CAAA,CAAA,CAAA,IAAA,CAAA,EAAA,IAAA,CAAA,KAAA;IAAA;;0BAAA,aAAA,GAAA,SAAA;MAAA,IAAA,IAAA,CAAA,SAAA;AAAA,eAAA;;MAAA,IAAA,CAAA,SAAA,GAAA;aAAA,UAAA,CAAA,IAAA,CAAA,QAAA,EAAA,IAAA,CAAA,KAAA;IAAA;;0BAAA,YAAA,GAAA,SAAA;aAAA,OAAA,IAAA,CAAA;IAAA;;0BAAA,QAAA,GAAA,SAAA;MAAA,IAAA,CAAA,IAAA,CAAA,SAAA;AAAA,eAAA;;MAAA,IAAA,CAAA,SAAA,CAAA,IAAA,CAAA,KAAA,GAAA,IAAA,CAAA,MAAA,CAAA,CAAA,GAAA,CAAA;aAAA,UAAA,CAAA,IAAA,CAAA,QAAA,EAAA,IAAA,CAAA,KAAA;IAAA;;0BAAA,UAAA,GAAA,SAAA,KAAA,EAAA,EAAA;AAAA,UAAA;MAAA,aAAA,GAAA,IAAA,CAAA;MAAA,IAAA,CAAA,KAAA,GAAA;MAAA,MAAA,GAAA,EAAA,CAAA;MAAA,IAAA,CAAA,KAAA,GAAA;aAAA;IAAA;;0BAAA,YAAA,GAAA,SAAA,YAAA;;QAAA,eAAA;;MAAA,IAAA,YAAA;QAAA,IAAA,CAAA,4BAAA,CAAA,EAAA;;aAAA,IAAA,CAAA,YAAA,CAAA,WAAA,GAAA,IAAA,CAAA,cAAA,CAAA;IAAA;;0BAAA,4BAAA,GAAA,SAAA;aAAA,IAAA,CAAA,OAAA,GAAA,IAAA,CAAA,OAAA,KAAA,EAAA,GAAA,GAAA,GAAA;IAAA;;0BAAA,cAAA,GAAA,SAAA;aAAA,IAAA,CAAA,eAAA,GAAA,GAAA,GAAA,SAAA,GAAA,0BAAA,GAAA,IAAA,CAAA,OAAA,GAAA,4HAAA,GAAA,IAAA,CAAA,OAAA,GAAA,cAAA,GAAA,IAAA,CAAA,KAAA,GAAA,0BAAA,GAAA,IAAA,CAAA,KAAA,GAAA,uBAAA,GAAA,CAAA,IAAA,CAAA,KAAA,GAAA,CAAA,CAAA,GAAA;IAAA;;;;;;EAAA,oBAAA,GAAA,SAAA,EAAA;WAAA,UAAA,CAAA,EAAA,EAAA,GAAA;EAAA;;EAAA,qCAAA,GAAA,SAAA;WAAA,QAAA,CAAA,gBAAA,CAAA,kBAAA,EAAA,CAAA,SAAA;MAAA,YAAA,CAAA,MAAA,CAAA,MAAA;aAAA,YAAA,CAAA,MAAA,CAAA,MAAA;IAAA,CAAA,CAAA,EAAA,IAAA;EAAA;;EAAA,yCAAA,GAAA,SAAA;IAAA,IAAA,OAAA,MAAA,KAAA,WAAA;aAAA,MAAA,CAAA,QAAA,CAAA,CAAA,EAAA,CAAA,aAAA,EAAA,SAAA,KAAA,EAAA,GAAA,EAAA,QAAA;QAAA,IAAA,CAAA,MAAA,CAAA,IAAA,CAAA,GAAA,CAAA,YAAA,CAAA;AAAA,iBAAA;;eAAA,YAAA,CAAA,MAAA,CAAA,MAAA;MAAA,CAAA,EAAA;;EAAA;;EAAA,2BAAA,GAAA,SAAA,KAAA;AAAA,QAAA;IAAA,qCAAA,CAAA,mBAAA;MAAA,IAAA,UAAA,GAAA,SAAA,CAAA,CAAA,IAAA,YAAA,CAAA,KAAA,CAAA,KAAA,CAAA,GAAA,CAAA,CAAA,CAAA,QAAA,CAAA;QAAA,gBAAA,CAAA;eAAA,YAAA,CAAA,UAAA,EAAA;OAAA,MAAA;eAAA,KAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,IAAA,EAAA;OAAA;;EAAA;;EAAA,oBAAA,GAAA,SAAA;IAAA,kBAAA,CAAA;IAAA,oBAAA,CAAA;IAAA,QAAA,CAAA,gBAAA,CAAA,OAAA,EAAA,KAAA,CAAA,kBAAA,EAAA,IAAA;IAAA,MAAA,CAAA,gBAAA,CAAA,YAAA,EAAA,SAAA,KAAA;MAAA,kBAAA,CAAA;aAAA,oBAAA,CAAA;IAAA,CAAA,EAAA,KAAA;WAAA,oBAAA,CAAA,SAAA;aAAA,MAAA,CAAA,gBAAA,CAAA,UAAA,EAAA,2BAAA,EAAA,KAAA;IAAA,CAAA;EAAA;;EAAA,qBAAA,GAAA,MAAA,CAAA,OAAA,CAAA,KAAA,KAAA,MAAA,IAAA,SAAA,CAAA,SAAA,CAAA,KAAA,CAAA,iBAAA;;EAAA,wBAAA,GAAA,MAAA,CAAA,OAAA,IAAA,MAAA,CAAA,OAAA,CAAA,SAAA,IAAA,MAAA,CAAA,OAAA,CAAA,YAAA,IAAA;;EAAA,gBAAA,GAAA,CAAA,SAAA,CAAA,SAAA,CAAA,KAAA,CAAA,SAAA;;EAAA,mBAAA,UAAA,SAAA,CAAA,gBAAA,EAAA,KAAA,KAAA,IAAA,GAAA,KAAA;;EAAA,yBAAA,GAAA,wBAAA,IAAA,gBAAA,IAAA;;EAAA,2BAAA,GAAA,QAAA,CAAA,gBAAA,IAAA,QAAA,CAAA;;EAAA,IAAA,2BAAA;IAAA,qCAAA,CAAA;IAAA,yCAAA,CAAA,EAAA;;;EAAA,IAAA,yBAAA;IAAA,KAAA,GAAA;IAAA,oBAAA,CAAA,EAAA;GAAA,MAAA;IAAA,KAAA,GAAA,SAAA,GAAA;aAAA,QAAA,CAAA,QAAA,CAAA,IAAA,GAAA;IAAA,EAAA;;;EAAA,IAAA,CAAA,UAAA,GAAA;IAAA,OAAA,KAAA;IAAA,aAAA,WAAA;IAAA,uBAAA,qBAAA;IAAA,mBAAA,iBAAA;IAAA,mBAAA,EAAA,IAAA,CAAA,eAAA;IAAA,SAAA,EAAA,yBAAA;IAAA,MAAA,EAAA,KAAA,CAAA,MAAA,CAAA;;AAAA;;ACAA;AAAA,MAAA;;EAAA,CAAA,GAAA;;EAAA,CAAA,GAAA;;AAAA;;EAAA,OAAA,CAAA,GAAA,CAAA,CAAA,GAAA,CAAA;AAAA;ACAA", sources: [ "jquery.source-2065aecca0fb9b0567358d352ed5f1ab72fce139bf449b4d09805f5d9c3725ed.js", "jquery_ujs.source-d456baa54c1fa6be2ec3711f0a72ddf7a5b2f34a6b4f515f33767d6207b7d4b3.js", "turbolinks.source-53eb7b866c82186e0c978d4521e6c5f5a51cd61b63704b626ac6ae2e5c97103d.coffee", "app.source-e4ae11daa0c049fb57d2a07b4b91c57ff74432fd63a7183dd924ec05c5851bfe.coffee", "application.source-fb74e9f41bc17d9dec121acd6dce208018ed9467e660fdbf73bb9939eb5d9b33.js" ], names: [ ] }sprockets-4.2.1/test/fixtures/asset/sprite.css.erb000066400000000000000000000003631447572140400223030ustar00rootroot00000000000000/* *= depend_on "POW.png" */ <% require 'base64' path = File.expand_path("../POW.png", __FILE__) data = Base64.encode64(File.open(path, "rb") { |f| f.read }) %> .pow { background: url(data:image/png;base64,<%= data %>) no-repeat; } sprockets-4.2.1/test/fixtures/asset/stub/000077500000000000000000000000001447572140400204675ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/stub/application.js000066400000000000000000000000751447572140400233320ustar00rootroot00000000000000//= require ./jquery //= require ./foo //= stub ./frameworks sprockets-4.2.1/test/fixtures/asset/stub/foo.js000066400000000000000000000000161447572140400216050ustar00rootroot00000000000000var Foo = {}; sprockets-4.2.1/test/fixtures/asset/stub/frameworks.js000066400000000000000000000000551447572140400232050ustar00rootroot00000000000000//= require ./jquery //= require ./jquery-ui sprockets-4.2.1/test/fixtures/asset/stub/jquery-ui.js000066400000000000000000000000511447572140400227530ustar00rootroot00000000000000//= require ./jquery var jQuery.UI = {}; sprockets-4.2.1/test/fixtures/asset/stub/jquery.js000066400000000000000000000000211447572140400223350ustar00rootroot00000000000000var jQuery = {}; sprockets-4.2.1/test/fixtures/asset/stub/skip_jquery.js000066400000000000000000000000521447572140400233670ustar00rootroot00000000000000//= require ./jquery-ui //= stub ./jquery sprockets-4.2.1/test/fixtures/asset/tree/000077500000000000000000000000001447572140400204515ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/all/000077500000000000000000000000001447572140400212215ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/all/README.md000066400000000000000000000000071447572140400224750ustar00rootroot00000000000000# Tree sprockets-4.2.1/test/fixtures/asset/tree/all/b.css000066400000000000000000000000401447572140400221460ustar00rootroot00000000000000/* b.css */ b { display: none } sprockets-4.2.1/test/fixtures/asset/tree/all/b.js.erb000066400000000000000000000000201447572140400225370ustar00rootroot00000000000000ok("b.js.erb"); sprockets-4.2.1/test/fixtures/asset/tree/all/b/000077500000000000000000000000001447572140400214425ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/all/b/c.js000066400000000000000000000000161447572140400222170ustar00rootroot00000000000000ok("b/c.js"); sprockets-4.2.1/test/fixtures/asset/tree/all/b/c/000077500000000000000000000000001447572140400216645ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/all/b/c/d.js000066400000000000000000000000411447572140400224400ustar00rootroot00000000000000// =require ../c ok("b/c/d.js"); sprockets-4.2.1/test/fixtures/asset/tree/all/b/c/e.js000066400000000000000000000000201447572140400224360ustar00rootroot00000000000000ok("b/c/e.js"); sprockets-4.2.1/test/fixtures/asset/tree/all/d/000077500000000000000000000000001447572140400214445ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/all/d/c.coffee000066400000000000000000000000201447572140400230270ustar00rootroot00000000000000ok "d/c.coffee" sprockets-4.2.1/test/fixtures/asset/tree/all/d/e.js000066400000000000000000000000161447572140400222230ustar00rootroot00000000000000ok("d/e.js"); sprockets-4.2.1/test/fixtures/asset/tree/all_with_require.js000066400000000000000000000002071447572140400243450ustar00rootroot00000000000000// =require ./all/b // =require ./all/b/c // =require ./all/b/c/d // =require ./all/b/c/e // =require ./all/d/c // =require ./all/d/e sprockets-4.2.1/test/fixtures/asset/tree/all_with_require_directory.js000066400000000000000000000000341447572140400264270ustar00rootroot00000000000000// =require_directory ./all sprockets-4.2.1/test/fixtures/asset/tree/all_with_require_tree.js000066400000000000000000000000301447572140400253560ustar00rootroot00000000000000// =require_tree ./all sprockets-4.2.1/test/fixtures/asset/tree/alpha/000077500000000000000000000000001447572140400215365ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/alpha/a.js000066400000000000000000000000301447572140400223050ustar00rootroot00000000000000//= require ./b var a; sprockets-4.2.1/test/fixtures/asset/tree/alpha/b.js000066400000000000000000000000271447572140400223140ustar00rootroot00000000000000//= require ./c var b; sprockets-4.2.1/test/fixtures/asset/tree/alpha/c.js000066400000000000000000000000071447572140400223130ustar00rootroot00000000000000var c; sprockets-4.2.1/test/fixtures/asset/tree/directory/000077500000000000000000000000001447572140400224555ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/directory/application.js000066400000000000000000000000411447572140400253110ustar00rootroot00000000000000//= require_directory . var App; sprockets-4.2.1/test/fixtures/asset/tree/directory/bar.js000066400000000000000000000000111447572140400235470ustar00rootroot00000000000000var Bar; sprockets-4.2.1/test/fixtures/asset/tree/directory/foo.js000066400000000000000000000000111447572140400235660ustar00rootroot00000000000000var Foo; sprockets-4.2.1/test/fixtures/asset/tree/require_tree_alpha.js000066400000000000000000000000311447572140400246410ustar00rootroot00000000000000//= require_tree ./alpha sprockets-4.2.1/test/fixtures/asset/tree/tree/000077500000000000000000000000001447572140400214105ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/tree/application.js000066400000000000000000000000341447572140400242460ustar00rootroot00000000000000//= require_tree . var App; sprockets-4.2.1/test/fixtures/asset/tree/tree/bar.js000066400000000000000000000000111447572140400225020ustar00rootroot00000000000000var Bar; sprockets-4.2.1/test/fixtures/asset/tree/tree/foo.js000066400000000000000000000000111447572140400225210ustar00rootroot00000000000000var Foo; sprockets-4.2.1/test/fixtures/asset/tree/with_logical_path/000077500000000000000000000000001447572140400241325ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/with_logical_path/a/000077500000000000000000000000001447572140400243525ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/with_logical_path/a/a.js000066400000000000000000000000041447572140400251220ustar00rootroot00000000000000a() sprockets-4.2.1/test/fixtures/asset/tree/with_logical_path/require_tree_with_logical_path.js000066400000000000000000000000231447572140400327170ustar00rootroot00000000000000// =require_tree a sprockets-4.2.1/test/fixtures/asset/tree/with_logical_path/require_tree_with_nonexistent_path.js000066400000000000000000000000331447572140400336640ustar00rootroot00000000000000//= require_tree ./missing sprockets-4.2.1/test/fixtures/asset/tree/without_argument/000077500000000000000000000000001447572140400240565ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/asset/tree/without_argument/a.js000066400000000000000000000000051447572140400246270ustar00rootroot00000000000000a(); sprockets-4.2.1/test/fixtures/asset/tree/without_argument/b.js000066400000000000000000000000051447572140400246300ustar00rootroot00000000000000b(); sprockets-4.2.1/test/fixtures/asset/tree/without_argument/require_tree_without_argument.js000066400000000000000000000000211447572140400325650ustar00rootroot00000000000000// =require_tree sprockets-4.2.1/test/fixtures/asset/two.css000066400000000000000000000000101447572140400210240ustar00rootroot00000000000000.two {} sprockets-4.2.1/test/fixtures/asset/unicode.js000066400000000000000000000000101447572140400214650ustar00rootroot00000000000000"☃"; sprockets-4.2.1/test/fixtures/asset/unknown_directives.js000066400000000000000000000001271447572140400237700ustar00rootroot00000000000000//= require project // // = Foo // // == Examples // // Foo.bar() // => "baz" var Foo; sprockets-4.2.1/test/fixtures/asset/unknownexts.min.js000066400000000000000000000000651447572140400232360ustar00rootroot00000000000000// =require "users.js" // =require "jquery.tmpl.min" sprockets-4.2.1/test/fixtures/asset/users.js.erb000066400000000000000000000000541447572140400217570ustar00rootroot00000000000000var Users = { find: function(id) { } }; sprockets-4.2.1/test/fixtures/compass/000077500000000000000000000000001447572140400200405ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/_compass.scss000066400000000000000000000000301447572140400225320ustar00rootroot00000000000000@import "compass/css3"; sprockets-4.2.1/test/fixtures/compass/compass/000077500000000000000000000000001447572140400215055ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/compass/css3.scss000066400000000000000000000002231447572140400232520ustar00rootroot00000000000000@mixin box-sizing($bs) { $bs: unquote($bs); @include experimental(box-sizing, $bs, -moz, -webkit, not -o, -ms, not -khtml, official ); } sprockets-4.2.1/test/fixtures/compass/foo.css000066400000000000000000000000001447572140400213230ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/foo.js000066400000000000000000000000001447572140400211470ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/foo.mov000066400000000000000000000000001447572140400213340ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/foo.mp3000066400000000000000000000000001447572140400212320ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/foo.svg000066400000000000000000000000001447572140400213320ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/foo.woff000066400000000000000000000000001447572140400214740ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/compass/foo.woff2000066400000000000000000000000001447572140400215560ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/context/000077500000000000000000000000001447572140400200575ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/context/POW.png000066400000000000000000001236451447572140400212450ustar00rootroot00000000000000PNG  IHDR,K9tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp pmPLTEjjjlll ޼pppzzy}}}uuuܹddcى􁁁+++[[[SSSKKKBBB333<<<$$$rrr뤤렠nnnԘ ǀ||{xxwppnnnl___rrptttlljggfOOOWWWHHG777///(((g-IIDATx콋[X>@IQ0Y5QT6rDDZѢ>ޟyj=_>vi:3׬^{kmuM/#_/Rͯj~巖_oef~=jK7#(sK7՜EEEMj" K'DUEZTt$A<'j4:pɊ(x~G~ 3y;m :~W\RA7t{ ÐYT0*zV aTiNFw wHoF%ڭ.u^P]P3ڳa TA7Tc5#"x&ͿپKLST>² T=T,ҽ*i hF&>ͯl$+2'_k¦Pdx&)e ,5Z-20$uQ8,!L|tg+~U#0_A{IDނdl]uvFk2߽?(jʜzT_nײمE'uK5烘L&߷eK?FBO"fW0[[Cd .X4 bɐKG-!c3'dN>׫bMS,1י7^:)aYSPS}TC^Qc*tuZyrA e{xEm2„#F.0Sa穊ȵ]5! hॲxej2,i"V{xj&Y^ga@pUm , b,f٣op'p7:_k(xnk|X"0e>Jj9+30ґ׾^f.=s[YsIT{><1Xͨt6ܻ}:'.&Go:fSci80b/( |`Օᘷ!V$U^i>3vڕw&߶gbkS9#OU | jՠSuz66hß6_w) W媩HZDX0$IUv2 GdC'=6F9Fe zxpVbLJ Q8um>8pvNk؂-skͪ돈,"oƘ Jeq|d߲ {F9Z{T=Ea] ' UiNM.O_xY*6C(ӄj"~b>UᙵTUc0=! 2\or>geP*(ź (kk)7)(`6:cWodqb7'ۦEmܝP/d/V͖@qoC;W 2T,Trļ ZK=A ?b|=Lfnb č|x!ف|ܰ ߣ!)·l^'Cn_7 Ѓ ? s?[C@^V:®nX\f؆;>(}U $o|-Fz#FpGی4›։BS[og!؆86?|H9w z5&MxVU`G8$]sUЄЯv[ HG{1;WdR`j"WR8,坦@oz8s x@RT,ZX)˲^BQESTb86|'hM?OmcWOsUѝkwlogq` %8!DQ*# C @0!R V T0 ژ @w}3];\\arsUӬB;{pNp`G]o)1w my[~{=ҹ`Bܶ=C|sw:蝋udٺڝRtYˮ݅iZnơ-by̐Vhz0vfp}uU="WY߀ޒij2uגiWX'C zk孾>oy;O/3@;.ț o7t$ CZRS'\@|3+y瀧gTO>vZ~Z}Li/23 "E4›G o: ?0K`oHc*k鱚r,=Bi*6R+9 D\*$eD1W]m঻XbY t|OyG/Vv u|%3<e![7/Lu sA8bne6Ab0ͲaD4gYjy xsȲ3U?2۟p@B/a"ץj v̈́Pg># :g7г<9&B[LO ?t>;zwEH& ӑ-3]# |D `HT$]Hj 'fQ8m [mli9}+t݉kJ' Y[>gl"q o/fX' (vmض6;ml~ۮS*sk<#X$MBL xEXm'wQTښ1{.#shl9:B7*cӴ{v-g̟Lx-m@hۀ́0aŸ1έ-tfvvFIF8i6Ge4^X3:kb51?Y5do5HU3<3 V3ƖE1`t<'@ Ba|0S&$Vba0=.]>/\tu)V\OKׅF%ݛ|pmaR4V]Iš!Mŋ T=MbqLlwŰpfQgcv&ۼ!&0%¥e4${ՠE W.i[Dhg!1G;Jw ^%El ,oj(&*Ĺ!imwv47 f›%:z6%`;6yJۯޯ>`uW%tPhLFph<^|FIpDROUTtI\/Q h٤TOhb=nZ6aؑ f`*Ithq:Fs~$D[~{e$J&OwW*og:)6pחs}(_XGܗD0qosڶP9%w_ vsN8gݞ[ߒ϶F]3K}3s 1ITVWDPq syݞz ۠pن@zpǁl$p;뺳U[;|Slr%ݼuIp`MF8 @xFh\O`Tmr_=t/[ {D`{FzJ}cʯ%gV_[a:Ge߀LoVWQ`0Iϱ~/T6zI@ 89acGwwΤgEz=p̾A}P?2Xz6k'Fvj|8*sO}%xKa mD>*2l8nx|h[zȷ{x픧fY~m^ϱ{;A(=p x6Z@ۡB-<*h=SU HST9{l\*PEƯo1n/cE0Ÿ"r~]Ax;vmB[+x!xt7k{Å¥m`!W(E(vglmwbvuwk i8?o?`jA'1"v77]_?{7CH |v ZUfܹ϶yOϗ! *qhg4mWw[/8Ȅʇa }ݴai/H,xnXf_ٸ %Ɩ-loApwvmK=[?g "" Lr[REҨ2u=^FVxde]4B9hG!77}G ]uv*kM|(Y^Eވ*MFnr06i#b@o,g=sø{/ 7~ܠAck6ModHAD$U:4MߊĞU, 0\P9u ťer0x̲, ozXvZ#n?._6aX>ZN/Ev?HWi?$;75uxvvX6Cc=pv{)ve=se}=hTE7RUHD:6Cu_CJ!w5zRgx&=cA=!7}~|uٳ~R!UE$֢6&]f `ai:֜_I߇mvέpMD3[Eo)Daw`400[N7B]U8a ^0,D57j l~[Yݮr|9!Bs&3cL@n6VAehnBRa4xk<ڙ@HϻԄ i#?Qiz q{<Ʊ~a^F aBE'{U&ZjֹBƘ"8xj of@=nu'̄tIUb+oSOQqYlb:J8}!h)"HkDCJv0Z%a/U @5ׄ7yu~ o^vK$qQ􄢉 TCMXVz  M(dR7Gq)kTf-fvJcR= ]mv?b)agb8c@'ԊM9Eoe:-g˾A7O|ecvq`*]3OnSWܰ\G! >mGbw(=lF흧۴-z꽟{Q(e%,Ih-*Q XO#93䂌"t56 _x@`!|vx|w'omm葥jIxqZdQsxd4M x=;WڼL܆D/ƳмwIfUx ekHxyfT6AO~'b!ȫۄ2,@+2zь͞A/M(F&|W?hosaQaMx3f H}NMgs@x3eDxs1]z 9<({,}Y {WE-0Ҹ;m#a R; ͆Fгio66m4/-[ 0MaO $aj:0Ȱ GS\pT5j -J [\'+1&6(P?{]qZ .tUE͌zL*ãSa[_}}FrVQ@ v (l&2H6&4 Ꮻ<'U y6Z 1m숻SĒIMKd):j+$!K0hƅyޜ B(VnNQ8Q5NSJ7ϞĪ0^"jQ T&J*@Hsf$NLȏ7Kd #'.jRĒ7L,eMx3H73Ɛ>ah 3(wT^ &h:$ Ah" g 8(j и o~@*g KuRb/?M8 b,2x( `U3!ܟ9|7K@wY3)9 gMTj x YuGPc#RZMVPh(&4ȿ,h//7D>߼@on7m`n1lm]gtN;ڌĶ{s4:r>M`ݦn8BBX;]kz 9YՑsƉ4i@fD/qB#D ?A5q~2J?hC֗Oӊpqt-a; ,4ÒA r 4f|hװN:@͜CxOo3]%FO O5FǛ+s3W"5f/B=l4sUtИ;A/0HO AhB[p4AEgeD;sP97Tn#(+oTx JS'qHvGص(?6iP]KQg8cJ+1jb[5N"eCpҕu*x,UT?JMvov}!zP}*v(|Ç+34`8##=bU0Ûb^5/&T2XV 8LQ QA8µ}x60Xm|?en% Wr5$Ti\?(9~P5tRp(s|}#)pBh8Ni;onhV._.`jpƘzӤ8lll.TΓǏyoL%dj, EB`iEx8}7(ik|ѵh{ݶ#~&㏲naOS}={y6xK"xEZTi>[&3&X*G wtf9 _$fF1h07!;u u2A|WSjo n›{%JIX/oKƽ-] ؍;[ێBg4Bn `;:[VVϷ熆:SSD-9lxi6Bf)ā=#@،1DY#Gs尭=ok裂$pXRWNM ѳ]{sp>%aN%P!FہM Y ۴9s~ar RR$P'+Ffmޠ;5Y t k;Zp₩!2(,@PT]W^2gVA@V-p"D'R۔ȑzi"T3/D^K"StWE"|A r㷓|nR VQ$o9;23C2;TRfd)!qn J&&Ʒx Paq.K3)/[o`[xYSq\U QV#4qԿ$Cs,~&|J£OKQS/.``)jf¹; xXYֻFQ!E1Yj#&eadȔȧɬxv潭-˂9߯z'p!J*9 v\ʶ.t9#6EJq{uGclИL4|.ʽu(L%;"Ř ,p:رjr2]KTqhs"3WpzLL? !pQhogXԩ#_#зP.OωT֪|O#ޜJ}zcOyj8׍}T{uA%xXj1t za|yIAl8'AY+Dt֞_|NRzbU%Y/Sk5»~MbkTx7ڞijqD,KT'amk%9b8Mn}Ѱ146gr@9YfagނR_Aߢ48amL$|}Xu8m,<.negfAp(rKدՉ'2s3@ުˁ̂HvԁU v4[XEGdIR9}P{ ö}k)u۳agล=q'i-Mz.ސD5#!iಆ/2(3uB՜M .OosOw!:os|xo#B =q~Xc/ DHN??Gk"f %#kWx#+)=kyWRmF3#X'mܺl76Aw]S*fl@Q 9.1ubHj%8dNgsniw+?i$~{KPEzM/tpN׌9CcNOw/'{gݲSIH\jyDg!z=dLco}˅8X*׈h8>CzLZLQ1ɝhLoooI o4XWVsJb\ 6\o)|}j%<?y1ȸ_37/P=IsߝBlԂqp櫟GTlvg]ࠫ~0_Ϳԫv#DDF /+NbŊ 'M[ŕ L5H~_Eu}9j$bv=e>琰i_cm@xs/#0ß7~3+Ҩds $qu=L\EՈ Z/ʅAwrϦ;Nm›#|}v.$T'-42oˏa\iLT=+2- Ͽ,,9S_}j`udRu}asw8ѴG2j96ڴhpCLvMfCU2KlNdj£R BX37Y?b›%_Є7@p}7]w7e<MU+ a4֪'~D&)C0k2!&n+sVZeտW^%skgoANM7MQN]cl>kqx4dj$gNQ1(ϾxIs`Mn9 xHC^cg-`›k;(OapcQ@f9h[B1]K.j_s kT=':Y]yhzXv2nFtcDy{d9%ivDǜt]tG^ޅF hxTExzY8?S  i>׏N46 z[T]@vlfBDE -jRЂ/@[WI,BKž8wh|\LwcSTu"u14bO47e23pcuA$5D2+otQ"X>T{Z](#y/dBw֊]N ]M`,u 6&___kaVLTLdq[u(X)εBNWߋR1ڗUd}B_Ӕ~N쾨+U!UUTؤͦJVFm8w` LΓ5+b9BtF곰ə'dq##UHldEL .KjV4V\Pȸ鮷@nQm5){8ji\\SULNg\~CI ;sNfO6|G̢-k#[Z|ypfT&SiG.2>9asI@I:ܐLfiEΕC0&fjQeE&wͺ ̩YbYwG NN1+{z q~ LYژj_|i vU3KXHt'%TL;;26#2",WXA@bN 9AXg*m3Tc\ O@yGvT˲XbaR#Z5xy kG̋_fʇ,]`/9o`ݓb'1kOca^>$n,ʻ䡈c^Y-ObV+J&z)y;`dOQ7y_CXVjYF*554QѸ-Vo^|5, rݤ5' nH.}C B !14Q k ;s̞Evp=\p@FyuZCR"kM{eKd7]m)&^dDF)*r˨/7F@dZ"S(hƠ ^c UwBzf=e: RcluZPrF,0"!?f[cK"C 15RGg 59xJ03` I_;"C a%v |Pg"Ef.vVly?ԓ`ޗ9J&W[HYDUƁL5{`gtYXxITcs6)ޤR3O=a4T6"fᡃVWOz#<*>0W:C"DQ8AMT׺Yod~;VfU.x}Yąz(.W4K?O59'4 :]88.9! 5ikx,RcK!!?bѓm، $_>\4`I]zk,>ۦpfV/+SR}WlB@g堏=ZtnjEY0Lw5nFkp Lg`!/"ZM5ē w }KaJ7Gotk2-޴s^|}l!j%^GԕG>X ,Y*24W_cOس).ezV ܼZ}S) q\-kl1|TUƞ]x\XUR0-rX S 岌'v;/ Ւ9Xa/ 6y(ijbu] T3٭C{Heq2)j'9i2 jH>xU -su&l cA+fdDhAI0 ly1iac`I=mgQh'ʭw"%.H:^.3n@P;zWͣɛH70w],۝^E<|sזᶨHsY1ų'hŗ?ϒU NPB->Ym A /7<5s:x5-[u Nxfg2l `{ęsނ 2S ,"' ,2w[u}=|o䬅f>"/Ջ3 [0r][OsUC '[ "+Dkŀ QL3|д¿Q"ZJaոY iÄjU[qX5vGFh82>dϋ+A}}ԻYrLG*|]8Gٕ +%O z!Hu_9t[d= T,hn?Iab_nɝ8>ZDNQ[1դ&u r'psW}%~}"z35:W1UCBE%y<3$mӤeXZ!džjBDMޒ ֑::xpSV,A('@N>iYLwdQT M>~]+aןV ?MIЄvwxk3ɲ H6hh C'vpbA<}U;B&^ہ~JJ:QMIG܊ ۞M?S3 S 9㑏et 9uI3hҟUM71 `y {mC70EMU)yȪyBOj߈$>0vsykHo<>7w`4m22UN4u߮Zs[:$< 7[Z膰W-82 !W,^I98WdCl>=º{v=o|C[.ྷ8}I9̈k4XdTSV[xT@\0jEaI,]`hq#w0E+}t AzpO^[:[FcDe:oQT#c! ӱX,QzPZxfmquv*Qoh$4"}}~Ot 9a:^$: X~ؾtLIVǔi: e,Z?_MtPDdg9FjtvZc?Jf qRoV'}l[gS2ͮө 'cidF$Tb^LYOgP_FHF5_;;~홗o|XS)x ߜ֍8>G~h g& ~)pRE蚠0;S 2D*YdD.y3_~Y|]?,\lg.fËo̖8Y 6PMCz*Z՛W/za Mdh̾k] 0hR.L۲Ju4<%Sy|R&kn=8^X ( jk;pk'rEV&ao87岄D l~InةgG!A e^:"W$5"N]w\3Ul\ĩZ`4uGl:_Q5 UPT6-2}>_tjA*Շ&'p{ RY,(|äCx!tfG-k5bO>bi~.(g$fFcki Ɂ$ Д̀2.XxzC͇|X!;4WBΐ1V.W/)b ]7hAkkxs'/2wlmxHyEfZi:W6=r:\b]}-p/K|PGt/x02 -k[TXlDM3fSA"@|``p(*zPIJm]})Y+vi{Szo9ѸFT9 䎅bec{Ƭ~M38]"*)ԵD0LrK( -G9_0͐_s&Q5fVc*\۾٠U*Uf䧻Yjl*fxjcWI5vr ,,z)-dz,'+Ps(mYd$6=Ȳ;,p/6$ZmԧT:/r wF7TMjDj/[L cuK+K2yx`@: ! <EL, yƲ8,zٛmAm++hOyX7 KEE]i̹*,ΰh;r<BÖAHwHqaUhK}_kvom ؠ+5,J"'_wm0&{猿93ڮ~ߪzުd@ I@}'ԇ %f,hiP'q&k䝋}41s6 /LyscV u73y4Dd<]ͪ^ut45_$ob^ lzQ@K/Dwj5||G˜Kh,Fa:%gJ-@Zn"ʻ4-]!x3гPrޢ߇Z)rHo{07w9[2oXWoYt?dBk^kl&Jy~ .~>+8e]q Q6>ۭwbYLfؗ\Q4ɕH61Mk7u9phsޡMUC5M#EB %Z+ĩ 3*l\pq8.&)TࡅC K#%"(lAQHrda6MisΆ- s_rㅸRJ,',_E34 \X'[4M}&o[^Z+ei& ×I? $WmͧtH9}1Qh72qK%=*9c[.P,O|\9 b!W MCI|TXګĿnd,XcF %<Θi⣏t@L 84+nhY*FS vw ؅Yo  ̳1SvXXӐ|n'lJ-~&nӳ)Xs z sU!'> 6ՂE#NW#v 6]q7M @SFIȕeگWCˍ&d4F3wi&wXn| /=§J4M}Jh|TS_=y` \EWNPm:BvRu=#|V2zY K2g~u_")adc=OIC6H,gq>bW-2Y=OyYa`AXB.`y r3`{X5W_͎f%8c'`!u+] e 囕T;>@Z]tznIx'IiQWEbPN;QzO0$1]_52:ſe&Az)kfCo6y>)7Jh˨xoӛE*~ؐZ/ei19Nq%Lwfx|9fx1h((ZEn9uBВgw?s()?cuiꒈ.KKv3HmRS4ЭjeIRgdH5KA-lrwYCz3~DkҒIK=s~LRһ )d7<Vtv20dztK~qB$*pNWEJ fL Φ& -/S*9} X):>TPQ*6F䎺nI^lH61iY!@_x%fC k\uoU|xh4 uߗIjql ?6MH:rx3LJΉNJP Xyo: gD=v,RP`N_m`P[jjUk3|yi[ﳩաWXMCA~Q;i_2np 6I ? <$〶= ~JXkGV tn (?mςh\3i40,z3fЛo|Ebry wN1r41KXrY^s& :NQϲ%GY>("v,km MC+%й՚fͰІct+C"y=+j: qǁpޤl_ЛICEpے\p`ޡ7 I&L"P*퀛fd4o#rJ؜DXz_*I4LƻyBiQ@Nw ϰB)vNX嗹6gi;%Fo\X,FFo+6UkS|^$[HS+]>GZ֡#Qn<.}U#1 յ,XwBkY:R*zZVEF ykfJ\qǺ42k?~4ZEׂ6k%T! 6oM4frTzqqמ*kiB@0FNZZ8r$CvSoR?nIj3ed:xdDV 9o%*T]^F͡(=~49k)[=#~Çs>fNGJi7l/j9Sx?p_!5,_q@&㱉FWub<&:-jY!Yhms;$wk!FKs7GirpónirXҮRE< fb[72W>Uo>hDzKEٰzsF]%;RRow!ab.{]E4Yq@uKjbIA\bciu"iN5H<<;b~`vDK'pE.`I&81lyQm!޽ DoSiZB̑"lÕﻦFR^t:d=.ǗD8Pt[865ih7P^d!PQcV3ڗ^4;WA媊&IKi_|m)>(?+?Ж64D\@BY"ы]SYF\QWkKH󔼯 L3ÍM=pF@vhQ_]!B#m oY&'?'_m[ov8~׶wG=q-ҟ,}4V$f^lim8a$7MjeIӰNI$%iI+<&3S8%xJi Z[rR\8`WB?C|o5~DhUZMf$AvLM{&߈48YWgX3-[j"H$u-c h{83@l)%daM9sxlƊ#De(|V`#G̸[ $r Tl8lxܘw~ ĠlW`Ko#m ygQ@!Hq"x&%Qcy/(?cٍ^9x8>7gՇ,b=ˆjn\3ATJ>|f#wH놜PԢn4oB QD?Mz=2o.>TKw{UM$XiFOԆpG '`+ɤDpIhXx_D }#kD%r(HhiɖhS1%Ngj,4ײd4uJ>Л76FL[ <83Wӂ& ^ =J. q&WVrP/dՐbauCaPXQ]_(_!Z r" As #~-c:B ^ӳ՞$!1{(fMA@=t Z⹩Q|3#7z5h 9n[٫c+sju,[WmOcEܪ .TYˤhkp"ug$uX#çٔX.iKwVG2RsРgHE7׎kP]UơZF"[67HEʓiRGDiR 5Id4ul)+B#{͉ةa|2 pN!yNo0KX`]IQOb*iAU C~5G&߽\8Ms6ۭ&Z`[;4g5{?$cN7zhb~bncb7u 񑦵LyIMQ԰wBDZzE b.y]* 0,#bEJĮG WJ$j;{ML~G$啵g:J$i ĹstXPh:kEśv8xewl{l;Ht^JȮ1ҫPO&n lx(R@9رa>9 uX=ƭUJ:!Y&GMJ({ zvYgG.i"+_mev@vcymmYVILowkӗdȫ% 7_ / ט& dӬ҃꾀n7c^Gz33~xhMD=AYg#$+oU6Ud4uzu[4TZڬ|PWYY$?ջ$FŸ2XT )27/Vh aaяe!F0—GxPE)ɝJ ;!dF57}5WҏDEfGpRi Üb€7\2\ZZe;]'-8\e2Iz9NfGz/L{2Et!NߘI:oKL3eD$.[|smn V:)v~E< Q֞=6H\|״x]4:8^`ch,G3%\4 II#oc\֘OOtjj?~OwZϯ5۱AiC#,%l5/d&&CWkRI3 '}m{oѳ7S4<_s WvBʴ?yڿXc6D^3zshaW-!`~hMwΕ-쇷hvtG%?ʻ1o'&XPo>74juCVU__8=kMU_􏲊R5ͻ}+'8i F <ݳk""BQ\e`*lh=h` ;[s`s&,iJ跸t璷e'GY,18֘ >8z$`ҹ/j s3\FC'9 8A#$3y_>`m&Ϫֶb&foeި0gy 4r&]57UZG; .{مwUXY *n5zy(H" ]d+]_Ci=]$9rCQu$bsKEuNf޹~O?ec9ЛtO&m<`t_-{/y OETOIA zA혧|f#D(]hc&zp/}^b{ s\C_sAMfŗdJgioZ+Cj 3ZAr*8]-A$_}ez)_/Bt"Evlħ=Ch';󷅼hW쯛q)?˫y}L; X7zuE]oZ*a>Rl$Y>KŖ^pvc}h.G1Qs1K<Kn` |ٸD<"6'P50So+Y~bci95_#v`sX`.Ȫk&|mx4X']&uTsx1"]o's C_yӒL\QߎQq_7 F͒ AHn)Vwe <r3vʏ!@y6tXXQ1N`4fC1enm`'[T OC Phš(ٗW##WA {> 3(˲EzӪ}WIM?Hnb' !p9yݍ⾧zQWپPHS& |$EۗVB87}Z;/QMs@c3҈髋nk0UlVV6#۞ƚz;^%ie>揅GMyo5 hc̺k: /2796 ZSkLcia+g>^8=;m3!O)i=5e.8 udn}y.'e7ڠLF] p+h 5Lcȝb өRI!:Ib[zyUJrﯚ&C4{y-M1qMlF3hDz8ٖs p l~pf)ϑgN;9-jc&9L08ۃ ^h-ka?}YC>@QNuCӱE액1r@EuZ85ڬz.`VmVv1񘋺h0z3`suZ!\y$+:g=һ[4ޙu^Az:tCݾr+1YzпWLDvo RjHr UAa[ æ\gbm*d&4`:n'Vi<.2BD3h1Z6>I5lhYЛs(=rܫe33A悅k۲–z*\V7Ul 5'0C]2qv nd_q-C Ov4F$ʼn_0u'n&A9 nvj .}$ {ЛMB1y &E!NuMMVTʙF ;x~+^ 80`CoNi^47q.0\9 Iz@\\eaWߵt 83:g6(;%6!-yBkpXH*Gސߠ"F2o>Gm'zݓRy4 yd(綰ZUd4YE,]$3;$>k0:Y*cWA@q_`1wOxG˜O#Pa=1 \m#p͛-̝ᛁr٨qojp~ӖS~M]/f7'|"Xm 6~6$|9crK7UZK-Iaͬc);U!ԡ7MH7 YAz37vDMBoZ`CoFS ꔾ%[ U+J_޹4FfGf IeI׽zϢ=se^`P F~vOrU3;u+}~ߝ&~W;9Rݞz"_ύ/>;j4SJvC?gMvyrܨ鶚>psG0 ڜWݕfml{AVI7#7cGۃA}pͱhfncM> lo6czcs;>pLxn"H|6,Fܪi.3Fyn x;u3[j_#LM伾X̺'& _sF%FB3X3^N}D͘#L8A9VSG.x49}| 5Zw8:\Ӕnx6Jڭi|QC*=`T r]7ymz3ڡ7( Ku"|tT[/ xjHoXhT[!f!nH@ ^rw=j7; " tIPTuiIa>c'Lw[6ARU-ٛyCEnwP,jzJڛmsEVe& ;rj\3gGgA?r7uif WMfsFU_qFZ3:4T]_Qq[3[Ztc9u{ș5:=cfh,k&X@u{uVUmӢc0|w]8MD'"hiK {ҍٰIӦ)< RvhgMk9v́` V30䞰 fmh$, @٦7y qHoބdf4]NIg6f`K k> ̥3 \~)Ѵ^l-` ؑ뼄 \"hA,B6*)- nA&1A U\RExr)%K I𝩨S~4uVƕ ٛ2|Gِi .>g16uf Xt` HH7پ#}>qic.OuD6ty! f=?0PLTͩ, #ۡ3~+g.^EVt"aܺ1+A BvD7#5n-謤i%˫貾 Fc{~y9\>\>"ɁxVd2 >1Cϭix=1|uJ+gGg|xrZZ^\xVi4Wdi?w CHh& `psgǹpLJsF%.0llC!7ِKP ܐ>zqJHpBnP񗋡^u;S +7![e fPE`t^K_C:p/vߐ[ѫ:~EP 3m%TO{nENzS%镝LA"V/k=y.u5 ` I1خ%?9 QԚyV󉚥pmL\6̫M*aTs [1Z\vÃñ9SP`}FflҎBd'UOࣵ/ 1R;AuS+H``/{JU2 psaq:ׄDIVy0Z5͖@2Y~,*amʃD#%<,Žm+*% 3L(_`}낡(f3e8KQFhHPRAk P Q8bu@a\@mQ3ru /=RU8e{ak奤DVX>ij,lak/[ƭbއ%ILZA3tµ-C2a쬐ꄦB> ~rJi S7i +v/ßoT44$f3#;Ja4 2dK5,` ;Lfb1_Z0OJTQ>)9LC~i LB11|< Uؔ(" :D칄Cm[ =Ic˖P4#RC됫%]MdM#nцEt#idmB;<3R@}K{C[5@f"`tw:6u#(~ u,ۿccnl`onh:#;VLm7$29c(*;v?)X${;c0"ѝ*so,"bN 2}H_1'IОWϟ44V$Q[)%i+=,/K$R'O Fd ILqM{B:W6Z .'vyӆJԥ/l*J^-2Ο6Dk]z^]ʉ ߏڽ:SՍԪ~nWtF7Q&sW|Wpwm7SCop}3R9 pM,ax˰wؠj0uju^MUoI'= e SsLUrRnU_$?Y__@ E'MɧK[Vrmv~X&ӏjöT<Ф. xj10`k..1MdTbd5h8!m4pe婰as3R]5r+T]AiiZzfTFʰH Cƫ>BMܰ3W(se;f"Z(E@D׻0N7u{d@-= kCCnTT]ֳ}3rV7eZ*EֆTvy,jqϻ]l 2}b(~.&)BK0izIsP5DR1%?4=]oHȢŦ i-W8^.ԁ9NMwKQS  QgR3OPq,׽k] j1[97L-fy&^a|TQF8NAk<dT=5c= pf ejY,bxʐ=`z)3$(qOӶVuzfO '/_2QMglXQ{)69eil{xm3JfNEk)'6 >rv6O |SN犨(I5%k sA&/OhYoWB:=T-nZꭾRd7y ҏL\ki`» Х6bLYtgpqҏ{l3A٪ 3j?`#:lG7/=|x]`.>\|_ێ^Z~YulO1-f W9燫̬ǜ9̼2`m8L|T+mO\I&pצ\%ӨC?mϮFx9Au蔢CR`W*yL׼8!+m`iMELݪo(mUe Wz^*m?ffw6j(7LPύ }nTc,ъh* 4JH/g$~4? kL$j5=:F`֥W7Cf HĕAkJ_8y;'m65Ngżq94[)7}l`=H[1;QHbHjj%jN,jav^5fsnhkKS%ZBH-o^Q?[Wz˿i664Jr֩.famz#EQQd4 ޜ'`jO2"֖nFoRSh+\_י!f4>7]p P&/+I-WB۾C߬/iX٣4F+XcMoDFz&CFqф[f4gh0RT-r:Fhv7!͟ ١7hh|(?EyjЛ:'9XFoNFЛ|r4ÈfI^]' Fxp_7.^{:U1"ڏl*CG27Fvw-< {9/"WrE}ĨccS޷ћŜwŘO -ŰCq'b E 7BEWjq xT%epoi6-КJA9rE]nfG6pA]%fS6M$\gԅeOܶF|:qkR:z>H(hQXUo9v2zX,.u{tlZ75F:δ$69IzPxY.5zSc420.қA'z 6 7gQr<#y 6omzGQp뜧a4w6"ʤdى;g _ o\5g4z&\A ίO߰uTY{k{nh C $y7t+BoR` ^\s /焖V|\ɴi,87W?ӛ FfP@ǜ%$G+eM9ɷMh2%2MH|?UdGraqWf'#z62yvŝcHż>uIb٬INU=^z+~O,7{k]x8N$( K53kUXuWG7\ۙ8kqƼGgJOC1cTA2q" @*htu5tGMoFMxf&7lI4MNWQz=Y֕hh;bŠ'zϪ5q 1Qab]b)'қoDsT4 y[TZE B;i%\JoJϨ >O ~Oz5`^Pg*u^u$)E"1,&13f ϲJ FP6 ?qdiTlҤKw2}fϩ\ћ<Ģs߁5q*X4${~Ko`3zSc4hcZ0 ?t8?\g5zseiMg;j7ۀ ,5v>\]47 12̐Ǵ_{ eIPk?kIg)~e{# ;9 &CزO緝i ' cu͘Q-}$Uʥ~i)y+7C.]FF_CrkwX%/=U|yEelmբia>~f.e@Z0}mkl4^ ϳ@M/bf/8dcu{YYIh˫]>B3:eFjD9r83ѽ>>oQFUz9\Z5z*Ju" ^&p t4s1;[ICi]cxڱ_G}.kRQ.D"dX<+h6;1xj2bԚpIsSM#N"HijD{'X+DO9^qglw|>^Yf2b<5F.7 r2A5vq+3?[1F3E|lfۿBi0͞^jʦ8 8x#y'o!qnRk@JS&{%8fkIJ &eiA:6Cn@rƶ~X]{P[~޻.{y6(}FTQ!30yfZg޹=7G7kφXγ5^3?V>o~N4(kX,N1%(rWgyHެ.ZJ$, sfnU?'`5_*튁a'ds]x6zV˶Kgk;u?pJ;Gp4L'{Y4U Mkve;P5%8lG`es&7a؏΅ {e|kŹİ ..| f |~G%&pMl4 j@g/y"H̚Dɕƥ-4Ꮝqp0Z_M(phRh[2(t'ij gcU$dnYnkX_]=2%E4k:{NId9:<%= environment.object_id %>; sprockets-4.2.1/test/fixtures/context/environment.js.erb000066400000000000000000000001231447572140400235240ustar00rootroot00000000000000//= require environment-dep <%= environment.class %>:<%= environment.object_id %>; sprockets-4.2.1/test/fixtures/context/foo.js000066400000000000000000000000171447572140400211760ustar00rootroot00000000000000var Foo = {}; sprockets-4.2.1/test/fixtures/context/helpers.css.erb000066400000000000000000000001651447572140400230040ustar00rootroot00000000000000.pow { background: url(data:image/png;base64,<%= datauri File.expand_path("../POW.png", __FILE__) %>) no-repeat; } sprockets-4.2.1/test/fixtures/context/properties.js.erb000066400000000000000000000003001447572140400233510ustar00rootroot00000000000000{ "filename" : "<%= filename %>", "__FILE__" : "<%= __FILE__ %>", "root_path" : "<%= root_path %>", "logical_path" : "<%= logical_path %>", "content_type" : "<%= content_type %>" }; sprockets-4.2.1/test/fixtures/context/properties.with.periods.js.erb000066400000000000000000000003001447572140400257670ustar00rootroot00000000000000{ "filename" : "<%= filename %>", "__FILE__" : "<%= __FILE__ %>", "root_path" : "<%= root_path %>", "logical_path" : "<%= logical_path %>", "content_type" : "<%= content_type %>" }; sprockets-4.2.1/test/fixtures/context/require_glob.js000066400000000000000000000000321447572140400230670ustar00rootroot00000000000000//= require_glob "fo*.js" sprockets-4.2.1/test/fixtures/context/resolve_content_type.js.erb000066400000000000000000000001231447572140400254320ustar00rootroot00000000000000<%= resolve("foo.js") %>; <%= resolve("foo", accept: "application/javascript") %>; sprockets-4.2.1/test/fixtures/context/sprite.css.embed000066400000000000000000000000611447572140400231470ustar00rootroot00000000000000.pow { background: url("POW.png") no-repeat; } sprockets-4.2.1/test/fixtures/context/sprite2.css000066400000000000000000000000611447572140400221560ustar00rootroot00000000000000.pow { background: url("POW.png") no-repeat; } sprockets-4.2.1/test/fixtures/context/svg-embed.css.erb000066400000000000000000000001111447572140400232020ustar00rootroot00000000000000.svg-embed { background: url(<%= asset_data_uri 'svg-embed.svg' %>); } sprockets-4.2.1/test/fixtures/context/svg-embed.svg000066400000000000000000000007421447572140400224540ustar00rootroot00000000000000 sprockets-4.2.1/test/fixtures/context/utf8.js.erb000066400000000000000000000000561447572140400220530ustar00rootroot00000000000000console.log("Snowman: <%= "\xe2\x98\x83" %>") sprockets-4.2.1/test/fixtures/default/000077500000000000000000000000001447572140400200175ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/+plus.js000066400000000000000000000000461447572140400214130ustar00rootroot00000000000000function plus(a, b) { return a + b; } sprockets-4.2.1/test/fixtures/default/README.md000066400000000000000000000000111447572140400212660ustar00rootroot00000000000000# README sprockets-4.2.1/test/fixtures/default/alias-index-link.js000066400000000000000000000000231447572140400235010ustar00rootroot00000000000000//= link coffee.js sprockets-4.2.1/test/fixtures/default/app/000077500000000000000000000000001447572140400205775ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/app/main.js000066400000000000000000000000231447572140400220540ustar00rootroot00000000000000//= require jquery sprockets-4.2.1/test/fixtures/default/application.coffee000066400000000000000000000001031447572140400234650ustar00rootroot00000000000000# My Application # =require "project.js" # =require qunit hello() sprockets-4.2.1/test/fixtures/default/blank.gif000066400000000000000000000000531447572140400215730ustar00rootroot00000000000000GIF89a!,D;sprockets-4.2.1/test/fixtures/default/blue_jpeg.jpeg000066400000000000000000000001241447572140400226170ustar00rootroot00000000000000PNG  IHDR(4PLTE;k| IDAT[c`b@OhIENDB`sprockets-4.2.1/test/fixtures/default/blue_jpg.jpg000066400000000000000000000001241447572140400223050ustar00rootroot00000000000000PNG  IHDR(4PLTE;k| IDAT[c`b@OhIENDB`sprockets-4.2.1/test/fixtures/default/bower/000077500000000000000000000000001447572140400211355ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/bower/bower.json000066400000000000000000000001631447572140400231460ustar00rootroot00000000000000{ "name": "bower", "version": "1.0.0", "main": "./main.js", "dependencies": { "jquery": "~1.7.2" } } sprockets-4.2.1/test/fixtures/default/bower/main.js000066400000000000000000000000131447572140400224110ustar00rootroot00000000000000var bower; sprockets-4.2.1/test/fixtures/default/coffee/000077500000000000000000000000001447572140400212465ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/coffee/foo.coffee000066400000000000000000000000161447572140400231770ustar00rootroot00000000000000foo = 'hello' sprockets-4.2.1/test/fixtures/default/coffee/index.js000066400000000000000000000000461447572140400227130ustar00rootroot00000000000000//= require ./foo return typeof foo; sprockets-4.2.1/test/fixtures/default/empty000066400000000000000000000000001447572140400210660ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/erb/000077500000000000000000000000001447572140400205675ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/erb/a.erb000066400000000000000000000000001447572140400214670ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/erb/b.txt.erb000066400000000000000000000000001447572140400223060ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/erb/c.js.erb000066400000000000000000000000001447572140400221040ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/erb/d.css.erb000066400000000000000000000000001447572140400222610ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/erb/e.html.erb000066400000000000000000000000001447572140400224360ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/erb/f.yml.erb000066400000000000000000000000001447572140400222740ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/explore-link.js000066400000000000000000000000311447572140400227600ustar00rootroot00000000000000//= link gallery-link.js sprockets-4.2.1/test/fixtures/default/express/000077500000000000000000000000001447572140400215105ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/express/index.js000066400000000000000000000000141447572140400231500ustar00rootroot00000000000000var express;sprockets-4.2.1/test/fixtures/default/express/package.json000066400000000000000000000000551447572140400237760ustar00rootroot00000000000000{ "name": "express", "version": "1.0.0" }sprockets-4.2.1/test/fixtures/default/favicon.ico000066400000000000000000000004761447572140400221470ustar00rootroot00000000000000(( sprockets-4.2.1/test/fixtures/default/future.es6000066400000000000000000000000341447572140400217450ustar00rootroot00000000000000const square = (n) => n * n sprockets-4.2.1/test/fixtures/default/gallery-link.js000066400000000000000000000000241447572140400227430ustar00rootroot00000000000000//= link gallery.js sprockets-4.2.1/test/fixtures/default/gallery.css.erb000066400000000000000000000000331447572140400227330ustar00rootroot00000000000000.gallery { color: red; } sprockets-4.2.1/test/fixtures/default/gallery.js000066400000000000000000000000221447572140400220060ustar00rootroot00000000000000var Gallery = {}; sprockets-4.2.1/test/fixtures/default/goodbye.jst.eco000066400000000000000000000000251447572140400227330ustar00rootroot00000000000000Goodbye <%= @name %> sprockets-4.2.1/test/fixtures/default/hello.jst.ejs000066400000000000000000000000231447572140400224170ustar00rootroot00000000000000hello: <%= name %> sprockets-4.2.1/test/fixtures/default/hello.txt000066400000000000000000000000141447572140400216560ustar00rootroot00000000000000Hello world sprockets-4.2.1/test/fixtures/default/homepage-links.html.erb000066400000000000000000000004231447572140400243560ustar00rootroot00000000000000

Hello, World!

sprockets-4.2.1/test/fixtures/default/interpolation.js000066400000000000000000000000421447572140400232400ustar00rootroot00000000000000var Interpolation = <%= 1 + 1 %>; sprockets-4.2.1/test/fixtures/default/jquery.tmpl.min.js000066400000000000000000000000141447572140400234240ustar00rootroot00000000000000var jQuery; sprockets-4.2.1/test/fixtures/default/logo.svg000066400000000000000000000001641447572140400215010ustar00rootroot00000000000000 sprockets-4.2.1/test/fixtures/default/manifest.js.yml000066400000000000000000000000371447572140400227630ustar00rootroot00000000000000require: - foo.js - bar.js sprockets-4.2.1/test/fixtures/default/menu/000077500000000000000000000000001447572140400207635ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/menu/menu.css000066400000000000000000000000111447572140400224310ustar00rootroot00000000000000.menu {} sprockets-4.2.1/test/fixtures/default/menu/menu.html000066400000000000000000000000161447572140400226120ustar00rootroot00000000000000 sprockets-4.2.1/test/fixtures/default/menu/menu.js000066400000000000000000000000201447572140400222550ustar00rootroot00000000000000$.fn.menu = {}; sprockets-4.2.1/test/fixtures/default/missing_absolute_depend_on.js000066400000000000000000000000461447572140400257370ustar00rootroot00000000000000//= depend_on /tmp/sprockets/notfound sprockets-4.2.1/test/fixtures/default/missing_depend_on.js000066400000000000000000000000271447572140400240400ustar00rootroot00000000000000//= depend_on notfound sprockets-4.2.1/test/fixtures/default/missing_require.js000066400000000000000000000000271447572140400235610ustar00rootroot00000000000000// =require "notfound" sprockets-4.2.1/test/fixtures/default/mobile-min/000077500000000000000000000000001447572140400220475ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/mobile-min/index.min.js000066400000000000000000000000071447572140400242730ustar00rootroot00000000000000var $; sprockets-4.2.1/test/fixtures/default/mobile/000077500000000000000000000000001447572140400212665ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/mobile/a.js000066400000000000000000000000071447572140400220410ustar00rootroot00000000000000var A; sprockets-4.2.1/test/fixtures/default/mobile/b.js000066400000000000000000000000071447572140400220420ustar00rootroot00000000000000var B; sprockets-4.2.1/test/fixtures/default/mobile/c.css000066400000000000000000000000061447572140400222160ustar00rootroot00000000000000.c {} sprockets-4.2.1/test/fixtures/default/mobile/d.css000066400000000000000000000000061447572140400222170ustar00rootroot00000000000000.d {} sprockets-4.2.1/test/fixtures/default/mobile/index.css000066400000000000000000000000321447572140400231020ustar00rootroot00000000000000/* *= require_tree . */ sprockets-4.2.1/test/fixtures/default/mobile/index.js000066400000000000000000000000231447572140400227260ustar00rootroot00000000000000//= require_tree . sprockets-4.2.1/test/fixtures/default/nokogiri-html.html.builder000066400000000000000000000001361447572140400251150ustar00rootroot00000000000000doc.html do doc.body do doc.span.bold do doc.text "Hello world" end end end sprockets-4.2.1/test/fixtures/default/nokogiri-xml.xml.builder000066400000000000000000000001651447572140400246070ustar00rootroot00000000000000xml.root do xml.products do xml.widget do xml.id_ "10" xml.name "Awesome widget" end end end sprockets-4.2.1/test/fixtures/default/noreturn.js000066400000000000000000000000101447572140400222200ustar00rootroot00000000000000var Foo;sprockets-4.2.1/test/fixtures/default/npm-nested/000077500000000000000000000000001447572140400220715ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/npm-nested/a/000077500000000000000000000000001447572140400223115ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/npm-nested/a/b/000077500000000000000000000000001447572140400225325ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/npm-nested/a/b/main.js000066400000000000000000000000201447572140400240040ustar00rootroot00000000000000var npm = true; sprockets-4.2.1/test/fixtures/default/npm-nested/a/b/package.json000066400000000000000000000000261447572140400250160ustar00rootroot00000000000000{"main": "./main.js"} sprockets-4.2.1/test/fixtures/default/npm/000077500000000000000000000000001447572140400206115ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/npm/main.js000066400000000000000000000000101447572140400220620ustar00rootroot00000000000000var npm;sprockets-4.2.1/test/fixtures/default/npm/package.json000066400000000000000000000002131447572140400230730ustar00rootroot00000000000000{ "name": "npm", "version": "1.0.0", "main": "./main.js", "style": "./style.css", "dependencies": { "jquery": "~1.7.2" } } sprockets-4.2.1/test/fixtures/default/npm/style.css000066400000000000000000000000071447572140400224600ustar00rootroot00000000000000body{} sprockets-4.2.1/test/fixtures/default/project.coffee.erb000066400000000000000000000000671447572140400234100ustar00rootroot00000000000000window.Project = class Project VERSION: <%= "1.0" %> sprockets-4.2.1/test/fixtures/default/qunit/000077500000000000000000000000001447572140400211575ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/qunit/bower.json000066400000000000000000000001251447572140400231660ustar00rootroot00000000000000{ "name": "qunit", "version": "1.0.0", "main": ["./qunit.js", "./qunit.css"] } sprockets-4.2.1/test/fixtures/default/qunit/qunit.css000066400000000000000000000000121447572140400230220ustar00rootroot00000000000000.qunit {} sprockets-4.2.1/test/fixtures/default/qunit/qunit.js000066400000000000000000000000131447572140400226470ustar00rootroot00000000000000var qunit; sprockets-4.2.1/test/fixtures/default/rails/000077500000000000000000000000001447572140400211315ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/rails/bower.json000066400000000000000000000000641447572140400231420ustar00rootroot00000000000000{ "name": "rails", "main": ["./rails.coffee"] } sprockets-4.2.1/test/fixtures/default/rails/rails.coffee000066400000000000000000000000131447572140400234060ustar00rootroot00000000000000Rails = {} sprockets-4.2.1/test/fixtures/default/rand.js.erb000066400000000000000000000000361447572140400220470ustar00rootroot00000000000000<% @dependencies << "rand" %> sprockets-4.2.1/test/fixtures/default/requirejs/000077500000000000000000000000001447572140400220305ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/requirejs/bower.json000066400000000000000000000001061447572140400240360ustar00rootroot00000000000000{ "name": "requirejs", "main": ["require.min.js", "require.js"] } sprockets-4.2.1/test/fixtures/default/requirejs/require.js000066400000000000000000000000301447572140400240330ustar00rootroot00000000000000function requirejs() {} sprockets-4.2.1/test/fixtures/default/schneems.js000066400000000000000000000001531447572140400221610ustar00rootroot00000000000000//= stub interpolation.js //= require ./noreturn.js //= require_self //= link logo.svg var dog = "cinco"; sprockets-4.2.1/test/fixtures/default/symlink000077700000000000000000000000001447572140400226152mobileustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/troll.png000066400000000000000000001707061447572140400216740ustar00rootroot00000000000000PNG  IHDRmgAMA|Q cHRMR@}y<sLl[,#ep2 p\5,3@Ȉ0⅄ qH"D"H%R ]H?r /a`g?&dcbc1G1>-(fKŪcMNl 6-Vbc۰ww8N gsbqոV\n7*xS >ƋEcs!8L"X| qa#D8K"LfrD}1%$]q,IdHr!ERIHUEC2Cv$*qe(#EbBSĔ#}*j@uQEFcL WfLL̐KYrU'eoȾ#yɱȝ[ɇgoo"?W0PQ**R0FCh4/@Hz* }>hXxFQ(((+*Pi,Z^YM]\ܪ|G CG%MeJ#UjjՋ/jjbjaupԯhhjidiոBSI]3UBMUUuNCHgT1b:YCH::tI,$ ^i=-`fD}~~[ : &  W6>4e6ӌ4MLRLjLn¦|fX3G3Y0`2J7wгio2ݲrJ*jUkkkum:NW<hvv[z; [9 PveG:nǏNN"N93Ӝ'.-lX8vs2\\JܴnnOuݹ'<=R=yzyrZxy{(DTKw//pY+U/?Bv{lBtBSgv=/qyuVpx.rDKRydKʔ|/~5Ujm#isA/S33?s04(K픽;{Z$<,HY<[!/*d| JVN]jj k<ԭ&]pzG76m}o7Eo*(\_8ossLhxڭl[|IJv?YT܎pe;v-/_U>+xW{TV!T-ܫl;55m~?w-%ޫk7<;{iCTCϬ.9hѾF&fY|EѿSu}:fzoTykw&tw?|8SټU_t} p.cnobw_KbE aL Kɐ̧$c3k(B??CaJ ߵzX3^OyG0~3Q< =3 6s~gEZ)'O(`K+bƜL/0cX[Z:dc=) ꝱͽ1_ҹٺ/h/z ?i3 pHYs..\d:tEXtSoftwarePaint.NET v3.5.100rIDATxw\eǟ3Ν;mKB  @H4E4)4( REQ0JAi6@ @Nsq}&M oGfgvfsBZhg[Zh!@B -Hh - $BZB Zh!@B - $BZB Zh!@B -Hh - $B Zh!@B -Hh - $BZB Zh - $BZB Zh!@B -Hh - ZϹs tN$X0p Bq$I$G~Vau&%`p\!Bɵl&L8Q9qbDH A &YLllr(QE6y )bccK)R )Q(9,bҤ%KL yzY5X؊ًC8/s6W}.l`bS&1<)(2YC vdK֥A EPHcHKDv51w,K lZsdgs4,d6ʳ4, CgKH&CSŔeI# +79Y~R%p$Ez ܎Y(#n#6Y}g]G? =@eO9F~D:Y\^|A <=)Prj<șRyHI.\Oƒ1#pJbG:W#e6e-QDI -Trx?||:~ 9 N&ѱN&Uk+E$?D!J15"D1tasI"9y9XLRdȐ E<Codo&c%Cc]; Vѷ#SpI2;?"I8Q"x?aňhSL/}L`5f#6ac6g{vElWvfcldeVfɮLa{as6dm&=XQXĻv. q [ӟ6)R$(K![%0wEy I,r$1$IIlɎ¹\_g%b6ҢiӢIpi0Ԩ0im7#]1<-Z842\f28q#q 5+E I$YRL)1s(듕Ú@$y)('+٢>:L)$) I L ʺlÞ\̵C<ś Pw9-Tr44PrGEKܦA:U{8IK~*4iQ_TFMjoҢE&r7g2"a&K&ilr(&qɒ$A,z(!A^9)t&SY@I㝦ؒ}8/s 7r? jƕՖcX)PpS&0qAy ^b:^~Ώ˹ 6s)?&~Ƀ×xyۤN!j_=:oݕoC0/A y$9 ˁw$HHbPɫ7T'U t+S"ہ1\1 M9-(j4ppi30 ҫ<\Ye<9 YY؆؍C8/p's p2q;۲}283ixCG®N>32r ~ζR&..MKF|nKiơؓ HV'K46% Rϒ@AD;K4O'3ɕGxGnֶ:ӔkF dH¬v :W\ƗٗY]9Rd'p>Ӹy7+b\,r3Y6(2gg6spiQ,RnkyE\vibpPIYI(D>佑,8NdM"(2$(I*%#vX#.Ia1"~}͆c(R$1 ؃W-N`u,[9?n[⊇y ٖ"Q1i>/ZԩkQ`СIFԚqA9LPA1+aP&-u*/)p(cc!ITv1؄9+xgy 2Ġ>u* 0x4^napGie]V:Q\Lge@ԥڹ#E^ߩ-uWos}?Cŗ#YGs QH`LZy. 1("yGdlI)"G"a(1<:axy !g\ʁLa=Ht(N>Vae]c21OTdH48ilVa3rr5fQ3h9rPkЊܠ;rug@C%G j8CVVX&SE - Aj "G DRFq,!@оȪ(R4!Oiʡ6٘;y!RK!p#vʎס nۜAD #Ĉ'M/lV>)~";0r$fooRFSBF'45x:>"V{@N=u40VGa5W񦀤Iͥ0ДoAlb1rD&G8dKS(!G4y=44ks fQc8PtJ?\S zή;j@yſyO/ASvaJw,3Яס Td6Q.bblU# q[(IR MY%24eL&M< x]j[ G =#.t =f|s!w1S@!݁hkxSěū^`%>\Zx6m 7~4AlU &_*)cǯ6f.$bkQDK$NuهKy! v콸l.g"&aV 4{.jd+r"YRɒ:BlkylH&M E^PJ* `mL@e a Û#I"Ȳ;q:xWx?GSܕרIbvU\]H pr {1+cnIRڕن㸞2էq::ojѠ*ڔLäЦ,.AVIQb.1j@MhӐ!YX(dX_Jz O$v/ lK F "6)R0  (bcJaƤ,qz9lj38\86_UdWx;S9ؔ)4HF>ُo#~kT\,Pu]lYs_ƑƦIx)2hJ:S9xYnQh1*,pdtUcHԢq''$ hfAUW\)0ɣ|AKjūMCtV%N.g%ePXI'IQ{VM*4%zfQ(呮4yi\ȉ'ؐ FIMVbcvfD\w+].|TN@aWaV$cy YP&I(Ybo.af>~*5黸tl΋OvG QFK`HSs(ϓ&#A7e0ICw00u%T{4ؑ'2$=MZ})6|͙ٝ@DNK~!NTXd2gLƱun)") S7pum]qtլ5b2'S CbD9 :Sl&#b#~ JDU,/Ju#GTs(G}O>'Q`'K"iLzQ9 ] tgjTAnO/:5Z0 Uez&ɶۥzMQ6~ą g;=k8g<+3l'ؓ8p7u"aLC^CC"|Y=`?k >\M_AMCT< (5\d<`!MZԁkQIƠM g'M4L X1ҋ<$Q>Ey 'y[H$x|I[.0 1f3 f629 2,dw- 2SSYVpDq 4_Cځz/ |M G,xhaMlQYvCGQ䇸L,Ag.ԛYPl!@vFa&er&5\`BnTX~۰K'LV–MځSTD?762qvH_ht;ҟbl~EgRx%HCM)W_ .M$L,iN\Րn4i(=u,./<5m"cVr fkY$4ccZr@:"y㼧0*1NҖ_d6} e'(etzKM#ƕi5=:yĂsiN SOMh!$3 qӜFE4-a)J/'#"BFFGpy($(E26oՒF0O dQl_i2W&*]j~! n #Wuu"KSj275:y?]081v Iג*\~("D Um4$ԃ&my\^"t6(%C,rXCQb&AHq)nE ;>)ەv&*C&q~= VݺPAJy{pF]9UbC:s˶L 3\%7|S9٘zkYc;Wzdb/ߋfs%4s4c}9r-IUi\%2P "K/iʌ#K$ ln ăKԜ I0b uF]X#ui[0'  ^p9]%TEF+8Jf=j8t]y#hR ʟ4.;M=jcCv\>F$"ly<+^3|5PPBX̧ Q(bl ]h艜(.:{7_2&AyEH1JPU:PazUANeO$M( ɍ->K !^oq+2qIIϗxGhMy.Ie:}E?n+%2P=")"ɺ?s3p,8a`P$cm~۴Oo٪ȓ=K,XaO"&661J(vt(z5w$tl.dk R( vB`f yIIaB!3_}+S؎U DiVzͣr@p`рWD$8>ڮ\F 7 ǎd*2e 92ۅ {A FA~s| ^'dxb(\4e[2za,ld)4S&f+uwĝ7e8mz'IHe;.eխuCJNlO[O: €xY84lDTrF2,剹88Qᥦˌ*[8a'PboI;QҤɰ+=9(c/ h$]QfU8B9uXN-mߣ++mD ~5$AGKKԡ.ss97J5"!e3A6-[#9(4q T4 /̆xSX$.k0 Ly\b+c4uy7=(J(6~zb%v|qtgo ޯ ߫|c75:Q˴iH9urj4kh'qvtvUqS(p.IE*.ƳPVFEgQjl%aVNt(aA &0C=z #2D;yWд~fHZr.צvpy8-L$FxZ[RA4 *2ؑ*p*CJҡp"qzE$,_N;xq21nmM, J%xZZ |9zw$v P AR]NBk1D6a[ cHjZ6s8₪a6%FIKQ "BedH;4iKGu84E .Ӈ8XH3<\- /hז(@Hs IPה 6#,%0 K!z=}/G;4 7ߦ܊9!,)zP| _$N^ m/~ _$)!96D=&7.7,BMaE]G._s4e1.ըL|űZ$SEz0EYvzVZ2.XyZ 7gA|3^k3kct6h3'PDq6JW~f3/ #wQ? K;8LcuҐݽQshaRerV_O>QļXwqq'ГDR cӺb g} byձ3P$vx<]moIk6idr 'PM0פ5:ؖLSg eR"BCo=HgFkj\w5iئ}_niH0d(Ģ @E"QzIl3z:NRg502?0C (L1(N^ h4?HR .].A7۔&(ɣΜo=\)De-LK:"D dwEKpo(g=4,AIg_1#OI  5B LNPOuiP] C܄{6p5lA`pȊ#)K{`3 2`cbQXe(a&%3Г> 5ţ~^~ A8gOl9rɵ+w@Gpԩ0CC?V뤼!G蒤p4 &2@SWf3_ ]1y'ϛ 1V'E9e\fSm{9xd7D?aljh8Fbt9fZM:'ͅLy:<4 %opͣ\+cp=9TXi=^o?Y Yd) N,Y1^R:kiksO[K84g}knJ̧$; vO1QӒ%:u`MxQh'2YYdܕɕ*l@k¤IC"JXIʘ([1BaoIctwXLrkR%TIˬ]^l*ZWJBaa$a%sp|}"$G;=f #,8<6֛.,`> YDxw E/Y3%n8z4%rF6V&&!22U;2b: +&Xx&YraaE,de>&OsR%`kH,Pnn&m V?60M2i20Y Q6zزa; 95\I,;^|ϫ=EQwa#U o\VdQiXɓ&E 6yRdcRbOc!I8Dc*C_u@UkypüNc(z!JyG ELJ0(ba_ >ChbvUS+iiѷvWcU]\yaey,@FY.W1S@޲~lZ},}h-L r䈑dGnG,dc_0P&EEI# K8{x~umd.Y@DZ-5U7 >Cr`@XtYzE[Eɧ8I(b$M>zHQN<qL3S(a^fS E 48ֲ+þMY"I ,a •|=dIY~i}Sd_p[Zٶ\|.i-kCsQ)bbEL01ɐ ,Y ~FeEH n[2BWVS %>o#@#G"jKg@_k*55[CsC+k-r @,İV:1 ؒSXcϰ衇<(&peka$+jq-!Ipy⤹FJЋZylޏ('J/=ȓLlQ-GĵLQܦ11q/CZWv>Oq7+Ξl6|8;^XnYU !u@qrHU~%O/) İ0IcQIïdXipdds4YHNDMz"Ay89"̰|n H)",IH.L^E ơAeT?+F0D6/D`2r.7)5#X ܇bY!TiUc/bId1u4 "GIԷ ,(#}3ybqɮ-iXuY< 2eؘؤED$}J3DɓTkxLEH IIӋbtH5zI[/mЃĤ(^$ȹ!O`\~6E[/QG{|1VD[7Lf3j ;W힉e)"@CIgIT$AwoEα([:k(%E^I Nna|fr#G^,%ֶ)%QmjCҒQ)xddX &)luqMez6}EY;Z *:;E N,!2ց p>?O$P.-'Rx ؒ5Y$zYMؗ#n/3<γKl!/Q$G/yrIcxRzrA,Xth)(g(P"FQ6/q: 9cJ/5I#I>2(Na2ܫH1؎B"d(:>,U+R@Q86C=oHWgkI=_ؒ9d0)0,$"wq#NXNfe ,Ib;%N_ Cd7u=@b$ȓ' {Ɂma~Ɂٔ[B1 l>|ky.?AaR"M)VU]pa#[ʲ>]`:ow :Q5I5ә,(ZcAxgy[8 ER,y,Y1D gGH#I %:L6;q<ƭ|Cʞ|y&6 )% 9'BRh.cipYRfJH2YېvvZtӗ_؅ŕiN* QpZy\~w).[ҕb`hߵmJo&w%K_]LW<9$JĈd)h ԲKғݑL>10?L%5O261HIa|%[C"K-Gh$ow8c8xיUq(YrH&}GUVH˯բMe9LuZy7 @ E.ڎCB|?PS&/˔Kτ^;r yDaCGMʑ!! j++'As 1PY~6Dؔ: JzdyLF<@w)%1myyb.:~< 2'8H)(>ֻkSXäW[6_m]s Ȣd(ي89s9_N>&-_@acSvỌERr /7uÖA[$(bZ&']* 3yG?BJ y2BIJS&08;~O@ )/`51QGS<VY8ʼiI0^ $# rH1Z#El?ckf5L—#N."sso7Y$IC/QiX0E-%q 'C^dCgH9GdD %ȋ@9r/ә%[Gjގn4ux1gxA:EIK #2%n΢>8>hQ/7AqnUέ|c87tsJ1P ĠH u)*Mb(l,fPLl Œ^I$ҤHOE9 C(~9ْF$Hke|"AFsl͢d0(a 6qıIk%S~, %Il#h3W(RX$40%)S¦" d؂8Gsġ0ÛcιLe"ibDH"&e=gfdbFm +<pߚ# Պ )s8"A@3)}-0oBMRQźQ#4Ĥ`U\rȞ(VlYdQ(ҕcu^': Al[52]rF9RˁzNbW6~pD[gH8M>2HdcQRtyMavDOEUF_Is$G؀8[xt!tu#W7*q:Q"&s4Rbe(Vª?u'YU]Nʡ$2"DPT$DL$AEY+b@F *DAED  C0ݕ߳fh~/0]uk KxӠc2|G˞F4h}Ԣ)@à},q+[hs:[`& j;aȬ4wcQ$Mx+/huԤb>!&XVu]h& ۧhӸ7S*AgK{WI/88=ٞMY F =#׏!g-ÿ(@i>?yB^v'p-0s<AV"Omlڝ}o‘|smI)۴У!EzaHjH!κ|i7hq:u5ʓtБ}Quőe@#~jIl y-7q3M*QOOsXb9Ȓ@ysb[t:Cؼ});˥z"kEc1n2;|Cx[!#ړ;M}!ҕH6EbK-QHL<%nZ* lCJyFQ Cl$')OӰWu؏3ItwbL7{d(C6 gc]9,eo\u0=%|nJ8#}Ms?r粐6gpq=~%f=mZjCӮIGNZu%ҌW!~eg,EME%TN0 X+R%AyAh/J4 j}?/[KN&y۸s"d[&,\SDL$Oe2 (mYlO49b!E49#!gX|$1ـ\Ju޶-V| Dk>3r5+rgj}gH@dͻ8uIs-BW1bx9Cy#Ȑ AV cc}|% E_0CN;Nv=th3 ,(ƐLJ"Z+UG_.!GY_˺걔6(`؂}e߅BZF1FcLvM\SlGEnl C<)*1!5ZCLVE0;2(D! Ĵ+8Lɪғa.kmNNkhN2 4EjTGiY"[bNqu. o))i9'J(QRl!Ӻl,۸@_Z1Vm9ٛ$Es#KB|dHSaW.`LF0MJ,LS-&E ˽HS_S66(6`.M*\r5 QȐp #ǟa vMB}IQ0Ɓ qJ1J! g db(RJ\)"Jd4@mq3'b)״'FA-Dtɗ99xY扬ᑢJ:<7e9YIm[L")1 R>$09s>`zRr]-f1l􁥮YVizN t{3,.d*H)dZY y$ISd i` _vZ-{`ɀ?/4+Z$1('cJCR J,!`\^xmLrR$I OT1HSƣFۖޯl-n~w[oɷmEq3,m1deMu'`0Lf/lB$o^]y];tyyjCv?cN2mqQS.}52ì@>77+|ӥ\.mƝ}pne>Q 2ŎQ%Gf!$MƵF vuZ" =TcrJ8O`E5jBӸP$*ɑ`ai=VƘ< )K|QrOaS kI.<::حq'k,ڎ\{̓/^y,%~g-v$ gBwoVRTW@ք{:1m#k&ۣ,J(P0QT1 ## EJudnL`3TJPe;#/E\1ܩڤׯirk&]'u`G %H9XVww ؍OM.7kK8S9X _?.Vsn e|2$ّJx/[*~Bm1BL:K9HRŰ3Dԟ϶^9~g$I!b  [ Hd7q;#Is%iҗfx-"'|x#ȓsbPfkvG`{MԝWTD?o-dL)Ĩ8  J |mQ( w Q2IQO,#eD޷3G7p=Ƶ'M㖎]^I"EHPUZLx(ky~ ,֬^dsڐV@b<FN~q" xVcV 1-邏cHDlГk8 Â)L򩐠(%D4 [Եg\;S:3Y>u1ҢָY#C~SMS\8X")x/6$wMq2[]*$_u ?Xm.Y+>@W}89].&Ja2dIS!/鲾ɋT"YzvpH 6N$eqZ].HNjhKv0 3Da{dZd@)`6K2rϫt!&wi'OV-< d!. N#ƐbՖ/ߐIO }q j׈ֲ.zoQ^Gb1e6\(&ͦg)74dnu[%H sHFOHF2cDC!9 ~`rF.w$*E# %6 r[2XyeՖ0'W TPq7HJmv-4Ӿa4elR[+A6I!/+_R 7cEOHG7-ڢ/w1NS [c)ʲ?[0ᶽ=-d'½jW,mDL06/{_ޮ(u ]v~-Hȼ'INY o^idb"2DݟQwÑ/KZ(pHKǮ n:{y;Q#|Mб7F x{yzؿ75qt8VhmƐՑ[vpX%[FZ?Ԝ|C >=[IP7M֌ ,-CQKQ])?+P'o'ΝtX SF ij]ԩӠxjȐɫ DE<$I3 -u/dabr_ѤMQ8 ˢX}vY(ښABqS[ۘng?  N8Sgs<' p)L:yUɮiu#4FR  ,{PQ M|܏wDmadghgT eh|mivpЇ%QnIܤ"Sa9^xj+ xܪCk1$0x\,sF>6U93ʐ*́vK1"E"e;r`X&?1ԈGF. )CP-.\V ބN`Ȓ>0 jN QgvU C]Nu )%x;8ę ˸̔(w5Y K|Y<mImBή+ox0rK%Y*yN|?jH;bG0ksxtFClߵgXj9|r 8@D 5AxgQ3A:Lj%pr+`x~ޱ7H?$b DKK}aUW=˜自Æ[91ĩPJRۖhL/uғdD?NI~9Q0G4_|nɁʚB"'!reǰңϳdag`= t9]ٙp,?29.i}gן̷& q~,q\Ȫ6rYy0H0:"\xsdW`OGk|fZ68zƮ& fN1';`w_cw}+YNK2 #%[bZߩ\\Jo;BL- FZ"FQ0<* cHp4`ڕ DVp{) 莌g7Ibh}R8s&οglE9gqcHʢ6G2y}1GHfJ H&A^әPt]|Ū@ELqӞhN'GSW@6!."/OOg;4qŘiogDLQ<\Y4C4 n.]~@ q2Tž2;@<ᶚ0d6Z޿R$L&<#|Y1Γ8V|y% dɝ1W!E<(U:HiODȋ dkGСR^V)0.E@M^G=8&q;-\!t 5 _|)#@q 45B3B6V |wf]bNN())`:C*L]w_73j_׈p,`GP"">p2E 7R3dLa< gV$,3F-'Ýnh'w Q(Me{#A %Mq^HmX 6jS\=6'9$ PǧL7ȁv6 $lQxJ )|j('GxAg~ :)Z E,Ismb&XJK{A08kY%wcQ.}LjՈ&1H0y;g ά$9䉰5񸩉(QY"fn57dbV@0EW$%1\s8scthАzGX uߴ6+ԿϔB2yMU!#|ב&@pϵMeIޜicDYr"qǐM~j̫M#B5#B 0\,Iۢ]2O wV@Sx},y|S:- =ؚ(YNp'MS;З:&BYYh K1``𘧌 > cx/h_@hxk9JB9RHSdaܡ ǜd̿ -B뷙7$ `[ 0$ZM8E7@O`+$7KB?6pn]/)SHl- c~j2m$!Qq U g^`6#s!C-°}]-w| Ѷ7N) ʊΐY/;-1T)(E*Y2 O=aHa#5`u6빌3"auECANe- |]>r ' ~%\ʯ)g]޼y'q(T~ǣ3$8e{-5ގ#N R:<-;/ af0-<V_bie>Vt5r>>KωQ&%כo%\)2Q9IJ{Qk%G -,P9_;Ƶ9< V@0>C.E|8g1`c.R"ǀ1Tɸ0C8Wzsb)֘ǧJ4 b.p=@Zlmmw9.dck9p"E79_Wnaa)uZ4x3!|yiso2I|$]ƙǗՖ6Q.QTY8VgXK$$rO!i ȍkE kI`osզHiP#0C䨒pf~9a(z|iҐ7zk [;q\ށ8-5(¸p_ABEd1$4)&Ky0lN8dW2xUStmUI}P&@smqz܋ljjC޸wo!aL4)6%|""2\" Y$u~G؊y;md6{=}T}{[Ww)8S',՜qd%Ok\^U"ɔ~_BzuM,f1ܩӉ:zX<]'Z+A%m:ܵ\I<IWupOOLy080rKiǩȸ!+qZrUd$zC{7)8ؑťDL݁GJV1va:~YFl6:cZ C2W G!s;f@W5W]}j-g{4ɥ!bT E*2%N*dTl8̷HB,eJĹIl s.#Xt(JT CMw@ &dHG~; jlý-1܆G,%rW&-qjԅْ REPc9X2iJ 'ʮ\>6%i!okc o(K ˧3˟):l!OUTC^[rM[(siQ!RvNzL#mCes~D9*I9M}eB(/LȗǑ{}gBC2GwQcώyIkaxh%*4YWr/#ƊoyZR*%ۆ$9f@kdVS 6c&\I,HsGR416#C 6Lm+ 7f'&C뤲 Cٱ=da>%2]ruu e 30-Q f5 * R"ugBf-}`ce t)ǚkG#=og7v>>U0JK\\ }c0( mJ徨D,>i eZH-RNԛ*5jWg ~`Q[=KaJ̡ FI^\I" e3ӓO")1' u4צ@ҁR3ӳ)L8S{"9K7?QR`EFDF͵hq_ r*jUT#Et tfȅwq0y 1vPqgɒ'GOB66>MD\N0GMZ0#l`ɼ8"4x02 sMSPvhӇr'/_^_޵9r2.ϐ GyuN'03֧rƲbyʢìȢ8(˝.g,m=CMϲĶ`g@0iE3t ;Ő"J).g:I1۝O$%ĴR&@1A{!ne@U xSc]~Θۡ4Y$hl.U b3wH|;<6Bmy }l޾npEZt~]u/mRx$'yiiAs3uMv'KYJ>iYm]RJ&G8gt0_OP!EQic?LL8_#禫:}gwj c7cX_4sV/`;$.}H:ZMm-ܿqty9$0D9MĀ v"A1"|z*0u=}H&}Y 䑧HMradYUMDQap= 'K*I2Lhޠ3-^.dCs;V)K, gv[Q&\u=-Gb ChR0Kg$|DNxihhQC"l3Z5sqJTS!>] ֌4G #p.Ӽ(,i r[#DVF>ȑƣJaz *@7}E1b~:䉑d '-<+#KLRb'k)} AzW8.AXբ|Cdp!]:la;M^-;,ek )9vK_e1];ڞQP`HS$IxB{؉ [ &,l< tR.CYwx-Qx5’&݃L7`$GȐ¥zCVO-C,PuPcm^śyG% 50c#ӁgO Ô(9';iPL)Kce7؂*9%z 4 u&#Rb 3!7Ijv8|jD۶u. M+h!*N,S k@.P OUʌ`8IEnƹBu4)<*}X`0]!A(;$2ۜyy-Zkْ8qx`z  na]WК)o/+ TIJ>qSWeLpC:9P/2YNudaR$:#C"T]d8b^3H_[]y;8͕Zs09A6Bd[12l!MI1D W z) 0B xVYOGmFRlbͦs[w-CɁ: nf8tM&!YEZs;dI6J2D5VȑJwA%wH}8vTu=݁Ұ,qǖ Y;,"kI~$pзKCT#gMA6)ή CO(5JٌK`>In󼁈6e%"V-Gr)^`/"$Y+ưƅ>ڴaI u:,$~e*aQ-~6%J-xVm=~|Jm)PcEjv&&s$%|"|KMLC 5ew qMHMQ"c&5T%4-`x w S ZSAFgC{Նne>,.5;1Z&h=Z[6P291.Ҡ-@"ԓt*lX)w&B 9*TI0 ݟKػm/P6"If͙Sz;RPXgJ6OyE'Ђ'J\"aŋ&=$npl߬r}S;1|d~Dx xQ=R: jN\jcLFHuyY[ѿeYO!"IF^P~ɒ&sy|G>)f:¬̫L5-)s WYB6YID~uU}mT,q[%cҡNOp>fq$3{âGi2LYag!-`ѧkQSf3.bG ̪@D8u1"Yf(Qhi8Iŭ8Of g5Ֆ0Ga(#%Ak;HpV@-X1ZZCR!CM+I>OXOv˄tK4ֺb>q#)w 5ʑq qBkŵ iWq6"GE`!pc G!+(nedbϏ:q?=8G`W7`M"}ƹS؅Ibd(* sF|TQ%!F8?*IL>;ׯҼH2ⰻ. wd`Bw-!Ű:[+Re- ]?&#)פ͓'ƶܯGfHUmRde|Ę^ܓp(,\lwUQ?ēY$F eJ= ٴƘ+["ӕ|EEr8!F!x IΪYw.{BNJ$Vw G@ah'f#bqŢrT&YecHg$wľ?V&tSA."k(:jE{;~[;yDR͜.fj8M< = =[*[G9ņ֣93ϓє!WL\hV9F$kt5KY휝i= 3[+$g) O¡mzZ&!AEeP A kI,'#6i )θ .$S?}.'R,R/Mny;hR\ W nd*W93i'XYao"em`M~M}Wm,aD5KC`5}9Za 3sas1$\ҧ*9ԞդRux1T8Y.-y0Sun3e?Zӈ{}SԆ@Q<:X!FBao9*.gʨG>>,p[N J 51K s講K Ɂ6+d-4VZk鸐CX! v]V!C49y ") Nr$WEO7SS/iƁ1TdrхQ. a0dOt(8M2$9?g}l%QKMĈ/KvXLXLB6,g ȏHS%0C4H o;{p%uD2OFY QVܣLD("-uWh!?\ 6n3mۤ&>]045Ոe"cYy,"dt i ` uB&?Pi//j5 qjig{{܆؉3QkMpiHЉkX2s0式؍3iХv Am[q(X=|]O(Ȋ/+c(vW&}SH NN5`.z>g!S1d*Sz!e$Qa2D)jz*`ldĮk ӑ^[uTELm Ȇ)HΛ'sh3}F(J H3n@SL!rc ba[U[Г-Ln 퇥CEhj " y8؝M!GDsAi}YE3NQfpxYiJ= TQ.! Kc2wdUOUnŮ$'  (PLE,YtEWȚTp AD$*yBu繧kf^e鮺=>q lős4E"QSa{JL(߮t *:qg_O}?'8+#F;,[tTY(1*G蔒sYDwFG(&'h][%^ȹ$ 6pG^Y/&}͜O0 z:0rqdJ.!׳ r_ LVטL[76)s.@rx|݅Ut鱂PPcW+:2wQZ|=e{,e{^[9{npН322.QvIMq6mي Rn]'Kf2"%塟kxw|12 ȐBO;s46{_95u ӳ.""U">qr PJqR2oVM!΍޾3n3H;V%ſw9Y*x 2<^Ңۈ"Ou&=:|H:>%+g0 %*b \Lrnyl كgq4-Ӝٜ|m嫜42`_`U*ĉCJʋ Knxew;p{nd[;/|+Pk1D@8/wI݁9ܣm|"ƩӲ. ޞ+8$N2p-+o$FI\v/cݘG亄R{S/'a]=BV|YӦ4;;vmG,Hڞ;S̨HB&E"^$1JW+V-zsHN4낳).:5$ٗRE\HT%b+tqub?G:cKs.&FITOokXz)UVG㻊s}~$fPDlG @@]uV6rHS' klE)jf jaY;c{Ͼʻ6Eƨc< ,` dk31m,A9Di1۽+0.cgnW5%cj[H^ÛE(Oǰ#S д][H DEƩ!:Dj;h9y7+7B@Ea#yyAr)n7 -`kxGe8OIa>.ޘ&<" [o.{&g@~^ Hƚ/HXhf׈X4dD +/\hЕ>˱“ b;DQWu<ps(9Rڙ?U$Gt:'8EK}. S9}9rT0ѡzj99uӬE9^% f$y/{O [*>mT\kWcާ31o; ѧCȃjwbm,AEO֕>*ȒÐc,Mr9QMIeJ% @*ES"#=xe"c[n1?t+K)Df9f]"g8Lâ&ikH^ p _&EJIc SIva]xr3ٍ'y4K1yO Kcg_$pTbWR}| m[S$ UZ;tKLmf/ 'UD7{~8bV';w͖HөP#KR5v]˔a1O|ⲣ4-6GL46mC& g ?6]ܴsD"udhX[Uo QU͜"Y{J-,Ż0XL<D==R8exJQN4kov&y Ez~3,rgmghIG.\@=YMȃ\XJFMxO._ejxp,C>'<6e'0"M@J,[s,_wt-wiiw/MwfD@aDWc³?Rg06fWy+R?QJ4i# E ,n'DDo]ȪQJ h:%QXEa HSBf j3@"狼E˷Q˼axckӡsDv~V'#lFyCԨWyG'[ udIaېH4* %=K'EHPa|KU4hu"pabf7I;sڇ6MڲFNKt&Ϗ8=#%U<59WRrr &}">$$ (f{UY N.(uXb5ZTm-qƨS8W9;tY?Ay@$KId (l3Td' ըHewZO;χ}Ve6'!yK{r؂88_pybMrΗpwwmrPu}\vTS߶N1jo_7Yg1qmf)jos[P.>BVK2uU# u(QSO8wIʱnBZ4UxSl(^X;︺y&EO%|RכE$PQ=dt2ܑ;8iIvV [Δsxx8(mRn.2:`@dsOѭk{o);˞Aq#nWyPkuWhRRk@,'l~V>vL^މJФH/ %G`UPίVJ"Us^hek{u$BFn4UFiG.cNm:kGo0+)AL, bx ЙC4e2,U!–3Cŭ*1zfXĀ k3g1 %d=N$U`K9ã>7UUoWS d1ƨQ!KKOiӯGFRy?qWw?w!f}VC?S&>fLrnoR&/w] &5X8{T̆KH1:*fɁ2ٝ9H@>͒ձ4$xR5_FCEx$'P_>g"VW,e$8뀿 Yƴ@͒JNJEKG6 +>fDOlXm/sRdt3uivz. غ>sKKt @r-q~߿6AN!i#KU?ѡ^=p4ۣHw֯[2[_af6kY*@e]2M!OEĉXOTHr)89K2~elQ!*#5% T˖a4rx box4bEI >s&DQ10H}5zbE7uA#漘N&;Obݢvp!yr2ά2lgs7H5(d-l.| [悛5N8l1"86ٰ7"7esmc(&r۶ɍxdI_:@+5hҢ[SIaoRgz$y )+ODK23kH PU y~\_5֜"&/ebs%BW{ITvQ~W{=bmrJPD8gmHi|q[8= 0:N.khKgY')? ]Pd9hX O]AOI/-pHZP̫\m `x))2ΝbO**5b,6)鼳)qsrB#2`}ox-ɒr-d_^-~SS@)k6"wL/ (Z070WEcWb|ex~7s䞗wey>3d7-1wȁ{ۤ0T3֣6HsbDT.#G*  kXWPo Jt>Q=AZV#A㸞i&-WffPg[ ޗ5r]DG2=`GEB̈́<>1dkbp ,'][Nah4mY u]!ʏm d(d4|ܹԔ,~@:9jYs-L3)á+q ɈYd uMrrkͦD,|A3aÚ)|C94b|B:?2u)y̓re;Pm, ;R,ۀQy!YL$: %E_:)!fI,H9 NL\ < V00loǃbb-Wǃ4iq#]k X..?0G, PPZ9(!X%Qwo+v&nXio(q7p2P;θ]ME]tO,/<zE2ZeR&ԓ9Yi쟵-R&E<T(7:Q>r,2Iɲ)O[)-S3_wsE 1G?(qzIQԏcau?/lc\~mSg2Cv \$>wPӜ GOn5I9IR%q1HIztcy黴t\iy"55{Rt(t_utRRLK5Lt…帶5RAVΈLCtPP<ڭj={y}yXXZp'H_!=(;NEd Qg~m@BQCBVĜd8^*i-7vD@vYrUTI`Hbȱ /\̈́ʌzL N~8YnsRݵHvBN#OJTHu4-L˝lHv- `ō{AL|g u!mWKL9'Şd8c UrT(;r8Br7lKRnS( a\[frRD ؽOs$9r#,iJs"0? ];_u)!M21x]XhHȱ H2e p u?tOD21* 8pv>!M%tX?ga(ᑡOs۱挖t% c95RQ$3I}͏o; F9o;| 5Z=c`W1TkR㚡=U cXE ST22tnds)Jdg?&,'gw r /P!N)e,!-)XeaT9J;ӣO@Z(Ku&@Zkk kdHcS.O iE u:|E}*%WH'>'$!&r_9el!sXۯFםmW-юg#+Da ȰGG1Z=thr2+R1!gs <7I(1T5y'Ѫ(zƟQH(QD]J 5J-9o"yTQO"|.b8|=l$ 9GAUGȿ3aX쒖%9%V0x':K`||E[7@NRր{$H,v}:Yetz)cS c_.CqCσ L~O!]np eS"M#lJǭ ڐ%5[ݓ KHR19I6Y ,s4\~X}'/'ôkūlO72gRQbHpHJU>/&?+"qr )0a >qr?:{#HO"2,c#ȹcK]xfyxFyȒ#YiLᓥL5rÉQ02 Gc\-tso @6w OF%x*>4h0颛>)ߠ3ap |LcuIL5IYrB'K1 dw29lƤR"yM>ts>֤44&tW2 h:~lt 6Eȱbp+7<&uJngG+ee0^rfT,L,xla3#=ݘ4g1%'x o#@<)~_ rҔɺ 1b|B9ZǤ3{>L(0b fS|=SsIr#iϯ;ngW#EeҤNα =TiW^XrJ mEIS0|FO}:7`DvY\@@1Rx)f,N0ׂ3Gɮ]Qhw0kSBM7x $&nX@,mYB<5iKB,U 0\$~+J$e(KΉ'לd7RRU9SXp6}22bRvl!96 b*/ +q"EFWȝn>B R<ꊒ§H: [?i r)щ9;{K3%ovljܕ|K_sȓf bTb("x1bDL4EuX@,6J錈eʃIPD]WA jILӯxV^=Gk Uْڵ9 Z"p~2BŴ-|RX3kkߎ \%ih~ PQ(`^2Uaywӗ1בkI)4!ذG%4ѷ\S!$ɩ.>cَYq zk.PBi ГJz{,~i H5̡(\fOyʈlOcTUXc*IZkqs>!Sn?f+tm&>y%C9B)b"G{x@RHQ׋8\Ԅ5mcuQ^*Y<'6e1 !I9U@ieGU$ra]wh`Mo]-R%E؈8?P]tMK dUbҽAoFSޠU7`ޔ=@(\%'p@uO2-64] \w.q'c@]9 =֤'K<>I/MN[%||Ei AV@éj}7L%io y4As_AqwP&MErkS&Y- Gǁ3#SipkBhJC2dHیaz_uϨ3GEdxx"c/b.rm u !F,u2 HSiyWW/N IʊT)ht);*q*!ti0*J.UHYu1RTH~!ɓe5eU5uY;#÷^TȎH?9iIW˫1IR5Ve8 '(Ku٧xdT$M.ai>Ɣqz]5}mLF#{~lʈO(m8W! PQttKiWƢ;Q`'L9^K_4ܥr*i|ĩ2N YJ$#q$?sG~6oCN u` rIu!zRXSJyXU!hx5: 22,w,LWN$XV$9C6$}>4L p+핊QuEۂߋ⡾ڎ3eJ$y;b0N d+Rʶq炭(CHbTH?BCxZcX8y6նrH!!+NU*Ry0Klk H^B1*dyJAp<) wL|d"Ps!mE9I-CNKDԩ݄*7'u^">DCC)~ˡb1U rΨ}5}ZCMH"5blHRUd2 fbhM? ۖ'S>s9 ^F)<(?b/%KF_ks5=GSw\a,Whk$5A0Q GóD/]Yv'02&͜+Yq%eȪ ԃX88l ՖALs6 l&߮uvGTж{<4E0 Ѥ%J:vPFZYIyH⩺.PİY}BU=a#P&-Kɟ#+5l ;؉Z4m]D> &5(w0FNpmaoSv[K F4h85~G؟0ۜnƩ(H@"p:T-"t',ێq8m)&sۓ$N0[.]Τ@89Q$Nw>1B|[eE^Bġ=_[.hZ×cIe]>z̨)T1d0ٌʻ"{%zL6dZγH eÞlK5\v1ͮrؖ. =4&ȫ}#wbMMαxIbHQ6 #+kcUi-B+ȒPHˤVmthkץGK'$!7pc002!S 6Őge$9?n&j G&f,*O|sN bo蹆A CՑFW`BeTcCd֏s)ۺ [kCUi, G3r _nU6#jr/q$egE̱Kق?y͸aNRg3 vڒyGP$1\D躈{o^2Hb82Bo,!@<0HCl)1hI. 9JzQQ ږ[8i!Ư;p*/+t @^G-SIRLVh烻AmSftq.I7Q*%Nqr8@,qbItlY<Ώ*p*%(`Hs$MSMG=os.Ts8/R+spoit=vÐ7ö&V}͔r)a Ge /zcyՀIOkXE 73ﱚF𪺇,dY(UdH !Igc^ٰp3-Nwbdj3sfU}͠Fizisd5ti4"ܽiML˔ 9{#khQL C?*BǨ~k̰8y< I囎%daH֥uM$R0$y3w z- ےG} %Sc8  ?^ chJY"J(=|1K931a Bb\|q@rcӔٍjF>&KJ42͔j)MP)Սاg~ʙ1έ1{+| Zk%9}>Nrfv"6}OfQ}.HrHaB~HG-GŴky19k>28%~$j]E7\QȳH%.c0D=jK;p(l m]崶TN#@d/$M~(8ĵ*/&I%$4嵞s.WKى}9r$/*i<.<)Oe{viAQ8}ٙ ;0rӪ4ɺ/įѩwEig: cFgp*d n2ZorS F9^@QC;k9 |SPW>{dr묍HR[o%OU\\ծR"AN }2' C1s"S廲*b 56"E49jǣ@8e<GL&!E %R)QUoRH&p䭘"K^:も'+Š yxx˾<PJq\ͤή. iq>Mri2OsN6mI5'*) y^;lf-ǐSwE>)|~qRElŪ%7]]&UE7ex_AfnYS!OV4y+'C@cd% 4L%%^%3Ru<9<;Ef)j1 rOJ|Mƀ8 ^5NV:&=.d0TRQqwe 'Å_q,H$ȐBqcPX"h;OOumKX bia#OM)YݴjMjF}NOOט'@s}BY:IJ孮ޗccd=\ԉa6_m}ٌy;NgEWoNjm=l#Bc'p<kYn{+v-bi.PtEiz*[B"O coa$DS"uסk\41:qg52tڦ<'\}+L CIRlWXLzvd$1GJ9z y#ʽZPrWadDCȞ1]:E؎:>I 1Rڹ\EuEʮ0JVa!Ѱ6'`P-*Ώ=AIl3x=ĄS #*f:3kI S$fiጆtcHI!$ޜ-\tC~Oň9\ROwa[cd9Ys [6$xh荞v þ K=H0dECmU}qϙDms==T( 4<װ џ_ض,$ uRXOs6mlv{N8SYI_E!'򼍻)}EL"cIP@]˼V}*݊~j+rSGG'l!@^\4||jTwidKrSM6#Q˥l"Ps7k8=،2yndޓ$I&C,\Σț9?*Jzdɐ"劤oHaH8؅}x o4ķ--4 L3fĶ=LY C2q іbccG푏!|zǚi8$Ia(vr)w6Rx )! paPK$bq 5@7"-ϿH;1ͤszqLp/7r1?|.^ϋ9؝]؞ٔq*Ng#AZ+2cl2eGv(^p:C.ZngXN+:nr tvw bX\RmMWr%Sy0TyGj,S ?81 PPydpF dWgf?uMSN?I3 Wz }(ΣqFvۃ3IשY/`%q'r 7p-Wq.Bwu.2ZnfnNaetaeMsR.#3p^+3`s}(+eiwiv$AJĕ1v3]!ϤhC<$P'MpnyXvwر!SU4kpmHބ'@J's?{B;u%JGA#R~}~[j+fMӳ~3Ao2NP@@khTw:"NC]c-:?e(;Ϧ_prŮAr#!͸"K4)°cH~1wrCsyfYuHאd1˸7gluxוR]wD'JQL|X[Oݶ9`3pWu2 Zg$ėD]4.E1boߒ'n) gf^'"K8e 6 愵)"3絝 |Q*9.8qJޢNrgG&;vAs {`H$e3ܫsc8p3A˫wCZ)I6N{d@N1¡} }^D<1ZƣHt)s/c~o˸˹+?r Ws .2~\U377~f Vp77q%<x1tvMϸEK6; :=YH>'I,)ݺ0W̴{-')X>Z/Bz''u(wEkq F^YW QC? 12) {kNۏ8'Q f)g_\>+/Zd %b$Er7$vMYI=<ĉ=IFlk,8@Dl!78H㴔ci`"92Ƨuk}(PkjDs2ahgEEE$/ORp se n^@3D>|da؁*+AdF`}'9^ǫx)G\@`0HᅼWZI1WOqq'nFnO\͍ p!w-Ȍ7jh 9 W]ɩ/HًӹGr["ױ\Nb,Ւ=0h dCQl"5TL<2'ap0uiFtI=;g?㤤O܊qS]u'%RxlkvJ]jn)!b 9j)@R:t@wDkp2M0 Lp`N2^8@zQR,L2y3W9j"D /}Ʉ{7y^Ffғ_:t#H6HGCJU#uw yz6|n\k~3/^; K6 ![aC DHC'*nT]uUEVA`$@t_uzM#!oI>iԯ=RJv/0E2z, F)~  Bqx 7'2 }t#KExJoW mprX:pSn4jĴD˕b-?)lGN_e7\.N'8ɈE"zՒҠ$L~ez|0#2Zں˗ 8V@dQhβ q^55z53EtCynb~JShG4 i:ϣ1YD M_׼|ޥi2W0Mo!Xz~/+49 2O&g8[Yi7Um]moC XVY. %,8V@X˶z0A|PT%2x,fE^PYn,9UHFoӿZ M=Yi#!WKz[2E\3v1=]y6g3RD &FI &,h ٳ}| PDpɉgU^9X(fXmٕ-8rqN ZRg$$Kzqnnd(l:)CaCs)R).6~:k~4ژOfu4eqZV5?CϷ"beWƑ#9[XuAU^zf HBewNIhU+N$yh<'Ѕ4Q,rH: iWE7&vd(ao6/Sxчqeϗɢ."(R x^QɡIOT]ػ@}*J;JT&> 5L%c maٓ<$s)c&r?XT!B$قy<-aO#5Q!p6QXmZ'0D]dyS^0qy<ZՔs,U~ϯ|}نqIPb]v\2ScjfC\"ӌ ßUV(0 ˪Nz#p ˔*F.HM"!TdkhnE˼ē<#r5q>qnLbsF6aNِI,\xMLg6y;^t3&幽X־}CS ^CuM$.YDȈK[a aש|[ b*^/?v3 j o3j_yư-Ә\G\]<#<Tyud)H\[PDّ 8V@oohBq(q"!M6lJwA*#O4U=\,@ KEL(<<'=1Tr@Q6DJ[94w$3^OzMM jmާ$`ywe(DIXi 9HA' T&/|aVVBAׯeDI3P'F|n573CZVe\ \zX“ͥ|LS03xE$m3[r"^H]orQ\1+ <@Eev(BCm g8iq dspϱ,k^,BoQ MDEƸ-Ro@&uxgxyT6'/v 8%"zd~WdyS9/4 %qp0Bg`#ǀHHst!?Ƈ7U%1kPe0&#/d]< Y!*5觇wx~y翘Lb=,(B$b a6gp 9z[!EM?|TꏲBZ$*aifIuMǧ/(ȢsO٫R]:| ySQHj[":|MAuؐ͘ȶ>ϾϗWT ٔ1tᐐ vHB8e 09/'xBjz\=䏢WV)"j 2p{E(yc!b t9(b#dv*·em(%$q*<5\M;; X֥(/3#q$HcS`6`<[ ;2,q:p%kY*Shל|Ui[ƝiJn|Sut s0.8tb$),"$ȣ0$Ctx9WiZ6pJL}kb Y,U^, 6+j4o^%>*Uqjp?Qh!?<UͰޡxo`A.idg-C (ҙ~)?-)Dr`%VJW5Z)HB,S:*G =}j H J|-ʪ^-|M>AI |\2X bgp %#HbViQW L pʧ7[瑯[! :vT06Y$(B1\6@HJ8 YݧVTkHɣRFD29ټˁw9̈́ߩdЉ 8:@PcF﹢qdw#WXSfOLI8фI%+~UisǚkH%I1eokfS$JjRƤ[`YI'C\T.qbrǚ|&E}Xh;Wti7/qs.gn!^M\SNQBMQ Q$ Yk@PAy\)Dk ;W:?rA$QdaghbqXLHEpO@`1pF9E_fH"#28IbS%E   8*c-x4r(:Z`n㪆fDWm WBYc\AlBɉ 2-ppVj#s*G=cgv2qhq@qDe;W[biMjmm~W!==' Qȓ!%C.9\2ihi=/ɥy&c mIa %K И{!F cAk+ӣC^<^ y }`-$H !E y\RDHK $b8XdG \{]y:51yɇansɅOr6;GE, K t Dɐ4 ŶR6Y:1@' \lc_2mY:K2:m݋(('. EyBE (2rX:0;GN$K f;>.[ JŎ<.'F?}29%¦=v}eF$QDq(y7ʑGiZwXrr&BM*p9#(!-Gd g\OoX[mSF^bb b(Ҕ)m86y\,$q(҉KJtaRd{5& (ҲIJQ(IRiFcrlRng¦X;#UibmwF:6ȳ{r2WX o.g ,)8dI.ٛwqw\$uY[~Wۮ:BF )ף~n /lۚ{<år :P(bdppȒ 0l,9#3zϑ&i%i lž ( @kg.9dcQ$MY"(F5a1%٠OVuU,!|E{,gEge ZKyM~m/4sXγ\dWF1"ķ0#[no4Y8$H!MHdew @>CQ KipI"M0\v$ɲiK`r+Xx؃+IE+֨/IxőG<$IcxA(Xdȑ&I0af/P|]0y:%&{Y&c38y%z`XE@bEC(MXy8c !-%D&;rQl 0S);ǘd:&Hf=75/OF9'Ymќ<Ӽ)it=6Yk<#%f#^ E49rIzGaft.q!B؀8du}7E%G\\,ly ]KR[v Ld<͉Xlx2WaH&E y:媔aIv50C8f2G[~@Jb<:-SpH#F,9\K:\„j"LzeQ.>?\İ)'E\oaɌEÒEhS$q%*+!+g+ڑ+^ZMdH߲rm!k33Hc$9 0 @>qPE]L@?aZM> g>wBdu r$Yсv0l f]Ȯ8RHg502H#Ax33|x#oT]2.LaX Z>PtǒMuKoĖEj<2ᗣ=*I rZ(29M Mu*_dcl[H`Y? =xZdY}I< b51׀Ԛ.c>{2uէ5 ̛Z,dIJ)d|]85 uuLfc!E$ R QXY b> S @L21eĔ)S @L21eʔ)S L2ebʔ)S L2ebʔ)S @L21eĔ)S @L21eĔ5 e1mwIENDB`sprockets-4.2.1/test/fixtures/default/vendor/000077500000000000000000000000001447572140400213145ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/vendor/gems/000077500000000000000000000000001447572140400222475ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/vendor/gems/jquery-1-9/000077500000000000000000000000001447572140400240725ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/vendor/gems/jquery-1-9/jquery.js000066400000000000000000000000141447572140400257420ustar00rootroot00000000000000var jQuery; sprockets-4.2.1/test/fixtures/default/vendor/gems/jquery-2-0/000077500000000000000000000000001447572140400240625ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/vendor/gems/jquery-2-0/jquery.js000066400000000000000000000000141447572140400257320ustar00rootroot00000000000000var jQuery; sprockets-4.2.1/test/fixtures/default/vendor/jquery.js000066400000000000000000000000101447572140400231600ustar00rootroot00000000000000jQuery; sprockets-4.2.1/test/fixtures/default/with_data_uri.css.erb000066400000000000000000000001171447572140400241220ustar00rootroot00000000000000body { background-image: url(<%= asset_data_uri 'blank.gif' %>) no-repeat; } sprockets-4.2.1/test/fixtures/default/zeroclipboard/000077500000000000000000000000001447572140400226565ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/zeroclipboard/FILEFILE000066400000000000000000000000001447572140400237460ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/zeroclipboard/bower.json000066400000000000000000000001471447572140400246710ustar00rootroot00000000000000{ "name": "zeroclipboard", "main": ["./missing.js", "./mimetype.unknown", "./FILEFILE", "zc.js"] } sprockets-4.2.1/test/fixtures/default/zeroclipboard/mimetype.unknown000066400000000000000000000000001447572140400261160ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/default/zeroclipboard/zc.js000066400000000000000000000000231447572140400236230ustar00rootroot00000000000000var ZeroClipboard; sprockets-4.2.1/test/fixtures/directives/000077500000000000000000000000001447572140400205345ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/directives/code_before_comment000066400000000000000000000001011447572140400244250ustar00rootroot00000000000000<% VERSION = "1.0" %> // comment var VERSION = "<%= VERSION %>"; sprockets-4.2.1/test/fixtures/directives/comment_without_directives000066400000000000000000000000471447572140400261260ustar00rootroot00000000000000/* * Comment */ (function() { })(); sprockets-4.2.1/test/fixtures/directives/directive_word_splitting000066400000000000000000000001631447572140400255650ustar00rootroot00000000000000// =require // =require "two" // =require two three // =require "two three" // ="require" five // =require 'seven' sprockets-4.2.1/test/fixtures/directives/directives_after_header000066400000000000000000000002141447572140400253060ustar00rootroot00000000000000/* * Header *= require a */ // =require b // =require c /*= require d */ /* Not a directive */ (function() { })(); /*= require e */ sprockets-4.2.1/test/fixtures/directives/documentation000066400000000000000000000001271447572140400233300ustar00rootroot00000000000000//= require project // // = Foo // // == Examples // // Foo.bar() // => "baz" var Foo; sprockets-4.2.1/test/fixtures/directives/double_slash000066400000000000000000000001251447572140400231210ustar00rootroot00000000000000// Header // // =require "a" // =require "b" // // =require "c" (function() { })(); sprockets-4.2.1/test/fixtures/directives/hash000066400000000000000000000001021447572140400213730ustar00rootroot00000000000000# Header # # =require "a" # =require "b" # # =require "c" (->)() sprockets-4.2.1/test/fixtures/directives/no_header000066400000000000000000000000241447572140400223770ustar00rootroot00000000000000(function() { })(); sprockets-4.2.1/test/fixtures/directives/slash_star000066400000000000000000000001311447572140400226150ustar00rootroot00000000000000/* Header * * =require "a" * =require "b" * * =require "c" */ (function() { })(); sprockets-4.2.1/test/fixtures/directives/slash_star_single000066400000000000000000000000501447572140400241560ustar00rootroot00000000000000/*= require "a" */ (function() { })(); sprockets-4.2.1/test/fixtures/directives/space_between_directive_word000066400000000000000000000000331447572140400263500ustar00rootroot00000000000000//= require "foo" var foo; sprockets-4.2.1/test/fixtures/directives/triple_hash000066400000000000000000000001001447572140400227500ustar00rootroot00000000000000### Header =require "a" =require "b" =require "c" ### (->)() sprockets-4.2.1/test/fixtures/double/000077500000000000000000000000001447572140400176455ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/double/link_a.js000066400000000000000000000000331447572140400214340ustar00rootroot00000000000000//= link stylesheets/a.css sprockets-4.2.1/test/fixtures/double/link_directory_manifest.js000066400000000000000000000000461447572140400251120ustar00rootroot00000000000000//= link_directory ./stylesheets .css sprockets-4.2.1/test/fixtures/double/link_same_file_twice.js000066400000000000000000000000561447572140400243400ustar00rootroot00000000000000//= link stylesheets/a.css //= link link_a.js sprockets-4.2.1/test/fixtures/double/stylesheets/000077500000000000000000000000001447572140400222215ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/double/stylesheets/a.css000066400000000000000000000000231447572140400231460ustar00rootroot00000000000000body {color: red;} sprockets-4.2.1/test/fixtures/double/stylesheets/a.css.erb000066400000000000000000000000241447572140400237160ustar00rootroot00000000000000body {color: blue;} sprockets-4.2.1/test/fixtures/encoding/000077500000000000000000000000001447572140400201615ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/encoding/ascii.js000066400000000000000000000000211447572140400216000ustar00rootroot00000000000000var foo = "bar"; sprockets-4.2.1/test/fixtures/encoding/ascii_utf8.js000066400000000000000000000000431447572140400225520ustar00rootroot00000000000000//= require ascii //= require utf8 sprockets-4.2.1/test/fixtures/encoding/binary.png000066400000000000000000001236451447572140400221660ustar00rootroot00000000000000PNG  IHDR,K9tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp pmPLTEjjjlll ޼pppzzy}}}uuuܹddcى􁁁+++[[[SSSKKKBBB333<<<$$$rrr뤤렠nnnԘ ǀ||{xxwppnnnl___rrptttlljggfOOOWWWHHG777///(((g-IIDATx콋[X>@IQ0Y5QT6rDDZѢ>ޟyj=_>vi:3׬^{kmuM/#_/Rͯj~巖_oef~=jK7#(sK7՜EEEMj" K'DUEZTt$A<'j4:pɊ(x~G~ 3y;m :~W\RA7t{ ÐYT0*zV aTiNFw wHoF%ڭ.u^P]P3ڳa TA7Tc5#"x&ͿپKLST>² T=T,ҽ*i hF&>ͯl$+2'_k¦Pdx&)e ,5Z-20$uQ8,!L|tg+~U#0_A{IDނdl]uvFk2߽?(jʜzT_nײمE'uK5烘L&߷eK?FBO"fW0[[Cd .X4 bɐKG-!c3'dN>׫bMS,1י7^:)aYSPS}TC^Qc*tuZyrA e{xEm2„#F.0Sa穊ȵ]5! hॲxej2,i"V{xj&Y^ga@pUm , b,f٣op'p7:_k(xnk|X"0e>Jj9+30ґ׾^f.=s[YsIT{><1Xͨt6ܻ}:'.&Go:fSci80b/( |`Օᘷ!V$U^i>3vڕw&߶gbkS9#OU | jՠSuz66hß6_w) W媩HZDX0$IUv2 GdC'=6F9Fe zxpVbLJ Q8um>8pvNk؂-skͪ돈,"oƘ Jeq|d߲ {F9Z{T=Ea] ' UiNM.O_xY*6C(ӄj"~b>UᙵTUc0=! 2\or>geP*(ź (kk)7)(`6:cWodqb7'ۦEmܝP/d/V͖@qoC;W 2T,Trļ ZK=A ?b|=Lfnb č|x!ف|ܰ ߣ!)·l^'Cn_7 Ѓ ? s?[C@^V:®nX\f؆;>(}U $o|-Fz#FpGی4›։BS[og!؆86?|H9w z5&MxVU`G8$]sUЄЯv[ HG{1;WdR`j"WR8,坦@oz8s x@RT,ZX)˲^BQESTb86|'hM?OmcWOsUѝkwlogq` %8!DQ*# C @0!R V T0 ژ @w}3];\\arsUӬB;{pNp`G]o)1w my[~{=ҹ`Bܶ=C|sw:蝋udٺڝRtYˮ݅iZnơ-by̐Vhz0vfp}uU="WY߀ޒij2uגiWX'C zk孾>oy;O/3@;.ț o7t$ CZRS'\@|3+y瀧gTO>vZ~Z}Li/23 "E4›G o: ?0K`oHc*k鱚r,=Bi*6R+9 D\*$eD1W]m঻XbY t|OyG/Vv u|%3<e![7/Lu sA8bne6Ab0ͲaD4gYjy xsȲ3U?2۟p@B/a"ץj v̈́Pg># :g7г<9&B[LO ?t>;zwEH& ӑ-3]# |D `HT$]Hj 'fQ8m [mli9}+t݉kJ' Y[>gl"q o/fX' (vmض6;ml~ۮS*sk<#X$MBL xEXm'wQTښ1{.#shl9:B7*cӴ{v-g̟Lx-m@hۀ́0aŸ1έ-tfvvFIF8i6Ge4^X3:kb51?Y5do5HU3<3 V3ƖE1`t<'@ Ba|0S&$Vba0=.]>/\tu)V\OKׅF%ݛ|pmaR4V]Iš!Mŋ T=MbqLlwŰpfQgcv&ۼ!&0%¥e4${ՠE W.i[Dhg!1G;Jw ^%El ,oj(&*Ĺ!imwv47 f›%:z6%`;6yJۯޯ>`uW%tPhLFph<^|FIpDROUTtI\/Q h٤TOhb=nZ6aؑ f`*Ithq:Fs~$D[~{e$J&OwW*og:)6pחs}(_XGܗD0qosڶP9%w_ vsN8gݞ[ߒ϶F]3K}3s 1ITVWDPq syݞz ۠pن@zpǁl$p;뺳U[;|Slr%ݼuIp`MF8 @xFh\O`Tmr_=t/[ {D`{FzJ}cʯ%gV_[a:Ge߀LoVWQ`0Iϱ~/T6zI@ 89acGwwΤgEz=p̾A}P?2Xz6k'Fvj|8*sO}%xKa mD>*2l8nx|h[zȷ{x픧fY~m^ϱ{;A(=p x6Z@ۡB-<*h=SU HST9{l\*PEƯo1n/cE0Ÿ"r~]Ax;vmB[+x!xt7k{Å¥m`!W(E(vglmwbvuwk i8?o?`jA'1"v77]_?{7CH |v ZUfܹ϶yOϗ! *qhg4mWw[/8Ȅʇa }ݴai/H,xnXf_ٸ %Ɩ-loApwvmK=[?g "" Lr[REҨ2u=^FVxde]4B9hG!77}G ]uv*kM|(Y^Eވ*MFnr06i#b@o,g=sø{/ 7~ܠAck6ModHAD$U:4MߊĞU, 0\P9u ťer0x̲, ozXvZ#n?._6aX>ZN/Ev?HWi?$;75uxvvX6Cc=pv{)ve=se}=hTE7RUHD:6Cu_CJ!w5zRgx&=cA=!7}~|uٳ~R!UE$֢6&]f `ai:֜_I߇mvέpMD3[Eo)Daw`400[N7B]U8a ^0,D57j l~[Yݮr|9!Bs&3cL@n6VAehnBRa4xk<ڙ@HϻԄ i#?Qiz q{<Ʊ~a^F aBE'{U&ZjֹBƘ"8xj of@=nu'̄tIUb+oSOQqYlb:J8}!h)"HkDCJv0Z%a/U @5ׄ7yu~ o^vK$qQ􄢉 TCMXVz  M(dR7Gq)kTf-fvJcR= ]mv?b)agb8c@'ԊM9Eoe:-g˾A7O|ecvq`*]3OnSWܰ\G! >mGbw(=lF흧۴-z꽟{Q(e%,Ih-*Q XO#93䂌"t56 _x@`!|vx|w'omm葥jIxqZdQsxd4M x=;WڼL܆D/ƳмwIfUx ekHxyfT6AO~'b!ȫۄ2,@+2zь͞A/M(F&|W?hosaQaMx3f H}NMgs@x3eDxs1]z 9<({,}Y {WE-0Ҹ;m#a R; ͆Fгio66m4/-[ 0MaO $aj:0Ȱ GS\pT5j -J [\'+1&6(P?{]qZ .tUE͌zL*ãSa[_}}FrVQ@ v (l&2H6&4 Ꮻ<'U y6Z 1m숻SĒIMKd):j+$!K0hƅyޜ B(VnNQ8Q5NSJ7ϞĪ0^"jQ T&J*@Hsf$NLȏ7Kd #'.jRĒ7L,eMx3H73Ɛ>ah 3(wT^ &h:$ Ah" g 8(j и o~@*g KuRb/?M8 b,2x( `U3!ܟ9|7K@wY3)9 gMTj x YuGPc#RZMVPh(&4ȿ,h//7D>߼@on7m`n1lm]gtN;ڌĶ{s4:r>M`ݦn8BBX;]kz 9YՑsƉ4i@fD/qB#D ?A5q~2J?hC֗Oӊpqt-a; ,4ÒA r 4f|hװN:@͜CxOo3]%FO O5FǛ+s3W"5f/B=l4sUtИ;A/0HO AhB[p4AEgeD;sP97Tn#(+oTx JS'qHvGص(?6iP]KQg8cJ+1jb[5N"eCpҕu*x,UT?JMvov}!zP}*v(|Ç+34`8##=bU0Ûb^5/&T2XV 8LQ QA8µ}x60Xm|?en% Wr5$Ti\?(9~P5tRp(s|}#)pBh8Ni;onhV._.`jpƘzӤ8lll.TΓǏyoL%dj, EB`iEx8}7(ik|ѵh{ݶ#~&㏲naOS}={y6xK"xEZTi>[&3&X*G wtf9 _$fF1h07!;u u2A|WSjo n›{%JIX/oKƽ-] ؍;[ێBg4Bn `;:[VVϷ熆:SSD-9lxi6Bf)ā=#@،1DY#Gs尭=ok裂$pXRWNM ѳ]{sp>%aN%P!FہM Y ۴9s~ar RR$P'+Ffmޠ;5Y t k;Zp₩!2(,@PT]W^2gVA@V-p"D'R۔ȑzi"T3/D^K"StWE"|A r㷓|nR VQ$o9;23C2;TRfd)!qn J&&Ʒx Paq.K3)/[o`[xYSq\U QV#4qԿ$Cs,~&|J£OKQS/.``)jf¹; xXYֻFQ!E1Yj#&eadȔȧɬxv潭-˂9߯z'p!J*9 v\ʶ.t9#6EJq{uGclИL4|.ʽu(L%;"Ř ,p:رjr2]KTqhs"3WpzLL? !pQhogXԩ#_#зP.OωT֪|O#ޜJ}zcOyj8׍}T{uA%xXj1t za|yIAl8'AY+Dt֞_|NRzbU%Y/Sk5»~MbkTx7ڞijqD,KT'amk%9b8Mn}Ѱ146gr@9YfagނR_Aߢ48amL$|}Xu8m,<.negfAp(rKدՉ'2s3@ުˁ̂HvԁU v4[XEGdIR9}P{ ö}k)u۳agล=q'i-Mz.ސD5#!iಆ/2(3uB՜M .OosOw!:os|xo#B =q~Xc/ DHN??Gk"f %#kWx#+)=kyWRmF3#X'mܺl76Aw]S*fl@Q 9.1ubHj%8dNgsniw+?i$~{KPEzM/tpN׌9CcNOw/'{gݲSIH\jyDg!z=dLco}˅8X*׈h8>CzLZLQ1ɝhLoooI o4XWVsJb\ 6\o)|}j%<?y1ȸ_37/P=IsߝBlԂqp櫟GTlvg]ࠫ~0_Ϳԫv#DDF /+NbŊ 'M[ŕ L5H~_Eu}9j$bv=e>琰i_cm@xs/#0ß7~3+Ҩds $qu=L\EՈ Z/ʅAwrϦ;Nm›#|}v.$T'-42oˏa\iLT=+2- Ͽ,,9S_}j`udRu}asw8ѴG2j96ڴhpCLvMfCU2KlNdj£R BX37Y?b›%_Є7@p}7]w7e<MU+ a4֪'~D&)C0k2!&n+sVZeտW^%skgoANM7MQN]cl>kqx4dj$gNQ1(ϾxIs`Mn9 xHC^cg-`›k;(OapcQ@f9h[B1]K.j_s kT=':Y]yhzXv2nFtcDy{d9%ivDǜt]tG^ޅF hxTExzY8?S  i>׏N46 z[T]@vlfBDE -jRЂ/@[WI,BKž8wh|\LwcSTu"u14bO47e23pcuA$5D2+otQ"X>T{Z](#y/dBw֊]N ]M`,u 6&___kaVLTLdq[u(X)εBNWߋR1ڗUd}B_Ӕ~N쾨+U!UUTؤͦJVFm8w` LΓ5+b9BtF곰ə'dq##UHldEL .KjV4V\Pȸ鮷@nQm5){8ji\\SULNg\~CI ;sNfO6|G̢-k#[Z|ypfT&SiG.2>9asI@I:ܐLfiEΕC0&fjQeE&wͺ ̩YbYwG NN1+{z q~ LYژj_|i vU3KXHt'%TL;;26#2",WXA@bN 9AXg*m3Tc\ O@yGvT˲XbaR#Z5xy kG̋_fʇ,]`/9o`ݓb'1kOca^>$n,ʻ䡈c^Y-ObV+J&z)y;`dOQ7y_CXVjYF*554QѸ-Vo^|5, rݤ5' nH.}C B !14Q k ;s̞Evp=\p@FyuZCR"kM{eKd7]m)&^dDF)*r˨/7F@dZ"S(hƠ ^c UwBzf=e: RcluZPrF,0"!?f[cK"C 15RGg 59xJ03` I_;"C a%v |Pg"Ef.vVly?ԓ`ޗ9J&W[HYDUƁL5{`gtYXxITcs6)ޤR3O=a4T6"fᡃVWOz#<*>0W:C"DQ8AMT׺Yod~;VfU.x}Yąz(.W4K?O59'4 :]88.9! 5ikx,RcK!!?bѓm، $_>\4`I]zk,>ۦpfV/+SR}WlB@g堏=ZtnjEY0Lw5nFkp Lg`!/"ZM5ē w }KaJ7Gotk2-޴s^|}l!j%^GԕG>X ,Y*24W_cOس).ezV ܼZ}S) q\-kl1|TUƞ]x\XUR0-rX S 岌'v;/ Ւ9Xa/ 6y(ijbu] T3٭C{Heq2)j'9i2 jH>xU -su&l cA+fdDhAI0 ly1iac`I=mgQh'ʭw"%.H:^.3n@P;zWͣɛH70w],۝^E<|sזᶨHsY1ų'hŗ?ϒU NPB->Ym A /7<5s:x5-[u Nxfg2l `{ęsނ 2S ,"' ,2w[u}=|o䬅f>"/Ջ3 [0r][OsUC '[ "+Dkŀ QL3|д¿Q"ZJaոY iÄjU[qX5vGFh82>dϋ+A}}ԻYrLG*|]8Gٕ +%O z!Hu_9t[d= T,hn?Iab_nɝ8>ZDNQ[1դ&u r'psW}%~}"z35:W1UCBE%y<3$mӤeXZ!džjBDMޒ ֑::xpSV,A('@N>iYLwdQT M>~]+aןV ?MIЄvwxk3ɲ H6hh C'vpbA<}U;B&^ہ~JJ:QMIG܊ ۞M?S3 S 9㑏et 9uI3hҟUM71 `y {mC70EMU)yȪyBOj߈$>0vsykHo<>7w`4m22UN4u߮Zs[:$< 7[Z膰W-82 !W,^I98WdCl>=º{v=o|C[.ྷ8}I9̈k4XdTSV[xT@\0jEaI,]`hq#w0E+}t AzpO^[:[FcDe:oQT#c! ӱX,QzPZxfmquv*Qoh$4"}}~Ot 9a:^$: X~ؾtLIVǔi: e,Z?_MtPDdg9FjtvZc?Jf qRoV'}l[gS2ͮө 'cidF$Tb^LYOgP_FHF5_;;~홗o|XS)x ߜ֍8>G~h g& ~)pRE蚠0;S 2D*YdD.y3_~Y|]?,\lg.fËo̖8Y 6PMCz*Z՛W/za Mdh̾k] 0hR.L۲Ju4<%Sy|R&kn=8^X ( jk;pk'rEV&ao87岄D l~InةgG!A e^:"W$5"N]w\3Ul\ĩZ`4uGl:_Q5 UPT6-2}>_tjA*Շ&'p{ RY,(|äCx!tfG-k5bO>bi~.(g$fFcki Ɂ$ Д̀2.XxzC͇|X!;4WBΐ1V.W/)b ]7hAkkxs'/2wlmxHyEfZi:W6=r:\b]}-p/K|PGt/x02 -k[TXlDM3fSA"@|``p(*zPIJm]})Y+vi{Szo9ѸFT9 䎅bec{Ƭ~M38]"*)ԵD0LrK( -G9_0͐_s&Q5fVc*\۾٠U*Uf䧻Yjl*fxjcWI5vr ,,z)-dz,'+Ps(mYd$6=Ȳ;,p/6$ZmԧT:/r wF7TMjDj/[L cuK+K2yx`@: ! <EL, yƲ8,zٛmAm++hOyX7 KEE]i̹*,ΰh;r<BÖAHwHqaUhK}_kvom ؠ+5,J"'_wm0&{猿93ڮ~ߪzުd@ I@}'ԇ %f,hiP'q&k䝋}41s6 /LyscV u73y4Dd<]ͪ^ut45_$ob^ lzQ@K/Dwj5||G˜Kh,Fa:%gJ-@Zn"ʻ4-]!x3гPrޢ߇Z)rHo{07w9[2oXWoYt?dBk^kl&Jy~ .~>+8e]q Q6>ۭwbYLfؗ\Q4ɕH61Mk7u9phsޡMUC5M#EB %Z+ĩ 3*l\pq8.&)TࡅC K#%"(lAQHrda6MisΆ- s_rㅸRJ,',_E34 \X'[4M}&o[^Z+ei& ×I? $WmͧtH9}1Qh72qK%=*9c[.P,O|\9 b!W MCI|TXګĿnd,XcF %<Θi⣏t@L 84+nhY*FS vw ؅Yo  ̳1SvXXӐ|n'lJ-~&nӳ)Xs z sU!'> 6ՂE#NW#v 6]q7M @SFIȕeگWCˍ&d4F3wi&wXn| /=§J4M}Jh|TS_=y` \EWNPm:BvRu=#|V2zY K2g~u_")adc=OIC6H,gq>bW-2Y=OyYa`AXB.`y r3`{X5W_͎f%8c'`!u+] e 囕T;>@Z]tznIx'IiQWEbPN;QzO0$1]_52:ſe&Az)kfCo6y>)7Jh˨xoӛE*~ؐZ/ei19Nq%Lwfx|9fx1h((ZEn9uBВgw?s()?cuiꒈ.KKv3HmRS4ЭjeIRgdH5KA-lrwYCz3~DkҒIK=s~LRһ )d7<Vtv20dztK~qB$*pNWEJ fL Φ& -/S*9} X):>TPQ*6F䎺nI^lH61iY!@_x%fC k\uoU|xh4 uߗIjql ?6MH:rx3LJΉNJP Xyo: gD=v,RP`N_m`P[jjUk3|yi[ﳩաWXMCA~Q;i_2np 6I ? <$〶= ~JXkGV tn (?mςh\3i40,z3fЛo|Ebry wN1r41KXrY^s& :NQϲ%GY>("v,km MC+%й՚fͰІct+C"y=+j: qǁpޤl_ЛICEpے\p`ޡ7 I&L"P*퀛fd4o#rJ؜DXz_*I4LƻyBiQ@Nw ϰB)vNX嗹6gi;%Fo\X,FFo+6UkS|^$[HS+]>GZ֡#Qn<.}U#1 յ,XwBkY:R*zZVEF ykfJ\qǺ42k?~4ZEׂ6k%T! 6oM4frTzqqמ*kiB@0FNZZ8r$CvSoR?nIj3ed:xdDV 9o%*T]^F͡(=~49k)[=#~Çs>fNGJi7l/j9Sx?p_!5,_q@&㱉FWub<&:-jY!Yhms;$wk!FKs7GirpónirXҮRE< fb[72W>Uo>hDzKEٰzsF]%;RRow!ab.{]E4Yq@uKjbIA\bciu"iN5H<<;b~`vDK'pE.`I&81lyQm!޽ DoSiZB̑"lÕﻦFR^t:d=.ǗD8Pt[865ih7P^d!PQcV3ڗ^4;WA媊&IKi_|m)>(?+?Ж64D\@BY"ы]SYF\QWkKH󔼯 L3ÍM=pF@vhQ_]!B#m oY&'?'_m[ov8~׶wG=q-ҟ,}4V$f^lim8a$7MjeIӰNI$%iI+<&3S8%xJi Z[rR\8`WB?C|o5~DhUZMf$AvLM{&߈48YWgX3-[j"H$u-c h{83@l)%daM9sxlƊ#De(|V`#G̸[ $r Tl8lxܘw~ ĠlW`Ko#m ygQ@!Hq"x&%Qcy/(?cٍ^9x8>7gՇ,b=ˆjn\3ATJ>|f#wH놜PԢn4oB QD?Mz=2o.>TKw{UM$XiFOԆpG '`+ɤDpIhXx_D }#kD%r(HhiɖhS1%Ngj,4ײd4uJ>Л76FL[ <83Wӂ& ^ =J. q&WVrP/dՐbauCaPXQ]_(_!Z r" As #~-c:B ^ӳ՞$!1{(fMA@=t Z⹩Q|3#7z5h 9n[٫c+sju,[WmOcEܪ .TYˤhkp"ug$uX#çٔX.iKwVG2RsРgHE7׎kP]UơZF"[67HEʓiRGDiR 5Id4ul)+B#{͉ةa|2 pN!yNo0KX`]IQOb*iAU C~5G&߽\8Ms6ۭ&Z`[;4g5{?$cN7zhb~bncb7u 񑦵LyIMQ԰wBDZzE b.y]* 0,#bEJĮG WJ$j;{ML~G$啵g:J$i ĹstXPh:kEśv8xewl{l;Ht^JȮ1ҫPO&n lx(R@9رa>9 uX=ƭUJ:!Y&GMJ({ zvYgG.i"+_mev@vcymmYVILowkӗdȫ% 7_ / ט& dӬ҃꾀n7c^Gz33~xhMD=AYg#$+oU6Ud4uzu[4TZڬ|PWYY$?ջ$FŸ2XT )27/Vh aaяe!F0—GxPE)ɝJ ;!dF57}5WҏDEfGpRi Üb€7\2\ZZe;]'-8\e2Iz9NfGz/L{2Et!NߘI:oKL3eD$.[|smn V:)v~E< Q֞=6H\|״x]4:8^`ch,G3%\4 II#oc\֘OOtjj?~OwZϯ5۱AiC#,%l5/d&&CWkRI3 '}m{oѳ7S4<_s WvBʴ?yڿXc6D^3zshaW-!`~hMwΕ-쇷hvtG%?ʻ1o'&XPo>74juCVU__8=kMU_􏲊R5ͻ}+'8i F <ݳk""BQ\e`*lh=h` ;[s`s&,iJ跸t璷e'GY,18֘ >8z$`ҹ/j s3\FC'9 8A#$3y_>`m&Ϫֶb&foeި0gy 4r&]57UZG; .{مwUXY *n5zy(H" ]d+]_Ci=]$9rCQu$bsKEuNf޹~O?ec9ЛtO&m<`t_-{/y OETOIA zA혧|f#D(]hc&zp/}^b{ s\C_sAMfŗdJgioZ+Cj 3ZAr*8]-A$_}ez)_/Bt"Evlħ=Ch';󷅼hW쯛q)?˫y}L; X7zuE]oZ*a>Rl$Y>KŖ^pvc}h.G1Qs1K<Kn` |ٸD<"6'P50So+Y~bci95_#v`sX`.Ȫk&|mx4X']&uTsx1"]o's C_yӒL\QߎQq_7 F͒ AHn)Vwe <r3vʏ!@y6tXXQ1N`4fC1enm`'[T OC Phš(ٗW##WA {> 3(˲EzӪ}WIM?Hnb' !p9yݍ⾧zQWپPHS& |$EۗVB87}Z;/QMs@c3҈髋nk0UlVV6#۞ƚz;^%ie>揅GMyo5 hc̺k: /2796 ZSkLcia+g>^8=;m3!O)i=5e.8 udn}y.'e7ڠLF] p+h 5Lcȝb өRI!:Ib[zyUJrﯚ&C4{y-M1qMlF3hDz8ٖs p l~pf)ϑgN;9-jc&9L08ۃ ^h-ka?}YC>@QNuCӱE액1r@EuZ85ڬz.`VmVv1񘋺h0z3`suZ!\y$+:g=һ[4ޙu^Az:tCݾr+1YzпWLDvo RjHr UAa[ æ\gbm*d&4`:n'Vi<.2BD3h1Z6>I5lhYЛs(=rܫe33A悅k۲–z*\V7Ul 5'0C]2qv nd_q-C Ov4F$ʼn_0u'n&A9 nvj .}$ {ЛMB1y &E!NuMMVTʙF ;x~+^ 80`CoNi^47q.0\9 Iz@\\eaWߵt 83:g6(;%6!-yBkpXH*Gސߠ"F2o>Gm'zݓRy4 yd(綰ZUd4YE,]$3;$>k0:Y*cWA@q_`1wOxG˜O#Pa=1 \m#p͛-̝ᛁr٨qojp~ӖS~M]/f7'|"Xm 6~6$|9crK7UZK-Iaͬc);U!ԡ7MH7 YAz37vDMBoZ`CoFS ꔾ%[ U+J_޹4FfGf IeI׽zϢ=se^`P F~vOrU3;u+}~ߝ&~W;9Rݞz"_ύ/>;j4SJvC?gMvyrܨ鶚>psG0 ڜWݕfml{AVI7#7cGۃA}pͱhfncM> lo6czcs;>pLxn"H|6,Fܪi.3Fyn x;u3[j_#LM伾X̺'& _sF%FB3X3^N}D͘#L8A9VSG.x49}| 5Zw8:\Ӕnx6Jڭi|QC*=`T r]7ymz3ڡ7( Ku"|tT[/ xjHoXhT[!f!nH@ ^rw=j7; " tIPTuiIa>c'Lw[6ARU-ٛyCEnwP,jzJڛmsEVe& ;rj\3gGgA?r7uif WMfsFU_qFZ3:4T]_Qq[3[Ztc9u{ș5:=cfh,k&X@u{uVUmӢc0|w]8MD'"hiK {ҍٰIӦ)< RvhgMk9v́` V30䞰 fmh$, @٦7y qHoބdf4]NIg6f`K k> ̥3 \~)Ѵ^l-` ؑ뼄 \"hA,B6*)- nA&1A U\RExr)%K I𝩨S~4uVƕ ٛ2|Gِi .>g16uf Xt` HH7پ#}>qic.OuD6ty! f=?0PLTͩ, #ۡ3~+g.^EVt"aܺ1+A BvD7#5n-謤i%˫貾 Fc{~y9\>\>"ɁxVd2 >1Cϭix=1|uJ+gGg|xrZZ^\xVi4Wdi?w CHh& `psgǹpLJsF%.0llC!7ِKP ܐ>zqJHpBnP񗋡^u;S +7![e fPE`t^K_C:p/vߐ[ѫ:~EP 3m%TO{nENzS%镝LA"V/k=y.u5 ` I1خ%?9 QԚyV󉚥pmL\6̫M*aTs [1Z\vÃñ9SP`}FflҎBd'UOࣵ/ 1R;AuS+H``/{JU2 psaq:ׄDIVy0Z5͖@2Y~,*amʃD#%<,Žm+*% 3L(_`}낡(f3e8KQFhHPRAk P Q8bu@a\@mQ3ru /=RU8e{ak奤DVX>ij,lak/[ƭbއ%ILZA3tµ-C2a쬐ꄦB> ~rJi S7i +v/ßoT44$f3#;Ja4 2dK5,` ;Lfb1_Z0OJTQ>)9LC~i LB11|< Uؔ(" :D칄Cm[ =Ic˖P4#RC됫%]MdM#nцEt#idmB;<3R@}K{C[5@f"`tw:6u#(~ u,ۿccnl`onh:#;VLm7$29c(*;v?)X${;c0"ѝ*so,"bN 2}H_1'IОWϟ44V$Q[)%i+=,/K$R'O Fd ILqM{B:W6Z .'vyӆJԥ/l*J^-2Ο6Dk]z^]ʉ ߏڽ:SՍԪ~nWtF7Q&sW|Wpwm7SCop}3R9 pM,ax˰wؠj0uju^MUoI'= e SsLUrRnU_$?Y__@ E'MɧK[Vrmv~X&ӏjöT<Ф. xj10`k..1MdTbd5h8!m4pe婰as3R]5r+T]AiiZzfTFʰH Cƫ>BMܰ3W(se;f"Z(E@D׻0N7u{d@-= kCCnTT]ֳ}3rV7eZ*EֆTvy,jqϻ]l 2}b(~.&)BK0izIsP5DR1%?4=]oHȢŦ i-W8^.ԁ9NMwKQS  QgR3OPq,׽k] j1[97L-fy&^a|TQF8NAk<dT=5c= pf ejY,bxʐ=`z)3$(qOӶVuzfO '/_2QMglXQ{)69eil{xm3JfNEk)'6 >rv6O |SN犨(I5%k sA&/OhYoWB:=T-nZꭾRd7y ҏL\ki`» Х6bLYtgpqҏ{l3A٪ 3j?`#:lG7/=|x]`.>\|_ێ^Z~YulO1-f W9燫̬ǜ9̼2`m8L|T+mO\I&pצ\%ӨC?mϮFx9Au蔢CR`W*yL׼8!+m`iMELݪo(mUe Wz^*m?ffw6j(7LPύ }nTc,ъh* 4JH/g$~4? kL$j5=:F`֥W7Cf HĕAkJ_8y;'m65Ngżq94[)7}l`=H[1;QHbHjj%jN,jav^5fsnhkKS%ZBH-o^Q?[Wz˿i664Jr֩.famz#EQQd4 ޜ'`jO2"֖nFoRSh+\_י!f4>7]p P&/+I-WB۾C߬/iX٣4F+XcMoDFz&CFqф[f4gh0RT-r:Fhv7!͟ ١7hh|(?EyjЛ:'9XFoNFЛ|r4ÈfI^]' Fxp_7.^{:U1"ڏl*CG27Fvw-< {9/"WrE}ĨccS޷ћŜwŘO -ŰCq'b E 7BEWjq xT%epoi6-КJA9rE]nfG6pA]%fS6M$\gԅeOܶF|:qkR:z>H(hQXUo9v2zX,.u{tlZ75F:δ$69IzPxY.5zSc420.қA'z 6 7gQr<#y 6omzGQp뜧a4w6"ʤdى;g _ o\5g4z&\A ίO߰uTY{k{nh C $y7t+BoR` ^\s /焖V|\ɴi,87W?ӛ FfP@ǜ%$G+eM9ɷMh2%2MH|?UdGraqWf'#z62yvŝcHż>uIb٬INU=^z+~O,7{k]x8N$( K53kUXuWG7\ۙ8kqƼGgJOC1cTA2q" @*htu5tGMoFMxf&7lI4MNWQz=Y֕hh;bŠ'zϪ5q 1Qab]b)'қoDsT4 y[TZE B;i%\JoJϨ >O ~Oz5`^Pg*u^u$)E"1,&13f ϲJ FP6 ?qdiTlҤKw2}fϩ\ћ<Ģs߁5q*X4${~Ko`3zSc4hcZ0 ?t8?\g5zseiMg;j7ۀ ,5v>\]47 12̐Ǵ_{ eIPk?kIg)~e{# ;9 &CزO緝i ' cu͘Q-}$Uʥ~i)y+7C.]FF_CrkwX%/=U|yEelmբia>~f.e@Z0}mkl4^ ϳ@M/bf/8dcu{YYIh˫]>B3:eFjD9r83ѽ>>oQFUz9\Z5z*Ju" ^&p t4s1;[ICi]cxڱ_G}.kRQ.D"dX<+h6;1xj2bԚpIsSM#N"HijD{'X+DO9^qglw|>^Yf2b<5F.7 r2A5vq+3?[1F3E|lfۿBi0͞^jʦ8 8x#y'o!qnRk@JS&{%8fkIJ &eiA:6Cn@rƶ~X]{P[~޻.{y6(}FTQ!30yfZg޹=7G7kφXγ5^3?V>o~N4(kX,N1%(rWgyHެ.ZJ$, sfnU?'`5_*튁a'ds]x6zV˶Kgk;u?pJ;Gp4L'{Y4U Mkve;P5%8lG`es&7a؏΅ {e|kŹİ ..| f |~G%&pMl4 j@g/y"H̚Dɕƥ-4Ꮝqp0Z_M(phRh[2(t'ij gcU$dnYnkX_]=2%E4k:{NId9&</p>sprockets-4.2.1/test/fixtures/encoding/utf16be.js000066400000000000000000000000461447572140400217730ustar00rootroot00000000000000var snowman = "&";sprockets-4.2.1/test/fixtures/encoding/utf16le-bom-charset.css000066400000000000000000000001241447572140400243600ustar00rootroot00000000000000@charset "utf-16le"; h1 { color: red; } sprockets-4.2.1/test/fixtures/encoding/utf16le-charset.css000066400000000000000000000001221447572140400236030ustar00rootroot00000000000000@charset "utf-16le"; h1 { color: red; } sprockets-4.2.1/test/fixtures/encoding/utf16le.html000066400000000000000000000000221447572140400223270ustar00rootroot00000000000000<p>&</p>sprockets-4.2.1/test/fixtures/encoding/utf16le.js000066400000000000000000000000461447572140400220050ustar00rootroot00000000000000var snowman = "&";sprockets-4.2.1/test/fixtures/encoding/utf32be.html000066400000000000000000000000441447572140400223170ustar00rootroot00000000000000<p>&</p>sprockets-4.2.1/test/fixtures/encoding/utf32be.js000066400000000000000000000001141447572140400217650ustar00rootroot00000000000000var snowman = "&";sprockets-4.2.1/test/fixtures/encoding/utf32le.html000066400000000000000000000000441447572140400223310ustar00rootroot00000000000000<p>&</p>sprockets-4.2.1/test/fixtures/encoding/utf32le.js000066400000000000000000000001141447572140400217770ustar00rootroot00000000000000var snowman = "&";sprockets-4.2.1/test/fixtures/encoding/utf8-charset.css000066400000000000000000000000461447572140400232100ustar00rootroot00000000000000@charset "utf-8"; h1 { color: red; } sprockets-4.2.1/test/fixtures/encoding/utf8-charset.html000066400000000000000000000000121447572140400233550ustar00rootroot00000000000000

sprockets-4.2.1/test/fixtures/encoding/utf8.js000066400000000000000000000000241447572140400214010ustar00rootroot00000000000000var snowman = "☃";sprockets-4.2.1/test/fixtures/encoding/utf8_bom.html000066400000000000000000000000151447572140400225660ustar00rootroot00000000000000

sprockets-4.2.1/test/fixtures/encoding/utf8_bom.js000066400000000000000000000000271447572140400222410ustar00rootroot00000000000000var snowman = "☃";sprockets-4.2.1/test/fixtures/errors/000077500000000000000000000000001447572140400177075ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/errors/symlink000077700000000000000000000000001447572140400237062missing_fileustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/index-assets/000077500000000000000000000000001447572140400210025ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/index-assets/bar/000077500000000000000000000000001447572140400215465ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/index-assets/bar/index.js000066400000000000000000000000171447572140400232110ustar00rootroot00000000000000var t = 'test' sprockets-4.2.1/test/fixtures/index-assets/baz/000077500000000000000000000000001447572140400215565ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/index-assets/baz/index.js.erb000066400000000000000000000000351447572140400237700ustar00rootroot00000000000000var a = '<%= "a string" %>'; sprockets-4.2.1/test/fixtures/index-assets/index/000077500000000000000000000000001447572140400221115ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/index-assets/index/foo/000077500000000000000000000000001447572140400226745ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/index-assets/index/foo/index.js000066400000000000000000000000171447572140400243370ustar00rootroot00000000000000var t = 'test' sprockets-4.2.1/test/fixtures/manifest_utils/000077500000000000000000000000001447572140400214215ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/manifest_utils/default/000077500000000000000000000000001447572140400230455ustar00rootroot00000000000000.sprockets-manifest-f4bf345974645583d284686ddfb7625e.json000066400000000000000000000000001447572140400337430ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/manifest_utils/defaultsprockets-4.2.1/test/fixtures/manifest_utils/with_two_manifests/000077500000000000000000000000001447572140400253365ustar00rootroot00000000000000.sprockets-manifest-00000000000000000000000000000000.json000066400000000000000000000000001447572140400351140ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/manifest_utils/with_two_manifests.sprockets-manifest-f4bf345974645583d284686ddfb7625e.json000066400000000000000000000000001447572140400362340ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/manifest_utils/with_two_manifestssprockets-4.2.1/test/fixtures/octicons/000077500000000000000000000000001447572140400202145ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/octicons/octicons.eot000066400000000000000000000000001447572140400225340ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/octicons/octicons.scss000066400000000000000000000005531447572140400227350ustar00rootroot00000000000000@font-face { font-family: 'octicons'; src: font-url('octicons.eot?#iefix') format('embedded-opentype'), font-url('octicons.woff2') format('woff2'), font-url('octicons.woff') format('woff'), font-url('octicons.ttf') format('truetype'), font-url('octicons.svg#octicons') format('svg'); font-weight: normal; font-style: normal; } sprockets-4.2.1/test/fixtures/octicons/octicons.svg000066400000000000000000000000001447572140400225440ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/octicons/octicons.ttf000066400000000000000000000000001447572140400225420ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/octicons/octicons.woff000066400000000000000000000000001447572140400227060ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/octicons/octicons.woff2000066400000000000000000000000001447572140400227700ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/000077500000000000000000000000001447572140400175125ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/all.coffee/000077500000000000000000000000001447572140400215105ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/all.coffee/hot.coffee000066400000000000000000000000001447572140400234410ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/all.coffee/index.coffee000066400000000000000000000000001447572140400237560ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/all.coffee/plain.js000066400000000000000000000000001447572140400231370ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/application.coffee000066400000000000000000000000001447572140400231540ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/application.css000066400000000000000000000000001447572140400225150ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/application.js000066400000000000000000000000001447572140400223410ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/application.js.erb000066400000000000000000000000001447572140400231100ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/application.scss000066400000000000000000000000001447572140400227000ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/bower/000077500000000000000000000000001447572140400206305ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/bower/bower.json000066400000000000000000000000001447572140400226270ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/bower/main.js000066400000000000000000000000001447572140400221000ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/coffee/000077500000000000000000000000001447572140400207415ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/coffee/foo.coffee000066400000000000000000000000001447572140400226630ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/coffee/index.js000066400000000000000000000000001447572140400223740ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/default/000077500000000000000000000000001447572140400211365ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/default/bower/000077500000000000000000000000001447572140400222545ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/default/bower/main.js000066400000000000000000000000001447572140400235240ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/empty000066400000000000000000000000001447572140400205610ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/files.erb000066400000000000000000000000001447572140400212740ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/hello.jst.ejs000066400000000000000000000000001447572140400221050ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery-coffee.min.coffee000066400000000000000000000000001447572140400241770ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery-custom.min.js.erb000066400000000000000000000000001447572140400242160ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.csv.js000066400000000000000000000000001447572140400221470ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.csv.min.js000066400000000000000000000000001447572140400227310ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.ext/000077500000000000000000000000001447572140400216305ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.ext/form.js000066400000000000000000000000001447572140400231170ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.ext/index.js000066400000000000000000000000001447572140400232630ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.foo.min.js000066400000000000000000000000001447572140400227210ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.js000066400000000000000000000000001447572140400213550ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.js.min000066400000000000000000000000001447572140400221370ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.min.js000066400000000000000000000000001447572140400221370ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.tmpl.js000066400000000000000000000000001447572140400223300ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/jquery.tmpl.min.js000066400000000000000000000000001447572140400231120ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/project.coffee.erb000066400000000000000000000000001447572140400230660ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/sprite.css.embed000066400000000000000000000000001447572140400225730ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/store.css.erb000066400000000000000000000000001447572140400221150ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/store.foo000066400000000000000000000000001447572140400213410ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/paths/traceur.es6000066400000000000000000000002031447572140400215710ustar00rootroot00000000000000class Greeter { sayHi(name = 'Anonymous') { console.log(`Hi ${name}!`); } } var greeter = new Greeter(); greeter.sayHi(); sprockets-4.2.1/test/fixtures/paths/traceur.js.es6000066400000000000000000000000001447572140400221770ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/public/000077500000000000000000000000001447572140400176515ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/public/compiled-digest-0aa2105d29558f3eb790d411d7d8fb66.js000066400000000000000000000000521447572140400275520ustar00rootroot00000000000000(function() { application.boot(); })(); sprockets-4.2.1/test/fixtures/public/compiled-digest-1c41eb0cf934a0c76babe875f982f9d1.js000066400000000000000000000000231447572140400277750ustar00rootroot00000000000000(function() {})(); sprockets-4.2.1/test/fixtures/resolve/000077500000000000000000000000001447572140400200525ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/resolve/javascripts/000077500000000000000000000000001447572140400224035ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/resolve/javascripts/bar.css000066400000000000000000000000001447572140400236470ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/resolve/javascripts/bar.js000066400000000000000000000000001447572140400234730ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/resolve/javascripts/foo.js000066400000000000000000000000001447572140400235120ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/resolve/stylesheets/000077500000000000000000000000001447572140400224265ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/resolve/stylesheets/foo.css000066400000000000000000000000001447572140400237110ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/sass/000077500000000000000000000000001447572140400173445ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/sass/_colors.scss000066400000000000000000000000371447572140400217010ustar00rootroot00000000000000$link: "red"; $header: 'blue'; sprockets-4.2.1/test/fixtures/sass/_header.scss000066400000000000000000000000361447572140400216270ustar00rootroot00000000000000#header { color: $header; } sprockets-4.2.1/test/fixtures/sass/_rounded.scss000066400000000000000000000002371447572140400220420ustar00rootroot00000000000000@mixin rounded($side, $radius: 10px) { border-#{$side}-radius: $radius; -moz-border-radius-#{$side}: $radius; -webkit-border-#{$side}-radius: $radius; } sprockets-4.2.1/test/fixtures/sass/data_url.scss000066400000000000000000000000611447572140400220310ustar00rootroot00000000000000div { url: asset-data-url('asset/blue.png'); } sprockets-4.2.1/test/fixtures/sass/error.sass000066400000000000000000000000451447572140400213670ustar00rootroot00000000000000.foo color: green .bar invalid/ sprockets-4.2.1/test/fixtures/sass/import_load_path.scss000066400000000000000000000000231447572140400235610ustar00rootroot00000000000000@import "compass"; sprockets-4.2.1/test/fixtures/sass/import_nonpartial.scss000066400000000000000000000000251447572140400237770ustar00rootroot00000000000000@import "variables"; sprockets-4.2.1/test/fixtures/sass/import_partial.sass000066400000000000000000000002041447572140400232610ustar00rootroot00000000000000@import "rounded" #navbar li @include rounded(top) #footer @include rounded(top, 5px) #sidebar @include rounded(left, 8px) sprockets-4.2.1/test/fixtures/sass/import_partial.scss000066400000000000000000000002141447572140400232640ustar00rootroot00000000000000@import "rounded"; #navbar li { @include rounded(top); } #footer { @include rounded(top, 5px); } #sidebar { @include rounded(left, 8px); } sprockets-4.2.1/test/fixtures/sass/links.scss000066400000000000000000000000561447572140400213620ustar00rootroot00000000000000@import "colors"; a:link { color: $link; } sprockets-4.2.1/test/fixtures/sass/main.scss000066400000000000000000000000431447572140400211620ustar00rootroot00000000000000$header: 'blue'; @import "header"; sprockets-4.2.1/test/fixtures/sass/nesting.scss000066400000000000000000000002171447572140400217100ustar00rootroot00000000000000table.hl { margin: 2em 0; td.ln { text-align: right; } } li { font: { family: serif; weight: bold; size: 1.2em; } } sprockets-4.2.1/test/fixtures/sass/paths.scss000066400000000000000000000004611447572140400213610ustar00rootroot00000000000000div { url: url(asset-path("foo.svg")); url: url(image-path("foo.png")); url: url(video-path("foo.mov")); url: url(audio-path("foo.mp3")); url: url(font-path("foo.woff2")); url: url(font-path("foo.woff")); url: url(javascript-path("foo.js")); url: url(stylesheet-path("foo.css")); }sprockets-4.2.1/test/fixtures/sass/relative.scss000066400000000000000000000000701447572140400220510ustar00rootroot00000000000000@import "shared/colors"; body { background: $grey; } sprockets-4.2.1/test/fixtures/sass/reset.css000066400000000000000000000001561447572140400212020ustar00rootroot00000000000000article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } sprockets-4.2.1/test/fixtures/sass/shared/000077500000000000000000000000001447572140400206125ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/sass/shared/colors.sass000066400000000000000000000000171447572140400230040ustar00rootroot00000000000000$grey: #666666 sprockets-4.2.1/test/fixtures/sass/shared/relative.scss000066400000000000000000000002171447572140400233220ustar00rootroot00000000000000@import "../rounded"; #navbar li { @include rounded(top); } #footer { @include rounded(top, 5px); } #sidebar { @include rounded(left, 8px); } sprockets-4.2.1/test/fixtures/sass/urls.scss000066400000000000000000000004011447572140400212210ustar00rootroot00000000000000div { url: asset-url("foo.svg"); url: image-url("foo.png"); url: video-url("foo.mov"); url: audio-url("foo.mp3"); url: font-url("foo.woff2"); url: font-url("foo.woff"); url: javascript-url("foo.js"); url: stylesheet-url("foo.css"); }sprockets-4.2.1/test/fixtures/sass/variables.sass000066400000000000000000000002571447572140400222130ustar00rootroot00000000000000$blue: #3bbfce $margin: 16px .content-navigation border-color: $blue color: darken($blue, 9%) .border padding: $margin / 2 margin: $margin / 2 border-color: $blue sprockets-4.2.1/test/fixtures/server/000077500000000000000000000000001447572140400177015ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/server/app/000077500000000000000000000000001447572140400204615ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/server/app/images/000077500000000000000000000000001447572140400217265ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/server/app/images/logo.png000066400000000000000000001236451447572140400234070ustar00rootroot00000000000000PNG  IHDR,K9tEXtSoftwareAdobe ImageReadyqe<fiTXtXML:com.adobe.xmp pmPLTEjjjlll ޼pppzzy}}}uuuܹddcى􁁁+++[[[SSSKKKBBB333<<<$$$rrr뤤렠nnnԘ ǀ||{xxwppnnnl___rrptttlljggfOOOWWWHHG777///(((g-IIDATx콋[X>@IQ0Y5QT6rDDZѢ>ޟyj=_>vi:3׬^{kmuM/#_/Rͯj~巖_oef~=jK7#(sK7՜EEEMj" K'DUEZTt$A<'j4:pɊ(x~G~ 3y;m :~W\RA7t{ ÐYT0*zV aTiNFw wHoF%ڭ.u^P]P3ڳa TA7Tc5#"x&ͿپKLST>² T=T,ҽ*i hF&>ͯl$+2'_k¦Pdx&)e ,5Z-20$uQ8,!L|tg+~U#0_A{IDނdl]uvFk2߽?(jʜzT_nײمE'uK5烘L&߷eK?FBO"fW0[[Cd .X4 bɐKG-!c3'dN>׫bMS,1י7^:)aYSPS}TC^Qc*tuZyrA e{xEm2„#F.0Sa穊ȵ]5! hॲxej2,i"V{xj&Y^ga@pUm , b,f٣op'p7:_k(xnk|X"0e>Jj9+30ґ׾^f.=s[YsIT{><1Xͨt6ܻ}:'.&Go:fSci80b/( |`Օᘷ!V$U^i>3vڕw&߶gbkS9#OU | jՠSuz66hß6_w) W媩HZDX0$IUv2 GdC'=6F9Fe zxpVbLJ Q8um>8pvNk؂-skͪ돈,"oƘ Jeq|d߲ {F9Z{T=Ea] ' UiNM.O_xY*6C(ӄj"~b>UᙵTUc0=! 2\or>geP*(ź (kk)7)(`6:cWodqb7'ۦEmܝP/d/V͖@qoC;W 2T,Trļ ZK=A ?b|=Lfnb č|x!ف|ܰ ߣ!)·l^'Cn_7 Ѓ ? s?[C@^V:®nX\f؆;>(}U $o|-Fz#FpGی4›։BS[og!؆86?|H9w z5&MxVU`G8$]sUЄЯv[ HG{1;WdR`j"WR8,坦@oz8s x@RT,ZX)˲^BQESTb86|'hM?OmcWOsUѝkwlogq` %8!DQ*# C @0!R V T0 ژ @w}3];\\arsUӬB;{pNp`G]o)1w my[~{=ҹ`Bܶ=C|sw:蝋udٺڝRtYˮ݅iZnơ-by̐Vhz0vfp}uU="WY߀ޒij2uגiWX'C zk孾>oy;O/3@;.ț o7t$ CZRS'\@|3+y瀧gTO>vZ~Z}Li/23 "E4›G o: ?0K`oHc*k鱚r,=Bi*6R+9 D\*$eD1W]m঻XbY t|OyG/Vv u|%3<e![7/Lu sA8bne6Ab0ͲaD4gYjy xsȲ3U?2۟p@B/a"ץj v̈́Pg># :g7г<9&B[LO ?t>;zwEH& ӑ-3]# |D `HT$]Hj 'fQ8m [mli9}+t݉kJ' Y[>gl"q o/fX' (vmض6;ml~ۮS*sk<#X$MBL xEXm'wQTښ1{.#shl9:B7*cӴ{v-g̟Lx-m@hۀ́0aŸ1έ-tfvvFIF8i6Ge4^X3:kb51?Y5do5HU3<3 V3ƖE1`t<'@ Ba|0S&$Vba0=.]>/\tu)V\OKׅF%ݛ|pmaR4V]Iš!Mŋ T=MbqLlwŰpfQgcv&ۼ!&0%¥e4${ՠE W.i[Dhg!1G;Jw ^%El ,oj(&*Ĺ!imwv47 f›%:z6%`;6yJۯޯ>`uW%tPhLFph<^|FIpDROUTtI\/Q h٤TOhb=nZ6aؑ f`*Ithq:Fs~$D[~{e$J&OwW*og:)6pחs}(_XGܗD0qosڶP9%w_ vsN8gݞ[ߒ϶F]3K}3s 1ITVWDPq syݞz ۠pن@zpǁl$p;뺳U[;|Slr%ݼuIp`MF8 @xFh\O`Tmr_=t/[ {D`{FzJ}cʯ%gV_[a:Ge߀LoVWQ`0Iϱ~/T6zI@ 89acGwwΤgEz=p̾A}P?2Xz6k'Fvj|8*sO}%xKa mD>*2l8nx|h[zȷ{x픧fY~m^ϱ{;A(=p x6Z@ۡB-<*h=SU HST9{l\*PEƯo1n/cE0Ÿ"r~]Ax;vmB[+x!xt7k{Å¥m`!W(E(vglmwbvuwk i8?o?`jA'1"v77]_?{7CH |v ZUfܹ϶yOϗ! *qhg4mWw[/8Ȅʇa }ݴai/H,xnXf_ٸ %Ɩ-loApwvmK=[?g "" Lr[REҨ2u=^FVxde]4B9hG!77}G ]uv*kM|(Y^Eވ*MFnr06i#b@o,g=sø{/ 7~ܠAck6ModHAD$U:4MߊĞU, 0\P9u ťer0x̲, ozXvZ#n?._6aX>ZN/Ev?HWi?$;75uxvvX6Cc=pv{)ve=se}=hTE7RUHD:6Cu_CJ!w5zRgx&=cA=!7}~|uٳ~R!UE$֢6&]f `ai:֜_I߇mvέpMD3[Eo)Daw`400[N7B]U8a ^0,D57j l~[Yݮr|9!Bs&3cL@n6VAehnBRa4xk<ڙ@HϻԄ i#?Qiz q{<Ʊ~a^F aBE'{U&ZjֹBƘ"8xj of@=nu'̄tIUb+oSOQqYlb:J8}!h)"HkDCJv0Z%a/U @5ׄ7yu~ o^vK$qQ􄢉 TCMXVz  M(dR7Gq)kTf-fvJcR= ]mv?b)agb8c@'ԊM9Eoe:-g˾A7O|ecvq`*]3OnSWܰ\G! >mGbw(=lF흧۴-z꽟{Q(e%,Ih-*Q XO#93䂌"t56 _x@`!|vx|w'omm葥jIxqZdQsxd4M x=;WڼL܆D/ƳмwIfUx ekHxyfT6AO~'b!ȫۄ2,@+2zь͞A/M(F&|W?hosaQaMx3f H}NMgs@x3eDxs1]z 9<({,}Y {WE-0Ҹ;m#a R; ͆Fгio66m4/-[ 0MaO $aj:0Ȱ GS\pT5j -J [\'+1&6(P?{]qZ .tUE͌zL*ãSa[_}}FrVQ@ v (l&2H6&4 Ꮻ<'U y6Z 1m숻SĒIMKd):j+$!K0hƅyޜ B(VnNQ8Q5NSJ7ϞĪ0^"jQ T&J*@Hsf$NLȏ7Kd #'.jRĒ7L,eMx3H73Ɛ>ah 3(wT^ &h:$ Ah" g 8(j и o~@*g KuRb/?M8 b,2x( `U3!ܟ9|7K@wY3)9 gMTj x YuGPc#RZMVPh(&4ȿ,h//7D>߼@on7m`n1lm]gtN;ڌĶ{s4:r>M`ݦn8BBX;]kz 9YՑsƉ4i@fD/qB#D ?A5q~2J?hC֗Oӊpqt-a; ,4ÒA r 4f|hװN:@͜CxOo3]%FO O5FǛ+s3W"5f/B=l4sUtИ;A/0HO AhB[p4AEgeD;sP97Tn#(+oTx JS'qHvGص(?6iP]KQg8cJ+1jb[5N"eCpҕu*x,UT?JMvov}!zP}*v(|Ç+34`8##=bU0Ûb^5/&T2XV 8LQ QA8µ}x60Xm|?en% Wr5$Ti\?(9~P5tRp(s|}#)pBh8Ni;onhV._.`jpƘzӤ8lll.TΓǏyoL%dj, EB`iEx8}7(ik|ѵh{ݶ#~&㏲naOS}={y6xK"xEZTi>[&3&X*G wtf9 _$fF1h07!;u u2A|WSjo n›{%JIX/oKƽ-] ؍;[ێBg4Bn `;:[VVϷ熆:SSD-9lxi6Bf)ā=#@،1DY#Gs尭=ok裂$pXRWNM ѳ]{sp>%aN%P!FہM Y ۴9s~ar RR$P'+Ffmޠ;5Y t k;Zp₩!2(,@PT]W^2gVA@V-p"D'R۔ȑzi"T3/D^K"StWE"|A r㷓|nR VQ$o9;23C2;TRfd)!qn J&&Ʒx Paq.K3)/[o`[xYSq\U QV#4qԿ$Cs,~&|J£OKQS/.``)jf¹; xXYֻFQ!E1Yj#&eadȔȧɬxv潭-˂9߯z'p!J*9 v\ʶ.t9#6EJq{uGclИL4|.ʽu(L%;"Ř ,p:رjr2]KTqhs"3WpzLL? !pQhogXԩ#_#зP.OωT֪|O#ޜJ}zcOyj8׍}T{uA%xXj1t za|yIAl8'AY+Dt֞_|NRzbU%Y/Sk5»~MbkTx7ڞijqD,KT'amk%9b8Mn}Ѱ146gr@9YfagނR_Aߢ48amL$|}Xu8m,<.negfAp(rKدՉ'2s3@ުˁ̂HvԁU v4[XEGdIR9}P{ ö}k)u۳agล=q'i-Mz.ސD5#!iಆ/2(3uB՜M .OosOw!:os|xo#B =q~Xc/ DHN??Gk"f %#kWx#+)=kyWRmF3#X'mܺl76Aw]S*fl@Q 9.1ubHj%8dNgsniw+?i$~{KPEzM/tpN׌9CcNOw/'{gݲSIH\jyDg!z=dLco}˅8X*׈h8>CzLZLQ1ɝhLoooI o4XWVsJb\ 6\o)|}j%<?y1ȸ_37/P=IsߝBlԂqp櫟GTlvg]ࠫ~0_Ϳԫv#DDF /+NbŊ 'M[ŕ L5H~_Eu}9j$bv=e>琰i_cm@xs/#0ß7~3+Ҩds $qu=L\EՈ Z/ʅAwrϦ;Nm›#|}v.$T'-42oˏa\iLT=+2- Ͽ,,9S_}j`udRu}asw8ѴG2j96ڴhpCLvMfCU2KlNdj£R BX37Y?b›%_Є7@p}7]w7e<MU+ a4֪'~D&)C0k2!&n+sVZeտW^%skgoANM7MQN]cl>kqx4dj$gNQ1(ϾxIs`Mn9 xHC^cg-`›k;(OapcQ@f9h[B1]K.j_s kT=':Y]yhzXv2nFtcDy{d9%ivDǜt]tG^ޅF hxTExzY8?S  i>׏N46 z[T]@vlfBDE -jRЂ/@[WI,BKž8wh|\LwcSTu"u14bO47e23pcuA$5D2+otQ"X>T{Z](#y/dBw֊]N ]M`,u 6&___kaVLTLdq[u(X)εBNWߋR1ڗUd}B_Ӕ~N쾨+U!UUTؤͦJVFm8w` LΓ5+b9BtF곰ə'dq##UHldEL .KjV4V\Pȸ鮷@nQm5){8ji\\SULNg\~CI ;sNfO6|G̢-k#[Z|ypfT&SiG.2>9asI@I:ܐLfiEΕC0&fjQeE&wͺ ̩YbYwG NN1+{z q~ LYژj_|i vU3KXHt'%TL;;26#2",WXA@bN 9AXg*m3Tc\ O@yGvT˲XbaR#Z5xy kG̋_fʇ,]`/9o`ݓb'1kOca^>$n,ʻ䡈c^Y-ObV+J&z)y;`dOQ7y_CXVjYF*554QѸ-Vo^|5, rݤ5' nH.}C B !14Q k ;s̞Evp=\p@FyuZCR"kM{eKd7]m)&^dDF)*r˨/7F@dZ"S(hƠ ^c UwBzf=e: RcluZPrF,0"!?f[cK"C 15RGg 59xJ03` I_;"C a%v |Pg"Ef.vVly?ԓ`ޗ9J&W[HYDUƁL5{`gtYXxITcs6)ޤR3O=a4T6"fᡃVWOz#<*>0W:C"DQ8AMT׺Yod~;VfU.x}Yąz(.W4K?O59'4 :]88.9! 5ikx,RcK!!?bѓm، $_>\4`I]zk,>ۦpfV/+SR}WlB@g堏=ZtnjEY0Lw5nFkp Lg`!/"ZM5ē w }KaJ7Gotk2-޴s^|}l!j%^GԕG>X ,Y*24W_cOس).ezV ܼZ}S) q\-kl1|TUƞ]x\XUR0-rX S 岌'v;/ Ւ9Xa/ 6y(ijbu] T3٭C{Heq2)j'9i2 jH>xU -su&l cA+fdDhAI0 ly1iac`I=mgQh'ʭw"%.H:^.3n@P;zWͣɛH70w],۝^E<|sזᶨHsY1ų'hŗ?ϒU NPB->Ym A /7<5s:x5-[u Nxfg2l `{ęsނ 2S ,"' ,2w[u}=|o䬅f>"/Ջ3 [0r][OsUC '[ "+Dkŀ QL3|д¿Q"ZJaոY iÄjU[qX5vGFh82>dϋ+A}}ԻYrLG*|]8Gٕ +%O z!Hu_9t[d= T,hn?Iab_nɝ8>ZDNQ[1դ&u r'psW}%~}"z35:W1UCBE%y<3$mӤeXZ!džjBDMޒ ֑::xpSV,A('@N>iYLwdQT M>~]+aןV ?MIЄvwxk3ɲ H6hh C'vpbA<}U;B&^ہ~JJ:QMIG܊ ۞M?S3 S 9㑏et 9uI3hҟUM71 `y {mC70EMU)yȪyBOj߈$>0vsykHo<>7w`4m22UN4u߮Zs[:$< 7[Z膰W-82 !W,^I98WdCl>=º{v=o|C[.ྷ8}I9̈k4XdTSV[xT@\0jEaI,]`hq#w0E+}t AzpO^[:[FcDe:oQT#c! ӱX,QzPZxfmquv*Qoh$4"}}~Ot 9a:^$: X~ؾtLIVǔi: e,Z?_MtPDdg9FjtvZc?Jf qRoV'}l[gS2ͮө 'cidF$Tb^LYOgP_FHF5_;;~홗o|XS)x ߜ֍8>G~h g& ~)pRE蚠0;S 2D*YdD.y3_~Y|]?,\lg.fËo̖8Y 6PMCz*Z՛W/za Mdh̾k] 0hR.L۲Ju4<%Sy|R&kn=8^X ( jk;pk'rEV&ao87岄D l~InةgG!A e^:"W$5"N]w\3Ul\ĩZ`4uGl:_Q5 UPT6-2}>_tjA*Շ&'p{ RY,(|äCx!tfG-k5bO>bi~.(g$fFcki Ɂ$ Д̀2.XxzC͇|X!;4WBΐ1V.W/)b ]7hAkkxs'/2wlmxHyEfZi:W6=r:\b]}-p/K|PGt/x02 -k[TXlDM3fSA"@|``p(*zPIJm]})Y+vi{Szo9ѸFT9 䎅bec{Ƭ~M38]"*)ԵD0LrK( -G9_0͐_s&Q5fVc*\۾٠U*Uf䧻Yjl*fxjcWI5vr ,,z)-dz,'+Ps(mYd$6=Ȳ;,p/6$ZmԧT:/r wF7TMjDj/[L cuK+K2yx`@: ! <EL, yƲ8,zٛmAm++hOyX7 KEE]i̹*,ΰh;r<BÖAHwHqaUhK}_kvom ؠ+5,J"'_wm0&{猿93ڮ~ߪzުd@ I@}'ԇ %f,hiP'q&k䝋}41s6 /LyscV u73y4Dd<]ͪ^ut45_$ob^ lzQ@K/Dwj5||G˜Kh,Fa:%gJ-@Zn"ʻ4-]!x3гPrޢ߇Z)rHo{07w9[2oXWoYt?dBk^kl&Jy~ .~>+8e]q Q6>ۭwbYLfؗ\Q4ɕH61Mk7u9phsޡMUC5M#EB %Z+ĩ 3*l\pq8.&)TࡅC K#%"(lAQHrda6MisΆ- s_rㅸRJ,',_E34 \X'[4M}&o[^Z+ei& ×I? $WmͧtH9}1Qh72qK%=*9c[.P,O|\9 b!W MCI|TXګĿnd,XcF %<Θi⣏t@L 84+nhY*FS vw ؅Yo  ̳1SvXXӐ|n'lJ-~&nӳ)Xs z sU!'> 6ՂE#NW#v 6]q7M @SFIȕeگWCˍ&d4F3wi&wXn| /=§J4M}Jh|TS_=y` \EWNPm:BvRu=#|V2zY K2g~u_")adc=OIC6H,gq>bW-2Y=OyYa`AXB.`y r3`{X5W_͎f%8c'`!u+] e 囕T;>@Z]tznIx'IiQWEbPN;QzO0$1]_52:ſe&Az)kfCo6y>)7Jh˨xoӛE*~ؐZ/ei19Nq%Lwfx|9fx1h((ZEn9uBВgw?s()?cuiꒈ.KKv3HmRS4ЭjeIRgdH5KA-lrwYCz3~DkҒIK=s~LRһ )d7<Vtv20dztK~qB$*pNWEJ fL Φ& -/S*9} X):>TPQ*6F䎺nI^lH61iY!@_x%fC k\uoU|xh4 uߗIjql ?6MH:rx3LJΉNJP Xyo: gD=v,RP`N_m`P[jjUk3|yi[ﳩաWXMCA~Q;i_2np 6I ? <$〶= ~JXkGV tn (?mςh\3i40,z3fЛo|Ebry wN1r41KXrY^s& :NQϲ%GY>("v,km MC+%й՚fͰІct+C"y=+j: qǁpޤl_ЛICEpے\p`ޡ7 I&L"P*퀛fd4o#rJ؜DXz_*I4LƻyBiQ@Nw ϰB)vNX嗹6gi;%Fo\X,FFo+6UkS|^$[HS+]>GZ֡#Qn<.}U#1 յ,XwBkY:R*zZVEF ykfJ\qǺ42k?~4ZEׂ6k%T! 6oM4frTzqqמ*kiB@0FNZZ8r$CvSoR?nIj3ed:xdDV 9o%*T]^F͡(=~49k)[=#~Çs>fNGJi7l/j9Sx?p_!5,_q@&㱉FWub<&:-jY!Yhms;$wk!FKs7GirpónirXҮRE< fb[72W>Uo>hDzKEٰzsF]%;RRow!ab.{]E4Yq@uKjbIA\bciu"iN5H<<;b~`vDK'pE.`I&81lyQm!޽ DoSiZB̑"lÕﻦFR^t:d=.ǗD8Pt[865ih7P^d!PQcV3ڗ^4;WA媊&IKi_|m)>(?+?Ж64D\@BY"ы]SYF\QWkKH󔼯 L3ÍM=pF@vhQ_]!B#m oY&'?'_m[ov8~׶wG=q-ҟ,}4V$f^lim8a$7MjeIӰNI$%iI+<&3S8%xJi Z[rR\8`WB?C|o5~DhUZMf$AvLM{&߈48YWgX3-[j"H$u-c h{83@l)%daM9sxlƊ#De(|V`#G̸[ $r Tl8lxܘw~ ĠlW`Ko#m ygQ@!Hq"x&%Qcy/(?cٍ^9x8>7gՇ,b=ˆjn\3ATJ>|f#wH놜PԢn4oB QD?Mz=2o.>TKw{UM$XiFOԆpG '`+ɤDpIhXx_D }#kD%r(HhiɖhS1%Ngj,4ײd4uJ>Л76FL[ <83Wӂ& ^ =J. q&WVrP/dՐbauCaPXQ]_(_!Z r" As #~-c:B ^ӳ՞$!1{(fMA@=t Z⹩Q|3#7z5h 9n[٫c+sju,[WmOcEܪ .TYˤhkp"ug$uX#çٔX.iKwVG2RsРgHE7׎kP]UơZF"[67HEʓiRGDiR 5Id4ul)+B#{͉ةa|2 pN!yNo0KX`]IQOb*iAU C~5G&߽\8Ms6ۭ&Z`[;4g5{?$cN7zhb~bncb7u 񑦵LyIMQ԰wBDZzE b.y]* 0,#bEJĮG WJ$j;{ML~G$啵g:J$i ĹstXPh:kEśv8xewl{l;Ht^JȮ1ҫPO&n lx(R@9رa>9 uX=ƭUJ:!Y&GMJ({ zvYgG.i"+_mev@vcymmYVILowkӗdȫ% 7_ / ט& dӬ҃꾀n7c^Gz33~xhMD=AYg#$+oU6Ud4uzu[4TZڬ|PWYY$?ջ$FŸ2XT )27/Vh aaяe!F0—GxPE)ɝJ ;!dF57}5WҏDEfGpRi Üb€7\2\ZZe;]'-8\e2Iz9NfGz/L{2Et!NߘI:oKL3eD$.[|smn V:)v~E< Q֞=6H\|״x]4:8^`ch,G3%\4 II#oc\֘OOtjj?~OwZϯ5۱AiC#,%l5/d&&CWkRI3 '}m{oѳ7S4<_s WvBʴ?yڿXc6D^3zshaW-!`~hMwΕ-쇷hvtG%?ʻ1o'&XPo>74juCVU__8=kMU_􏲊R5ͻ}+'8i F <ݳk""BQ\e`*lh=h` ;[s`s&,iJ跸t璷e'GY,18֘ >8z$`ҹ/j s3\FC'9 8A#$3y_>`m&Ϫֶb&foeި0gy 4r&]57UZG; .{مwUXY *n5zy(H" ]d+]_Ci=]$9rCQu$bsKEuNf޹~O?ec9ЛtO&m<`t_-{/y OETOIA zA혧|f#D(]hc&zp/}^b{ s\C_sAMfŗdJgioZ+Cj 3ZAr*8]-A$_}ez)_/Bt"Evlħ=Ch';󷅼hW쯛q)?˫y}L; X7zuE]oZ*a>Rl$Y>KŖ^pvc}h.G1Qs1K<Kn` |ٸD<"6'P50So+Y~bci95_#v`sX`.Ȫk&|mx4X']&uTsx1"]o's C_yӒL\QߎQq_7 F͒ AHn)Vwe <r3vʏ!@y6tXXQ1N`4fC1enm`'[T OC Phš(ٗW##WA {> 3(˲EzӪ}WIM?Hnb' !p9yݍ⾧zQWپPHS& |$EۗVB87}Z;/QMs@c3҈髋nk0UlVV6#۞ƚz;^%ie>揅GMyo5 hc̺k: /2796 ZSkLcia+g>^8=;m3!O)i=5e.8 udn}y.'e7ڠLF] p+h 5Lcȝb өRI!:Ib[zyUJrﯚ&C4{y-M1qMlF3hDz8ٖs p l~pf)ϑgN;9-jc&9L08ۃ ^h-ka?}YC>@QNuCӱE액1r@EuZ85ڬz.`VmVv1񘋺h0z3`suZ!\y$+:g=һ[4ޙu^Az:tCݾr+1YzпWLDvo RjHr UAa[ æ\gbm*d&4`:n'Vi<.2BD3h1Z6>I5lhYЛs(=rܫe33A悅k۲–z*\V7Ul 5'0C]2qv nd_q-C Ov4F$ʼn_0u'n&A9 nvj .}$ {ЛMB1y &E!NuMMVTʙF ;x~+^ 80`CoNi^47q.0\9 Iz@\\eaWߵt 83:g6(;%6!-yBkpXH*Gސߠ"F2o>Gm'zݓRy4 yd(綰ZUd4YE,]$3;$>k0:Y*cWA@q_`1wOxG˜O#Pa=1 \m#p͛-̝ᛁr٨qojp~ӖS~M]/f7'|"Xm 6~6$|9crK7UZK-Iaͬc);U!ԡ7MH7 YAz37vDMBoZ`CoFS ꔾ%[ U+J_޹4FfGf IeI׽zϢ=se^`P F~vOrU3;u+}~ߝ&~W;9Rݞz"_ύ/>;j4SJvC?gMvyrܨ鶚>psG0 ڜWݕfml{AVI7#7cGۃA}pͱhfncM> lo6czcs;>pLxn"H|6,Fܪi.3Fyn x;u3[j_#LM伾X̺'& _sF%FB3X3^N}D͘#L8A9VSG.x49}| 5Zw8:\Ӕnx6Jڭi|QC*=`T r]7ymz3ڡ7( Ku"|tT[/ xjHoXhT[!f!nH@ ^rw=j7; " tIPTuiIa>c'Lw[6ARU-ٛyCEnwP,jzJڛmsEVe& ;rj\3gGgA?r7uif WMfsFU_qFZ3:4T]_Qq[3[Ztc9u{ș5:=cfh,k&X@u{uVUmӢc0|w]8MD'"hiK {ҍٰIӦ)< RvhgMk9v́` V30䞰 fmh$, @٦7y qHoބdf4]NIg6f`K k> ̥3 \~)Ѵ^l-` ؑ뼄 \"hA,B6*)- nA&1A U\RExr)%K I𝩨S~4uVƕ ٛ2|Gِi .>g16uf Xt` HH7پ#}>qic.OuD6ty! f=?0PLTͩ, #ۡ3~+g.^EVt"aܺ1+A BvD7#5n-謤i%˫貾 Fc{~y9\>\>"ɁxVd2 >1Cϭix=1|uJ+gGg|xrZZ^\xVi4Wdi?w CHh& `psgǹpLJsF%.0llC!7ِKP ܐ>zqJHpBnP񗋡^u;S +7![e fPE`t^K_C:p/vߐ[ѫ:~EP 3m%TO{nENzS%镝LA"V/k=y.u5 ` I1خ%?9 QԚyV󉚥pmL\6̫M*aTs [1Z\vÃñ9SP`}FflҎBd'UOࣵ/ 1R;AuS+H``/{JU2 psaq:ׄDIVy0Z5͖@2Y~,*amʃD#%<,Žm+*% 3L(_`}낡(f3e8KQFhHPRAk P Q8bu@a\@mQ3ru /=RU8e{ak奤DVX>ij,lak/[ƭbއ%ILZA3tµ-C2a쬐ꄦB> ~rJi S7i +v/ßoT44$f3#;Ja4 2dK5,` ;Lfb1_Z0OJTQ>)9LC~i LB11|< Uؔ(" :D칄Cm[ =Ic˖P4#RC됫%]MdM#nцEt#idmB;<3R@}K{C[5@f"`tw:6u#(~ u,ۿccnl`onh:#;VLm7$29c(*;v?)X${;c0"ѝ*so,"bN 2}H_1'IОWϟ44V$Q[)%i+=,/K$R'O Fd ILqM{B:W6Z .'vyӆJԥ/l*J^-2Ο6Dk]z^]ʉ ߏڽ:SՍԪ~nWtF7Q&sW|Wpwm7SCop}3R9 pM,ax˰wؠj0uju^MUoI'= e SsLUrRnU_$?Y__@ E'MɧK[Vrmv~X&ӏjöT<Ф. xj10`k..1MdTbd5h8!m4pe婰as3R]5r+T]AiiZzfTFʰH Cƫ>BMܰ3W(se;f"Z(E@D׻0N7u{d@-= kCCnTT]ֳ}3rV7eZ*EֆTvy,jqϻ]l 2}b(~.&)BK0izIsP5DR1%?4=]oHȢŦ i-W8^.ԁ9NMwKQS  QgR3OPq,׽k] j1[97L-fy&^a|TQF8NAk<dT=5c= pf ejY,bxʐ=`z)3$(qOӶVuzfO '/_2QMglXQ{)69eil{xm3JfNEk)'6 >rv6O |SN犨(I5%k sA&/OhYoWB:=T-nZꭾRd7y ҏL\ki`» Х6bLYtgpqҏ{l3A٪ 3j?`#:lG7/=|x]`.>\|_ێ^Z~YulO1-f W9燫̬ǜ9̼2`m8L|T+mO\I&pצ\%ӨC?mϮFx9Au蔢CR`W*yL׼8!+m`iMELݪo(mUe Wz^*m?ffw6j(7LPύ }nTc,ъh* 4JH/g$~4? kL$j5=:F`֥W7Cf HĕAkJ_8y;'m65Ngżq94[)7}l`=H[1;QHbHjj%jN,jav^5fsnhkKS%ZBH-o^Q?[Wz˿i664Jr֩.famz#EQQd4 ޜ'`jO2"֖nFoRSh+\_י!f4>7]p P&/+I-WB۾C߬/iX٣4F+XcMoDFz&CFqф[f4gh0RT-r:Fhv7!͟ ١7hh|(?EyjЛ:'9XFoNFЛ|r4ÈfI^]' Fxp_7.^{:U1"ڏl*CG27Fvw-< {9/"WrE}ĨccS޷ћŜwŘO -ŰCq'b E 7BEWjq xT%epoi6-КJA9rE]nfG6pA]%fS6M$\gԅeOܶF|:qkR:z>H(hQXUo9v2zX,.u{tlZ75F:δ$69IzPxY.5zSc420.қA'z 6 7gQr<#y 6omzGQp뜧a4w6"ʤdى;g _ o\5g4z&\A ίO߰uTY{k{nh C $y7t+BoR` ^\s /焖V|\ɴi,87W?ӛ FfP@ǜ%$G+eM9ɷMh2%2MH|?UdGraqWf'#z62yvŝcHż>uIb٬INU=^z+~O,7{k]x8N$( K53kUXuWG7\ۙ8kqƼGgJOC1cTA2q" @*htu5tGMoFMxf&7lI4MNWQz=Y֕hh;bŠ'zϪ5q 1Qab]b)'қoDsT4 y[TZE B;i%\JoJϨ >O ~Oz5`^Pg*u^u$)E"1,&13f ϲJ FP6 ?qdiTlҤKw2}fϩ\ћ<Ģs߁5q*X4${~Ko`3zSc4hcZ0 ?t8?\g5zseiMg;j7ۀ ,5v>\]47 12̐Ǵ_{ eIPk?kIg)~e{# ;9 &CزO緝i ' cu͘Q-}$Uʥ~i)y+7C.]FF_CrkwX%/=U|yEelmբia>~f.e@Z0}mkl4^ ϳ@M/bf/8dcu{YYIh˫]>B3:eFjD9r83ѽ>>oQFUz9\Z5z*Ju" ^&p t4s1;[ICi]cxڱ_G}.kRQ.D"dX<+h6;1xj2bԚpIsSM#N"HijD{'X+DO9^qglw|>^Yf2b<5F.7 r2A5vq+3?[1F3E|lfۿBi0͞^jʦ8 8x#y'o!qnRk@JS&{%8fkIJ &eiA:6Cn@rƶ~X]{P[~޻.{y6(}FTQ!30yfZg޹=7G7kφXγ5^3?V>o~N4(kX,N1%(rWgyHެ.ZJ$, sfnU?'`5_*튁a'ds]x6zV˶Kgk;u?pJ;Gp4L'{Y4U Mkve;P5%8lG`es&7a؏΅ {e|kŹİ ..| f |~G%&pMl4 j@g/y"H̚Dɕƥ-4Ꮝqp0Z_M(phRh[2(t'ij gcU$dnYnkX_]=2%E4k:{NId9 $('search').focus() sprockets-4.2.1/test/fixtures/source-maps/babel/000077500000000000000000000000001447572140400216765ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/babel/main.es6000066400000000000000000000007361447572140400232470ustar00rootroot00000000000000 var odds = evens.map(v => v + 1); var nums = evens.map((v, i) => v + i); class SkinnedMesh extends THREE.Mesh { constructor(geometry, materials) { super(geometry, materials); } update(camera) { super.update(); } static defaultMatrix() { return new THREE.Matrix4(); } } var fibonacci = { [Symbol.iterator]: function*() { var pre = 0, cur = 1; for (;;) { var temp = pre; pre = cur; cur += temp; yield cur; } } } sprockets-4.2.1/test/fixtures/source-maps/babel/precompiled/000077500000000000000000000000001447572140400242015ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/babel/precompiled/main.es6000066400000000000000000000007361447572140400255520ustar00rootroot00000000000000 var odds = evens.map(v => v + 1); var nums = evens.map((v, i) => v + i); class SkinnedMesh extends THREE.Mesh { constructor(geometry, materials) { super(geometry, materials); } update(camera) { super.update(); } static defaultMatrix() { return new THREE.Matrix4(); } } var fibonacci = { [Symbol.iterator]: function*() { var pre = 0, cur = 1; for (;;) { var temp = pre; pre = cur; cur += temp; yield cur; } } } sprockets-4.2.1/test/fixtures/source-maps/babel/precompiled/main.js000066400000000000000000000064451447572140400254740ustar00rootroot00000000000000"use strict"; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } var odds = evens.map(function (v) { return v + 1; }); var nums = evens.map(function (v, i) { return v + i; }); var SkinnedMesh = (function (_THREE$Mesh) { _inherits(SkinnedMesh, _THREE$Mesh); function SkinnedMesh(geometry, materials) { _classCallCheck(this, SkinnedMesh); _get(Object.getPrototypeOf(SkinnedMesh.prototype), "constructor", this).call(this, geometry, materials); } _createClass(SkinnedMesh, [{ key: "update", value: function update(camera) { _get(Object.getPrototypeOf(SkinnedMesh.prototype), "update", this).call(this); } }], [{ key: "defaultMatrix", value: function defaultMatrix() { return new THREE.Matrix4(); } }]); return SkinnedMesh; })(THREE.Mesh); var fibonacci = _defineProperty({}, Symbol.iterator, regeneratorRuntime.mark(function callee$0$0() { var pre, cur, temp; return regeneratorRuntime.wrap(function callee$0$0$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: pre = 0, cur = 1; case 1: temp = pre; pre = cur; cur += temp; context$1$0.next = 6; return cur; case 6: context$1$0.next = 1; break; case 8: case "end": return context$1$0.stop(); } }, callee$0$0, this); })); sprockets-4.2.1/test/fixtures/source-maps/babel/precompiled/main.js.map000066400000000000000000000014571447572140400262460ustar00rootroot00000000000000{ "version": 3, "sources": ["main.es6"], "names": [], "mappings": ";;;;;;;;;;AACA,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,UAAA,CAAC;SAAI,CAAC,GAAG,CAAC;CAAA,CAAC,CAAC;AACjC,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,UAAC,CAAC,EAAE,CAAC;SAAK,CAAC,GAAG,CAAC;CAAA,CAAC,CAAC;;IAEhC,WAAW;AACJ,WADP,WAAW,CACH,QAAQ,EAAE,SAAS,EAAE;0BAD7B,WAAW;;AAEb,+BAFE,WAAW,6CAEP,QAAQ,EAAE,SAAS,EAAE;GAE5B;;YAJG,WAAW;;eAAX,WAAW;;WAKT,gBAAC,MAAM,EAAE;AACb,iCANE,WAAW,wCAME;KAChB;;;WACmB,yBAAG;AACrB,aAAO,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;KAC5B;;;SAVG,WAAW;GAAS,KAAK,CAAC,IAAI;;AAapC,IAAI,SAAS,uBACV,MAAM,CAAC,QAAQ,0BAAG;MACb,GAAG,EAAM,GAAG,EAEV,IAAI;;;;AAFN,WAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC;;;AAEd,YAAI,GAAG,GAAG;;AACd,WAAG,GAAG,GAAG,CAAC;AACV,WAAG,IAAI,IAAI,CAAC;;eACN,GAAG;;;;;;;;;;;CAEZ,EACF,CAAA", "file": "main.es6", "sourceRoot": "" } sprockets-4.2.1/test/fixtures/source-maps/child.js000066400000000000000000000000521447572140400222470ustar00rootroot00000000000000var Child = { find: function(id) { } }; sprockets-4.2.1/test/fixtures/source-maps/coffee/000077500000000000000000000000001447572140400220605ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/coffee/main.coffee000066400000000000000000000006361447572140400241620ustar00rootroot00000000000000# Assignment: number = 42 opposite = true # Conditions: number = -42 if opposite # Functions: square = (x) -> x * x # Arrays: list = [1, 2, 3, 4, 5] # Objects: math = root: Math.sqrt square: square cube: (x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # Existence: alert "I knew it!" if elvis? # Array comprehensions: cubes = (math.cube num for num in list) sprockets-4.2.1/test/fixtures/source-maps/coffee/precompiled/000077500000000000000000000000001447572140400243635ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/coffee/precompiled/main.coffee000066400000000000000000000006361447572140400264650ustar00rootroot00000000000000# Assignment: number = 42 opposite = true # Conditions: number = -42 if opposite # Functions: square = (x) -> x * x # Arrays: list = [1, 2, 3, 4, 5] # Objects: math = root: Math.sqrt square: square cube: (x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # Existence: alert "I knew it!" if elvis? # Array comprehensions: cubes = (math.cube num for num in list) sprockets-4.2.1/test/fixtures/source-maps/coffee/precompiled/main.js000066400000000000000000000016601447572140400256500ustar00rootroot00000000000000// Generated by CoffeeScript 1.8.0 (function() { var cubes, list, math, num, number, opposite, race, square, __slice = [].slice; number = 42; opposite = true; if (opposite) { number = -42; } square = function(x) { return x * x; }; list = [1, 2, 3, 4, 5]; math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); } }; race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); }; if (typeof elvis !== "undefined" && elvis !== null) { alert("I knew it!"); } cubes = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = list.length; _i < _len; _i++) { num = list[_i]; _results.push(math.cube(num)); } return _results; })(); }).call(this); //# sourceMappingURL=main.js.map sprockets-4.2.1/test/fixtures/source-maps/coffee/precompiled/main.js.map000066400000000000000000000016151447572140400264240ustar00rootroot00000000000000{ "version": 3, "file": "main.js", "sourceRoot": "", "sources": [ "main.coffee" ], "names": [], "mappings": ";AACA;AAAA,MAAA,sDAAA;IAAA,kBAAA;;AAAA,EAAA,MAAA,GAAW,EAAX,CAAA;;AAAA,EACA,QAAA,GAAW,IADX,CAAA;;AAIA,EAAA,IAAgB,QAAhB;AAAA,IAAA,MAAA,GAAS,CAAA,EAAT,CAAA;GAJA;;AAAA,EAOA,MAAA,GAAS,SAAC,CAAD,GAAA;WAAO,CAAA,GAAI,EAAX;EAAA,CAPT,CAAA;;AAAA,EAUA,IAAA,GAAO,CAAC,CAAD,EAAI,CAAJ,EAAO,CAAP,EAAU,CAAV,EAAa,CAAb,CAVP,CAAA;;AAAA,EAaA,IAAA,GACE;AAAA,IAAA,IAAA,EAAQ,IAAI,CAAC,IAAb;AAAA,IACA,MAAA,EAAQ,MADR;AAAA,IAEA,IAAA,EAAQ,SAAC,CAAD,GAAA;aAAO,CAAA,GAAI,MAAA,CAAO,CAAP,EAAX;IAAA,CAFR;GAdF,CAAA;;AAAA,EAmBA,IAAA,GAAO,SAAA,GAAA;AACL,QAAA,eAAA;AAAA,IADM,uBAAQ,iEACd,CAAA;WAAA,KAAA,CAAM,MAAN,EAAc,OAAd,EADK;EAAA,CAnBP,CAAA;;AAuBA,EAAA,IAAsB,8CAAtB;AAAA,IAAA,KAAA,CAAM,YAAN,CAAA,CAAA;GAvBA;;AAAA,EA0BA,KAAA;;AAAS;SAAA,2CAAA;qBAAA;AAAA,oBAAA,IAAI,CAAC,IAAL,CAAU,GAAV,EAAA,CAAA;AAAA;;MA1BT,CAAA;AAAA" }sprockets-4.2.1/test/fixtures/source-maps/dynamic/000077500000000000000000000000001447572140400222555ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/dynamic/application.coffee000066400000000000000000000001161447572140400257270ustar00rootroot00000000000000#= require dynamic/unstable document.on 'dom:loaded', -> console.log("Hi") sprockets-4.2.1/test/fixtures/source-maps/foo/000077500000000000000000000000001447572140400214145ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/foo/file.coffee000066400000000000000000000000501447572140400234770ustar00rootroot00000000000000console.log("foo/file.coffee") if 1 < 2 sprockets-4.2.1/test/fixtures/source-maps/foo/index.js000066400000000000000000000000521447572140400230560ustar00rootroot00000000000000//= require ./file console.log("foo.js"); sprockets-4.2.1/test/fixtures/source-maps/multi-require.js000066400000000000000000000001321447572140400237670ustar00rootroot00000000000000//= require child.js //= require coffee/main.js //= require sub/a.js //= require plain.js sprockets-4.2.1/test/fixtures/source-maps/parent.js000066400000000000000000000001471447572140400224620ustar00rootroot00000000000000//= require child //= require users document.on('dom:loaded', function() { $('parent').focus(); }); sprockets-4.2.1/test/fixtures/source-maps/plain.js000066400000000000000000000000711447572140400222700ustar00rootroot00000000000000function plain(input) { console.log("Plain" + input) } sprockets-4.2.1/test/fixtures/source-maps/project.coffee000066400000000000000000000000321447572140400234430ustar00rootroot00000000000000Project = find: (id) -> sprockets-4.2.1/test/fixtures/source-maps/sass/000077500000000000000000000000001447572140400216025ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/sass/_imported.scss000066400000000000000000000000251447572140400244560ustar00rootroot00000000000000body { color: red; } sprockets-4.2.1/test/fixtures/source-maps/sass/main.scss000066400000000000000000000002721447572140400234240ustar00rootroot00000000000000nav { ul { margin: 0; padding: 0; list-style: none; } li { display: inline-block; } a { display: block; padding: 6px 12px; text-decoration: none; } } sprockets-4.2.1/test/fixtures/source-maps/sass/precompiled/000077500000000000000000000000001447572140400241055ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/sass/precompiled/main.css000066400000000000000000000003161447572140400255430ustar00rootroot00000000000000nav ul { margin: 0; padding: 0; list-style: none; } nav li { display: inline-block; } nav a { display: block; padding: 6px 12px; text-decoration: none; } /*# sourceMappingURL=main.css.map */ sprockets-4.2.1/test/fixtures/source-maps/sass/precompiled/main.css.map000066400000000000000000000005301447572140400263150ustar00rootroot00000000000000{ "version": 3, "mappings": "AACE,MAAG;EACD,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,UAAU,EAAE,IAAI;AAGlB,MAAG;EAAE,OAAO,EAAE,YAAY;AAE1B,KAAE;EACA,OAAO,EAAE,KAAK;EACd,OAAO,EAAE,QAAQ;EACjB,eAAe,EAAE,IAAI", "sources": ["file:///Users/josh/Projects/sprockets/test/fixtures/source-maps/sass/precompiled/main.scss"], "names": [], "file": "main.css" }sprockets-4.2.1/test/fixtures/source-maps/sass/precompiled/main.scss000066400000000000000000000002721447572140400257270ustar00rootroot00000000000000nav { ul { margin: 0; padding: 0; list-style: none; } li { display: inline-block; } a { display: block; padding: 6px 12px; text-decoration: none; } } sprockets-4.2.1/test/fixtures/source-maps/sass/with-import.scss000066400000000000000000000000521447572140400247570ustar00rootroot00000000000000@import 'imported'; nav { color: blue; } sprockets-4.2.1/test/fixtures/source-maps/sub/000077500000000000000000000000001447572140400214225ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/sub/a.js000066400000000000000000000000551447572140400222000ustar00rootroot00000000000000function a() { console.log('sub/a.js') } sprockets-4.2.1/test/fixtures/source-maps/sub/directory.js000066400000000000000000000001261447572140400237630ustar00rootroot00000000000000//= require ./a //= require ./modules/something console.log('sub/directory.js'); a(); sprockets-4.2.1/test/fixtures/source-maps/sub/modules/000077500000000000000000000000001447572140400230725ustar00rootroot00000000000000sprockets-4.2.1/test/fixtures/source-maps/sub/modules/something.js000066400000000000000000000000341447572140400254220ustar00rootroot00000000000000console.log("something.js") sprockets-4.2.1/test/fixtures/source-maps/users.coffee000066400000000000000000000000301447572140400231340ustar00rootroot00000000000000Users = find: (id) -> sprockets-4.2.1/test/fixtures/symlink000077700000000000000000000000001447572140400213462defaultustar00rootroot00000000000000sprockets-4.2.1/test/shared_sass_tests.rb000066400000000000000000000175761447572140400206100ustar00rootroot00000000000000# frozen_string_literal: true module SharedSassTestNoFunction extend Sprockets::TestDefinition test "aren't included globally" do silence_warnings do assert sass_functions.instance_methods.include?(:javascript_path) assert sass_functions.instance_methods.include?(:stylesheet_path) filename = fixture_path('sass/paths.scss') assert data = File.read(filename) engine = sass_engine.new(data, { filename: filename, syntax: :scss }) assert sass_functions.instance_methods.include?(:javascript_path) assert sass_functions.instance_methods.include?(:stylesheet_path) assert_equal <<-EOS, engine.render div { url: url(asset-path("foo.svg")); url: url(image-path("foo.png")); url: url(video-path("foo.mov")); url: url(audio-path("foo.mp3")); url: url(font-path("foo.woff2")); url: url(font-path("foo.woff")); url: url("/js/foo.js"); url: url("/css/foo.css"); } EOS end end end module SharedSassTestSprockets extend Sprockets::TestDefinition test "process variables" do assert_equal <<-EOS, render('sass/variables.sass') .content-navigation { border-color: #3bbfce; color: #2ca2af; } .border { padding: 8px; margin: 8px; border-color: #3bbfce; } EOS end test "process nesting" do assert_equal <<-EOS, render('sass/nesting.scss') table.hl { margin: 2em 0; } table.hl td.ln { text-align: right; } li { font-family: serif; font-weight: bold; font-size: 1.2em; } EOS end test "@import scss partial from scss" do assert_equal <<-EOS, render('sass/import_partial.scss') #navbar li { border-top-radius: 10px; -moz-border-radius-top: 10px; -webkit-border-top-radius: 10px; } #footer { border-top-radius: 5px; -moz-border-radius-top: 5px; -webkit-border-top-radius: 5px; } #sidebar { border-left-radius: 8px; -moz-border-radius-left: 8px; -webkit-border-left-radius: 8px; } EOS end test "@import scss partial from sass" do assert_equal <<-EOS, render('sass/import_partial.sass') #navbar li { border-top-radius: 10px; -moz-border-radius-top: 10px; -webkit-border-top-radius: 10px; } #footer { border-top-radius: 5px; -moz-border-radius-top: 5px; -webkit-border-top-radius: 5px; } #sidebar { border-left-radius: 8px; -moz-border-radius-left: 8px; -webkit-border-left-radius: 8px; } EOS end test "@import sass non-partial from scss" do assert_equal <<-EOS, render('sass/import_nonpartial.scss') .content-navigation { border-color: #3bbfce; color: #2ca2af; } .border { padding: 8px; margin: 8px; border-color: #3bbfce; } EOS end test "@import css file from load path" do skip "Does not work on jruby" if RUBY_PLATFORM.include?('java') skip "Does not work on windows with sassc" if File::ALT_SEPARATOR && self.sass == ::SassC assert_match(/\A\s*\z/, render('sass/import_load_path.scss')) end test "process css file" do assert_equal <<-EOS, render('sass/reset.css') article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } EOS end test "@import relative file" do assert_equal <<-EOS, render('sass/shared/relative.scss') #navbar li { border-top-radius: 10px; -moz-border-radius-top: 10px; -webkit-border-top-radius: 10px; } #footer { border-top-radius: 5px; -moz-border-radius-top: 5px; -webkit-border-top-radius: 5px; } #sidebar { border-left-radius: 8px; -moz-border-radius-left: 8px; -webkit-border-left-radius: 8px; } EOS end test "@import relative nested file" do assert_equal <<-EOS, render('sass/relative.scss') body { background: #666666; } EOS end test "modify file causes it to recompile" do filename = fixture_path('sass/test.scss') sandbox filename do File.open(filename, 'w') { |f| f.write "body { background: red; };" } assert_equal "body {\n background: red; }\n", render(filename) File.open(filename, 'w') { |f| f.write "body { background: blue; };" } mtime = Time.now + 1 File.utime(mtime, mtime, filename) assert_equal "body {\n background: blue; }\n", render(filename) end end test "modify partial causes it to recompile" do filename, partial = fixture_path('sass/test.scss'), fixture_path('sass/_partial.scss') sandbox filename, partial do File.open(filename, 'w') { |f| f.write "@import 'partial';" } File.open(partial, 'w') { |f| f.write "body { background: red; };" } assert_equal "body {\n background: red; }\n", render(filename) File.open(partial, 'w') { |f| f.write "body { background: blue; };" } mtime = Time.now + 1 File.utime(mtime, mtime, partial) assert_equal "body {\n background: blue; }\n", render(filename) end end test "reference @import'd variable" do assert_equal <<-EOS, render('sass/links.scss') a:link { color: "red"; } EOS end test "@import reference variable" do assert_equal <<-EOS, render('sass/main.scss') #header { color: "blue"; } EOS end end module SharedSassTestCompressor extend Sprockets::TestDefinition def setup @env = Sprockets::Environment.new @env.append_path File.expand_path("../fixtures", __FILE__) end test "compress css" do silence_warnings do uncompressed = "p {\n margin: 0;\n padding: 0;\n}\n" compressed = "p{margin:0;padding:0}\n" input = { environment: @env, filename: File.expand_path("../fixtures/uncompressed.css", __FILE__), load_path: File.expand_path("../fixtures", __FILE__), data: uncompressed, metadata: {}, cache: Sprockets::Cache.new } assert_equal compressed, compressor.call(input)[:data] end end end module SharedSassTestFunctions extend Sprockets::TestDefinition test "path functions" do assert_equal <<-EOS, render('sass/paths.scss') div { url: url("/foo.svg"); url: url("/foo.png"); url: url("/foo.mov"); url: url("/foo.mp3"); url: url("/foo.woff2"); url: url("/foo.woff"); url: url("/foo.js"); url: url("/foo.css"); } EOS end test "url functions" do assert_equal <<-EOS, render('sass/urls.scss') div { url: url(/foo.svg); url: url(/foo.png); url: url(/foo.mov); url: url(/foo.mp3); url: url(/foo.woff2); url: url(/foo.woff); url: url(/foo.js); url: url(/foo.css); } EOS end test "url functions with query and hash parameters" do assert_equal <<-EOS, render('octicons/octicons.scss') @font-face { font-family: 'octicons'; src: url(/octicons.eot?#iefix) format("embedded-opentype"), url(/octicons.woff2) format("woff2"), url(/octicons.woff) format("woff"), url(/octicons.ttf) format("truetype"), url(/octicons.svg#octicons) format("svg"); font-weight: normal; font-style: normal; } EOS end test "path function generates links" do asset = silence_warnings do @env.find_asset('sass/paths.css') end assert asset assert_equal [ "file://#{fixture_path_for_uri('compass/foo.css')}?type=text/css&id=xxx", "file://#{fixture_path_for_uri('compass/foo.js')}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri('compass/foo.mov')}?id=xxx", "file://#{fixture_path_for_uri('compass/foo.mp3')}?type=audio/mpeg&id=xxx", "file://#{fixture_path_for_uri('compass/foo.svg')}?type=image/png&id=xxx", "file://#{fixture_path_for_uri('compass/foo.svg')}?type=image/svg+xml&id=xxx", "file://#{fixture_path_for_uri('compass/foo.woff2')}?type=application/font-woff2&id=xxx", "file://#{fixture_path_for_uri('compass/foo.woff')}?type=application/font-woff&id=xxx" ], asset.links.to_a.map { |uri| uri.sub(/id=\w+/, 'id=xxx') }.sort end test "data-url function" do assert_equal <<-EOS, render('sass/data_url.scss') div { url: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAABlBMVEUFO2sAAADPfNHpAAAACklEQVQIW2NgAAAAAgABYkBPaAAAAABJRU5ErkJggg%3D%3D); } EOS end end sprockets-4.2.1/test/sprockets_test.rb000066400000000000000000000107051447572140400201260ustar00rootroot00000000000000# frozen_string_literal: true require "minitest/autorun" require "sprockets" require "sprockets/environment" require "fileutils" require "timecop" require "rack/lint" old_verbose, $VERBOSE = $VERBOSE, false Encoding.default_external = 'UTF-8' Encoding.default_internal = 'UTF-8' $VERBOSE = old_verbose def silence_warnings old_verbose, $VERBOSE = $VERBOSE, false yield ensure $VERBOSE = old_verbose end # Popular extensions for testing but not part of Sprockets core Sprockets.register_dependency_resolver "rand" do rand(2**100) end NoopProcessor = proc { |input| input[:data] } Sprockets.register_mime_type 'text/haml', extensions: ['.haml'] Sprockets.register_transformer 'text/haml', 'text/html', NoopProcessor Sprockets.register_mime_type 'text/mustache', extensions: ['.mustache'] Sprockets.register_transformer 'text/mustache', 'application/javascript+function', NoopProcessor Sprockets.register_mime_type 'text/x-handlebars-template', extensions: ['.handlebars'] Sprockets.register_transformer 'text/x-handlebars-template', 'application/javascript+function', NoopProcessor Sprockets.register_mime_type 'application/dart', extensions: ['.dart'] Sprockets.register_transformer 'application/dart', 'application/javascript', NoopProcessor require 'nokogiri' HtmlBuilderProcessor = proc { |input| instance_eval <<-EOS builder = Nokogiri::HTML::Builder.new do |doc| #{input[:data]} end builder.to_html EOS } Sprockets.register_mime_type 'application/html+builder', extensions: ['.html.builder'] Sprockets.register_transformer 'application/html+builder', 'text/html', HtmlBuilderProcessor XmlBuilderProcessor = proc { |input| instance_eval <<-EOS builder = Nokogiri::XML::Builder.new do |xml| #{input[:data]} end builder.to_xml EOS } Sprockets.register_mime_type 'application/xml+builder', extensions: ['.xml.builder'] Sprockets.register_transformer 'application/xml+builder', 'application/xml', XmlBuilderProcessor SVG2PNG = proc { |input| "\x89\x50\x4e\x47\xd\xa\x1a\xa#{input[:data]}" } Sprockets.register_transformer 'image/svg+xml', 'image/png', SVG2PNG PNG2GIF = proc { |input| "\x47\x49\x46\x38\x37\61#{input[:data]}" } Sprockets.register_transformer 'image/png', 'image/gif', PNG2GIF CSS2HTMLIMPORT = proc { |input| "" } Sprockets.register_transformer 'text/css', 'text/html', CSS2HTMLIMPORT JS2HTMLIMPORT = proc { |input| "" } Sprockets.register_transformer 'application/javascript', 'text/html', JS2HTMLIMPORT Sprockets.register_bundle_metadata_reducer 'text/css', :selector_count, :+ Sprockets.register_postprocessor 'text/css', proc { |input| { selector_count: input[:data].scan(/\{/).size } } module Sprockets::TestDefinition def test(name, &block) define_method("test_#{name.inspect}", &block) end end class Sprockets::TestCase < Minitest::Test extend Sprockets::TestDefinition FIXTURE_ROOT = File.join(__dir__, "fixtures") def fixture(path) IO.read(fixture_path(path)) end def fixture_path(path) if path.match(FIXTURE_ROOT) path else File.join(FIXTURE_ROOT, path) end end def fixture_path_for_uri(path) uri_path(fixture_path(path).to_s) end def uri_path(path) path = '/' + path if path[1] == ':' # Windows path / drive letter path end def sandbox(*paths) backup_paths = paths.select { |path| File.exist?(path) } remove_paths = paths.select { |path| !File.exist?(path) } begin backup_paths.each do |path| FileUtils.cp(path, "#{path}.orig") end yield ensure backup_paths.each do |path| if File.exist?("#{path}.orig") FileUtils.mv("#{path}.orig", path) end assert !File.exist?("#{path}.orig") end remove_paths.each do |path| if File.exist?(path) FileUtils.rm_rf(path) end assert !File.exist?(path) end end end def write(filename, contents, mtime = nil) if File.exist?(filename) mtime ||= [Time.now.to_i, File.stat(filename).mtime.to_i].max + 1 File.open(filename, 'w') do |f| f.write(contents) end File.utime(mtime, mtime, filename) else File.open(filename, 'w') do |f| f.write(contents) end File.utime(mtime, mtime, filename) if mtime end end def normalize_uri(uri) uri.sub(/id=\w+/, 'id=xxx') end def normalize_uris(uris) uris.to_a.map { |uri| normalize_uri(uri) }.sort end end sprockets-4.2.1/test/test_asset.rb000066400000000000000000001306141447572140400172320ustar00rootroot00000000000000# frozen_string_literal: true require "sprockets_test" module AssetTests def self.test(name, &block) define_method("test_#{name.inspect}", &block) end test "id is a SHA256 String" do assert_kind_of String, @asset.id assert_match(/^[0-9a-f]{64}$/, @asset.id) end test "uri can find itself" do # assert_kind_of URI, @asset.uri assert_equal @asset, @env.load(@asset.uri) end test "length is source length" do assert_equal @asset.to_s.length, @asset.length end test "bytesize is source bytesize" do assert_equal @asset.to_s.bytesize, @asset.bytesize end test "links are a Set" do assert_kind_of Set, @asset.links end test "write to file" do target = fixture_path('asset/tmp.js') begin @asset.write_to(target) assert File.exist?(target) ensure FileUtils.rm(target) if File.exist?(target) assert !File.exist?(target) end end test "write to gzipped file" do target = fixture_path('asset/tmp.js.gz') begin @asset.write_to(target) assert File.exist?(target) ensure FileUtils.rm(target) if File.exist?(target) assert !File.exist?(target) end end end module FreshnessTests def self.test(name, &block) define_method("test_#{name.inspect}", &block) end test "modify asset contents" do filename = fixture_path('asset/test.js') sandbox filename do write(filename, "a;") asset = asset('test.js') old_digest = asset.hexdigest old_uri = asset.uri assert_equal "a;\n", asset.to_s write(filename, "b;") asset = asset('test.js') refute_equal old_digest, asset.hexdigest refute_equal old_uri, asset.uri assert_equal "b;\n", asset.to_s end end test "remove asset" do filename = fixture_path('asset/test.js') sandbox filename do write(filename, "a;") asset('test.js') File.unlink(filename) refute asset('test.js') end end test "modify asset's dependency file" do main = fixture_path('asset/test-main.js.erb') dep = fixture_path('asset/test-dep.js') sandbox main, dep do write(main, "//= depend_on test-dep\n<%= File.read('#{dep}') %>") write(dep, "a;") asset = asset('test-main.js') old_digest = asset.hexdigest old_uri = asset.uri assert_equal "a;\n", asset.to_s write(dep, "b;") asset = asset('test-main.js') refute_equal old_digest, asset.hexdigest refute_equal old_uri, asset.uri assert_equal "b;\n", asset.to_s end end test "remove asset's dependency file" do main = fixture_path('asset/test-main.js') dep = fixture_path('asset/test-dep.js') sandbox main, dep do write(main, "//= depend_on test-dep\n") write(dep, "a;") asset('test-main.js') File.unlink(dep) assert_raises(Sprockets::FileNotFound) do asset('test-main.js') end end end test "modify asset's dependency file in directory" do main = fixture_path('asset/test-main.js.erb') dep = fixture_path('asset/data/foo.txt') begin ::FileUtils.mkdir File.dirname(dep) sandbox main, dep do write(main, "//= depend_on_directory ./data\n<%= File.read('#{dep}') %>") write(dep, "a;") asset = asset('test-main.js') old_digest = asset.hexdigest old_uri = asset.uri assert_equal "a;\n", asset.to_s write(dep, "b;") asset = asset('test-main.js') refute_equal old_digest, asset.hexdigest refute_equal old_uri, asset.uri assert_equal "b;\n", asset.to_s end ensure ::FileUtils.rmtree File.dirname(dep) end end test "asset's dependency on directory exists" do main = fixture_path('asset/test-missing-directory.js.erb') dep = fixture_path('asset/data/foo.txt') begin sandbox main, dep do ::FileUtils.rmtree File.dirname(dep) write(main, "//= depend_on_directory ./data") assert_raises(Sprockets::ArgumentError) do asset('test-missing-directory.js') end ::FileUtils.mkdir File.dirname(dep) assert asset('test-missing-directory.js') end ensure ::FileUtils.rmtree File.dirname(dep) end end end class TextStaticAssetTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('asset')) @env.cache = {} @asset = @env['log.txt'] end include AssetTests test "uri" do assert_equal "file://#{fixture_path_for_uri('asset/log.txt')}?type=text/plain&id=xxx", normalize_uri(@asset.uri) end test "logical path" do assert_equal "log.txt", @asset.logical_path end test "digest path" do assert_equal "log-66a045b452102c59d840ec097d59d9467e13a3f34f6494e539ffd32c1bb35f18.txt", @asset.digest_path end test "content type" do assert_equal "text/plain", @asset.content_type end test "charset is UTF-8" do assert_equal 'utf-8', @asset.charset end test "length" do assert_equal 6, @asset.length end test "bytesize" do assert_equal 6, @asset.bytesize end end class BinaryStaticAssetTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('asset')) @env.cache = {} @asset = @env['POW.png'] end include AssetTests test "uri" do assert_equal "file://#{fixture_path_for_uri('asset/POW.png')}?type=image/png&id=xxx", normalize_uri(@asset.uri) end test "logical path" do assert_equal "POW.png", @asset.logical_path end test "digest path" do assert_equal "POW-1da2e59df75d33d8b74c3d71feede698f203f136512cbaab20c68a5bdebd5800.png", @asset.digest_path end test "content type" do assert_equal "image/png", @asset.content_type end test "charset is nil" do assert_nil @asset.charset end test "length" do assert_equal 42917, @asset.length end test "bytesize" do assert_equal 42917, @asset.bytesize end test "source digest" do assert_equal [29, 162, 229, 157, 247, 93, 51, 216, 183, 76, 61, 113, 254, 237, 230, 152, 242, 3, 241, 54, 81, 44, 186, 171, 32, 198, 138, 91, 222, 189, 88, 0], @asset.digest.bytes.to_a end test "source hexdigest" do assert_equal "1da2e59df75d33d8b74c3d71feede698f203f136512cbaab20c68a5bdebd5800", @asset.hexdigest end test "source base64digest" do assert_equal "HaLlnfddM9i3TD1x/u3mmPID8TZRLLqrIMaKW969WAA=", @asset.base64digest end test "integrity" do assert_equal "sha256-HaLlnfddM9i3TD1x/u3mmPID8TZRLLqrIMaKW969WAA=", @asset.integrity end test "asset is fresh if its mtime is changed but its contents is the same" do filename = fixture_path('asset/test-POW.png') sandbox filename do File.open(filename, 'w') { |f| f.write "a" } asset = @env['test-POW.png'] assert asset old_digest = asset.hexdigest old_uri = asset.uri File.open(filename, 'w') { |f| f.write "a" } mtime = Time.now + 1 File.utime(mtime, mtime, filename) assert_equal old_digest, @env['test-POW.png'].hexdigest assert_equal old_uri, @env['test-POW.png'].uri end end test "asset is stale when its contents has changed" do filename = fixture_path('asset/POW.png') sandbox filename do File.open(filename, 'w') { |f| f.write "a" } asset = @env['POW.png'] assert asset old_digest = asset.hexdigest old_uri = asset.uri File.open(filename, 'w') { |f| f.write "b" } mtime = Time.now + 1 File.utime(mtime, mtime, filename) refute_equal old_digest, @env['POW.png'].hexdigest refute_equal old_uri, @env['POW.png'].uri end end test "asset is fresh if the file is removed" do filename = fixture_path('asset/POW.png') sandbox filename do File.open(filename, 'w') { |f| f.write "a" } asset = @env['POW.png'] assert asset File.unlink(filename) refute @env['POW.png'] end end end class SourceAssetTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('asset')) @env.cache = {} @pipeline = :source @asset = @env.find_asset('application.js', pipeline: :source) end include AssetTests test "uri" do assert_equal "file://#{fixture_path_for_uri('asset/application.js')}?type=application/javascript&pipeline=source&id=xxx", normalize_uri(@asset.uri) end test "logical path" do assert_equal "application.source.js", @asset.logical_path end test "digest path" do assert_equal "application.source-6ae801e02813bf209a84a89b8c5b5edf5eb770ca9e4253c56834c08a2fc5dbea.js", @asset.digest_path end test "content type" do assert_equal "application/javascript", @asset.content_type end test "length" do assert_equal 109, @asset.length end test "source digest" do assert_equal [106, 232, 1, 224, 40, 19, 191, 32, 154, 132, 168, 155, 140, 91, 94, 223, 94, 183, 112, 202, 158, 66, 83, 197, 104, 52, 192, 138, 47, 197, 219, 234], @asset.digest.bytes.to_a end test "source hexdigest" do assert_equal "6ae801e02813bf209a84a89b8c5b5edf5eb770ca9e4253c56834c08a2fc5dbea", @asset.hexdigest end test "source base64digest" do assert_equal "augB4CgTvyCahKibjFte3163cMqeQlPFaDTAii/F2+o=", @asset.base64digest end test "integrity" do assert_equal "sha256-augB4CgTvyCahKibjFte3163cMqeQlPFaDTAii/F2+o=", @asset.integrity end test "to_s" do assert_equal "// =require \"project\"\n// =require \"users\"\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n", @asset.to_s end def asset(logical_path, options = {}) @env.find_asset(logical_path, **{pipeline: @pipeline}.merge(options)) end end class ProcessedAssetTest < Sprockets::TestCase include FreshnessTests def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('asset')) @env.cache = {} @pipeline = :self @asset = @env.find_asset('application.js', pipeline: :self) end include AssetTests test "uri" do assert_equal "file://#{fixture_path_for_uri('asset/application.js')}?type=application/javascript&pipeline=self&id=xxx", normalize_uri(@asset.uri) end test "logical path" do assert_equal "application.self.js", @asset.logical_path end test "digest path" do assert_equal "application.self-6a5fff89e8328f158e77642b53e325c24ed844a6bcd5a96ec0f9004384e9c9a5.js", @asset.digest_path end test "content type" do assert_equal "application/javascript", @asset.content_type end test "length" do assert_equal 69, @asset.length end test "source digest" do assert_equal [106, 95, 255, 137, 232, 50, 143, 21, 142, 119, 100, 43, 83, 227, 37, 194, 78, 216, 68, 166, 188, 213, 169, 110, 192, 249, 0, 67, 132, 233, 201, 165], @asset.digest.bytes.to_a end test "source hexdigest" do assert_equal "6a5fff89e8328f158e77642b53e325c24ed844a6bcd5a96ec0f9004384e9c9a5", @asset.hexdigest end test "source base64digest" do assert_equal "al//iegyjxWOd2QrU+Mlwk7YRKa81aluwPkAQ4TpyaU=", @asset.base64digest end test "integrity" do assert_equal "sha256-al//iegyjxWOd2QrU+Mlwk7YRKa81aluwPkAQ4TpyaU=", @asset.integrity end test "charset is UTF-8" do assert_equal 'utf-8', @asset.charset end test "to_s" do assert_equal "\n\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n", @asset.to_s end test "each" do body = +"" @asset.each { |part| body << part } assert_equal "\n\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n", body end def asset(logical_path, options = {}) @env.find_asset(logical_path, **{pipeline: @pipeline}.merge(options)) end end class BundledAssetTest < Sprockets::TestCase include FreshnessTests def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('asset')) @env.cache = {} @pipeline = nil @asset = @env['application.js'] end include AssetTests test "uri" do assert_equal "file://#{fixture_path_for_uri('asset/application.js')}?type=application/javascript&id=xxx", normalize_uri(@asset.uri) end test "logical path" do assert_equal "application.js", @asset.logical_path end test "digest path" do assert_equal "application-955b2dddd0d1449b1c617124b83b46300edadec06d561104f7f6165241b31a94.js", @asset.digest_path end test "environment version" do @env.version = "v1" assert_equal "v1", @env['application.js'].environment_version end test "content type" do assert_equal "application/javascript", @asset.content_type end test "length" do assert_equal 159, @asset.length end test "source digest" do assert_equal [149, 91, 45, 221, 208, 209, 68, 155, 28, 97, 113, 36, 184, 59, 70, 48, 14, 218, 222, 192, 109, 86, 17, 4, 247, 246, 22, 82, 65, 179, 26, 148], @asset.digest.bytes.to_a end test "source hexdigest" do assert_equal "955b2dddd0d1449b1c617124b83b46300edadec06d561104f7f6165241b31a94", @asset.hexdigest end test "source base64digest" do assert_equal "lVst3dDRRJscYXEkuDtGMA7a3sBtVhEE9/YWUkGzGpQ=", @asset.base64digest end test "integrity" do assert_equal "sha256-lVst3dDRRJscYXEkuDtGMA7a3sBtVhEE9/YWUkGzGpQ=", @asset.integrity end test "charset is UTF-8" do assert_equal 'utf-8', @asset.charset end test "to_s" do assert_equal "var Project = {\n find: function(id) {\n }\n};\nvar Users = {\n find: function(id) {\n }\n};\n\n\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n", @asset.to_s end test "each" do body = +"" @asset.each { |part| body << part } assert_equal "var Project = {\n find: function(id) {\n }\n};\nvar Users = {\n find: function(id) {\n }\n};\n\n\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n", body end test "asset is stale when one of its source files is modified" do main = fixture_path('asset/test-main.js') dep = fixture_path('asset/test-dep.js') sandbox main, dep do File.open(main, 'w') { |f| f.write "//= require test-dep\n" } File.open(dep, 'w') { |f| f.write "a;" } asset = asset('test-main.js') old_digest = asset.hexdigest old_uri = asset.uri File.open(dep, 'w') { |f| f.write "b;" } mtime = Time.now + 1 File.utime(mtime, mtime, dep) refute_equal old_digest, asset('test-main.js').hexdigest refute_equal old_uri, asset('test-main.js').uri end end test "asset is stale when one of its asset dependencies is modified" do main = fixture_path('asset/test-main.js') dep = fixture_path('asset/test-dep.js') sandbox main, dep do File.open(main, 'w') { |f| f.write "//= depend_on_asset test-dep\n" } File.open(dep, 'w') { |f| f.write "a;" } asset = asset('test-main.js') old_digest = asset.hexdigest old_uri = asset.uri File.open(dep, 'w') { |f| f.write "b;" } mtime = Time.now + 1 File.utime(mtime, mtime, dep) asset = asset('test-main.js') assert_equal old_digest, asset.hexdigest refute_equal old_uri, asset.uri end end test "asset is stale when one of its source files dependencies is modified" do a = fixture_path('asset/test-a.js') b = fixture_path('asset/test-b.js') c = fixture_path('asset/test-c.js') sandbox a, b, c do File.open(a, 'w') { |f| f.write "//= require test-b\n" } File.open(b, 'w') { |f| f.write "//= require test-c\n" } File.open(c, 'w') { |f| f.write "c;" } asset_a = asset('test-a.js') asset_b = asset('test-b.js') asset_c = asset('test-c.js') old_asset_a_digest = asset_a.hexdigest old_asset_b_digest = asset_b.hexdigest old_asset_c_digest = asset_c.hexdigest old_asset_a_uri = asset_a.uri old_asset_b_uri = asset_b.uri old_asset_c_uri = asset_c.uri File.open(c, 'w') { |f| f.write "x;" } mtime = Time.now + 1 File.utime(mtime, mtime, c) refute_equal old_asset_a_digest, asset('test-a.js').hexdigest refute_equal old_asset_b_digest, asset('test-b.js').hexdigest refute_equal old_asset_c_digest, asset('test-c.js').hexdigest refute_equal old_asset_a_uri, asset('test-a.js').uri refute_equal old_asset_b_uri, asset('test-b.js').uri refute_equal old_asset_c_uri, asset('test-c.js').uri end end test "asset is stale when one of its dependency dependencies is modified" do a = fixture_path('asset/test-a.js') b = fixture_path('asset/test-b.js') c = fixture_path('asset/test-c.js') sandbox a, b, c do File.open(a, 'w') { |f| f.write "//= require test-b\n" } File.open(b, 'w') { |f| f.write "//= depend_on test-c\n" } File.open(c, 'w') { |f| f.write "c;" } asset_a = asset('test-a.js') asset_b = asset('test-b.js') asset_c = asset('test-c.js') old_asset_a_uri = asset_a.uri old_asset_b_uri = asset_b.uri old_asset_c_uri = asset_c.uri File.open(c, 'w') { |f| f.write "x;" } mtime = Time.now + 1 File.utime(mtime, mtime, c) refute_equal old_asset_a_uri, asset('test-a.js').uri refute_equal old_asset_b_uri, asset('test-b.js').uri refute_equal old_asset_c_uri, asset('test-c.js').uri end end test "asset is stale when one of its asset dependency dependencies is modified" do a = fixture_path('asset/test-a.js') b = fixture_path('asset/test-b.js') c = fixture_path('asset/test-c.js') sandbox a, b, c do File.open(a, 'w') { |f| f.write "//= depend_on_asset test-b\n" } File.open(b, 'w') { |f| f.write "//= depend_on_asset test-c\n" } File.open(c, 'w') { |f| f.write "c;" } asset_a = asset('test-a.js') asset_b = asset('test-b.js') asset_c = asset('test-c.js') old_asset_a_uri = asset_a.uri old_asset_b_uri = asset_b.uri old_asset_c_uri = asset_c.uri File.open(c, 'w') { |f| f.write "x;" } mtime = Time.now + 1 File.utime(mtime, mtime, c) refute_equal old_asset_a_uri, asset('test-a.js').uri refute_equal old_asset_b_uri, asset('test-b.js').uri refute_equal old_asset_c_uri, asset('test-c.js').uri end end test "asset is stale when one of its linked assets is modified" do a = fixture_path('asset/test-a.js') b = fixture_path('asset/test-b.js') sandbox a, b do File.open(a, 'w') { |f| f.write "//= link test-b\n" } File.open(b, 'w') { |f| f.write "b;" } asset_a = asset('test-a.js') asset_b = asset('test-b.js') old_asset_a_uri = asset_a.uri old_asset_b_uri = asset_b.uri File.open(b, 'w') { |f| f.write "x;" } mtime = Time.now + 1 File.utime(mtime, mtime, b) refute_equal old_asset_a_uri, asset('test-a.js').uri refute_equal old_asset_b_uri, asset('test-b.js').uri end end test "erb asset is stale when one of its linked assets is modified" do a = fixture_path('asset/test-a.js.erb') b = fixture_path('asset/test-b.js.erb') sandbox a, b do File.open(a, 'w') { |f| f.write "<% link_asset 'test-b' %>\n" } File.open(b, 'w') { |f| f.write "b;" } asset_a = asset('test-a.js') asset_b = asset('test-b.js') old_asset_a_uri = asset_a.uri old_asset_b_uri = asset_b.uri File.open(b, 'w') { |f| f.write "x;" } mtime = Time.now + 1 File.utime(mtime, mtime, b) refute_equal old_asset_a_uri, asset('test-a.js').uri refute_equal old_asset_b_uri, asset('test-b.js').uri end end test "asset is stale if a file is added to its require directory" do asset = asset("tree/all_with_require_directory.js") assert asset old_uri = asset.uri dirname = File.join(fixture_path("asset"), "tree/all") filename = File.join(dirname, "z.js") sandbox filename do File.open(filename, 'w') { |f| f.write "z" } mtime = Time.now + 1 File.utime(mtime, mtime, dirname) refute_equal old_uri, asset("tree/all_with_require_directory.js").uri end end test "asset is stale if a file is added to its require tree" do asset = asset("tree/all_with_require_tree.js") assert asset old_uri = asset.uri dirname = File.join(fixture_path("asset"), "tree/all/b/c") filename = File.join(dirname, "z.js") sandbox filename do File.open(filename, 'w') { |f| f.write "z" } mtime = Time.now + 1 File.utime(mtime, mtime, dirname) refute_equal old_uri, asset("tree/all_with_require_tree.js").uri end end test "asset is stale if its declared dependency changes" do sprite = fixture_path('asset/sprite.css.erb') image = fixture_path('asset/POW.png') sandbox sprite, image do asset = asset('sprite.css') assert asset old_uri = asset.uri File.open(image, 'w') { |f| f.write "(change)" } mtime = Time.now + 1 File.utime(mtime, mtime, image) refute_equal old_uri, asset('sprite.css').uri end end test "asset if stale if once of its source files is removed" do main = fixture_path('asset/test-main.js') dep = fixture_path('asset/test-dep.js') sandbox main, dep do File.open(main, 'w') { |f| f.write "//= require test-dep\n" } File.open(dep, 'w') { |f| f.write "a;" } assert asset('test-main.js') File.unlink(dep) assert_raises(Sprockets::FileNotFound) do asset('test-main.js') end end end test "asset is stale when one of its stubbed targets dependencies are modified" do frameworks = fixture_path('asset/stub-frameworks.js') app = fixture_path('asset/stub-app.js') jquery = fixture_path('asset/stub-jquery.js') sandbox frameworks, app, jquery do write(frameworks, "frameworks = {};") write(app, "//= stub stub-frameworks\n//= require stub-jquery\napp = {};") write(jquery, "jquery = {};") asset_jquery = asset('stub-jquery.js', pipeline: :self) old_asset_frameworks_uri = asset('stub-frameworks.js').uri old_asset_app_uri = asset('stub-app.js').uri write(frameworks, "//= require stub-jquery\nframeworks = {};") # jquery never changed assert_equal asset_jquery.uri, asset('stub-jquery.js', pipeline: :self).uri refute_equal old_asset_frameworks_uri, asset('stub-frameworks.js').uri refute_equal old_asset_app_uri, asset('stub-app.js').uri end end test "requiring the same file multiple times has no effect" do assert_equal read("asset/project.js.erb")+"\n\n\n", asset("multiple.js").to_s end test "requiring index file directly and by alias includes it only once" do assert_equal "alert(1);\n\n\n", asset("index_alias/require.js").to_s end test "requiring index file by tree and by alias includes it only once" do assert_equal "alert(1);\n", asset("index_alias/require_tree.js").to_s end test "requiring a file of a different format raises an exception" do assert_raises Sprockets::FileNotFound do asset("mismatch.js") end end test "bundling joins files with blank line" do assert_equal "var Project = {\n find: function(id) {\n }\n};\nvar Users = {\n find: function(id) {\n }\n};\n\n\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n", asset("application.js").to_s end test "dependencies appear in the source before files that required them" do assert_match(/Project.+Users.+focus/m, asset("application.js").to_s) end test "processing a source file with no engine extensions" do assert_equal read("asset/users.js.erb"), asset("noengine.js").to_s end test "processing a source file with different content type extensions" do assert_equal read("asset/users.js.erb"), asset("es6_asset.js").to_s end test "processing a source file with different content type extensions 1" do assert_equal read("asset/users.js.erb") + "(function() {\n\n\n}).call(this);\n", asset("coffee_asset.js").to_s end test "processing a source file with unknown extensions" do assert_equal read("asset/users.js.erb") + "var jQuery;\n\n\n", asset("unknownexts.min.js").to_s end test "requiring a file with a relative path" do assert_equal read("asset/project.js.erb") + "\n", asset("relative/require.js").to_s end test "can't require files outside the load path" do assert !@env.paths.include?(fixture_path("default")), @env.paths.inspect assert_raises Sprockets::FileNotFound do asset("relative/require_outside_path.js") end end test "can't require files in another load path" do @env.append_path(fixture_path("default")) assert @env.paths.include?(fixture_path("default")), @env.paths.inspect assert_raises Sprockets::FileNotFound do asset("relative/require_other_load_path.js") end end test "can't require absolute files outside the load path" do assert_raises Sprockets::FileOutsidePaths do asset("absolute/require_outside_path.js").to_s end end test "require_directory requires all child files in alphabetical order" do assert_equal( "ok(\"b.js.erb\");\n", asset("tree/all_with_require_directory.js").to_s ) end test "require_directory current directory includes self last" do assert_equal( "var Bar;\nvar Foo;\nvar App;\n", asset("tree/directory/application.js").to_s ) end test "require_tree requires all descendant files in alphabetical order" do assert_equal( asset("tree/all_with_require.js").to_s, asset("tree/all_with_require_tree.js").to_s + "\n\n\n\n\n\n" ) end test "require_tree without an argument defaults to the current directory" do assert_equal( "a();\nb();\n", asset("tree/without_argument/require_tree_without_argument.js").to_s ) end test "require_tree with current directory includes self last" do assert_equal( "var Bar;\nvar Foo;\nvar App;\n", asset("tree/tree/application.js").to_s ) end test "require_tree with a logical path argument raises an exception" do assert_raises(Sprockets::ArgumentError) do asset("tree/with_logical_path/require_tree_with_logical_path.js").to_s end end test "require_tree with a nonexistent path raises an exception" do assert_raises(Sprockets::ArgumentError) do asset("tree/with_logical_path/require_tree_with_nonexistent_path.js").to_s end end test "require_tree with a nonexistent absolute path raises an exception" do assert_raises(Sprockets::ArgumentError) do asset("absolute/require_nonexistent_path.js").to_s end end test "require_tree respects order of child dependencies" do assert_equal( "var c;\nvar b;\nvar a;\n\n", asset("tree/require_tree_alpha.js").to_s ) end test "require_self inserts the current file's body at the specified point" do assert_equal "/* b.css */\nb { display: none }\n/*\n\n\n\n */\n\nbody {}\n.project {}\n", asset("require_self.css").to_s end test "multiple require_self directives raises and error" do assert_raises(Sprockets::ArgumentError) do asset("require_self_twice.css") end end test "linked asset depends on target asset" do assert asset = asset("require_manifest.js") assert_equal <<-EOS, asset.to_s define("application.js", "application-955b2dddd0d1449b1c617124b83b46300edadec06d561104f7f6165241b31a94.js") define("application.css", "application-46d50149c56fc370805f53c29f79b89a52d4cc479eeebcdc8db84ab116d7ab1a.css") define("POW.png", "POW-1da2e59df75d33d8b74c3d71feede698f203f136512cbaab20c68a5bdebd5800.png"); EOS assert_equal [ "file://#{fixture_path_for_uri("asset/POW.png")}?type=image/png&id=xxx", "file://#{fixture_path_for_uri("asset/application.css")}?type=text/css&id=xxx", "file://#{fixture_path_for_uri("asset/application.js")}?type=application/javascript&id=xxx" ], normalize_uris(asset.links) end test "directive linked asset depends on target asset" do assert asset = asset("require_manifest2.js") assert_equal <<-EOS, asset.to_s define("application.js", "application-955b2dddd0d1449b1c617124b83b46300edadec06d561104f7f6165241b31a94.js") define("application.css", "application-46d50149c56fc370805f53c29f79b89a52d4cc479eeebcdc8db84ab116d7ab1a.css") define("POW.png", "POW-1da2e59df75d33d8b74c3d71feede698f203f136512cbaab20c68a5bdebd5800.png"); EOS assert_equal [ "file://#{fixture_path_for_uri("asset/POW.png")}?type=image/png&id=xxx", "file://#{fixture_path_for_uri("asset/application.css")}?type=text/css&id=xxx", "file://#{fixture_path_for_uri("asset/application.js")}?type=application/javascript&id=xxx" ], normalize_uris(asset.links) end test "link_directory current directory includes self last" do assert_equal [ "file://#{fixture_path_for_uri("asset/link/directory/bar.js")}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri("asset/link/directory/foo.js")}?type=application/javascript&id=xxx" ], normalize_uris(asset("link/directory/application.js").links) end test "link_tree requires all descendant files in alphabetical order" do assert_equal normalize_uris(asset("link/all_with_require.js").links), normalize_uris(asset("link/all_with_require_tree.js").links) end test "link_tree without an argument defaults to the current directory" do assert_equal [ "file://#{fixture_path_for_uri("asset/link/without_argument/a.js")}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri("asset/link/without_argument/b.js")}?type=application/javascript&id=xxx" ], normalize_uris(asset("link/without_argument/require_tree_without_argument.js").links) end test "link_tree with current directory includes self last" do assert_equal [ "file://#{fixture_path_for_uri("asset/link/tree/bar.js")}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri("asset/link/tree/foo.js")}?type=application/javascript&id=xxx" ], normalize_uris(asset("link/tree/application.js").links) end test "link_tree with a logical path argument raises an exception" do assert_raises(Sprockets::ArgumentError) do asset("link/with_logical_path/require_tree_with_logical_path.js") end end test "link_tree with a nonexistent path raises an exception" do assert_raises(Sprockets::ArgumentError) do asset("link/with_logical_path/require_tree_with_nonexistent_path.js") end end test "link_directory requires all child files in alphabetical order" do assert_equal [ "file://#{fixture_path_for_uri("asset/link/all/README.md")}?id=xxx", "file://#{fixture_path_for_uri("asset/link/all/b.css")}?type=text/css&id=xxx", "file://#{fixture_path_for_uri("asset/link/all/b.js.erb")}?type=application/javascript+ruby&id=xxx" ], normalize_uris(asset("link/all_with_require_directory.js").links) end test "link_directory as app/js requires all child files in alphabetical order" do assert_equal [ "file://#{fixture_path_for_uri("asset/link/all/b.js.erb")}?type=application/javascript&id=xxx" ], normalize_uris(asset("link/all_with_require_directory_as_js.js").links) end test "link_tree respects order of child dependencies" do assert_equal [ "file://#{fixture_path_for_uri("asset/link/alpha/a.js")}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri("asset/link/alpha/b.js")}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri("asset/link/alpha/c.js")}?type=application/javascript&id=xxx" ], normalize_uris(asset("link/require_tree_alpha.js").links) end test "link_tree as app/js respects order of child dependencies" do assert_equal [ "file://#{fixture_path_for_uri("asset/link/alpha/a.js")}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri("asset/link/alpha/b.js")}?type=application/javascript&id=xxx", "file://#{fixture_path_for_uri("asset/link/alpha/c.js")}?type=application/javascript&id=xxx" ], normalize_uris(asset("link/require_tree_alpha_as_js.js").links) end test "link_asset with uri" do assert asset = asset("link/asset_uri.css") assert_equal <<-EOS, asset.to_s .logo { background: url(POW-1da2e59df75d33d8b74c3d71feede698f203f136512cbaab20c68a5bdebd5800.png); } EOS assert_equal [ "file://#{fixture_path_for_uri("asset/POW.png")}?type=image/png&id=xxx" ], normalize_uris(asset.links) end test "stub single dependency" do assert_equal "var jQuery.UI = {};\n\n\n", asset("stub/skip_jquery").to_s end test "stub dependency tree" do assert_equal "var Foo = {};\n\n\n\n", asset("stub/application").to_s end test "resolves circular link_tree" do assert_equal 'var A;', asset("circle_link_tree/a.js").to_s.chomp end test "resolves circular link_directory" do assert_equal 'var A;', asset("circle_link_directory/a.js").to_s.chomp end test "resolves circular link" do assert_equal 'var A;', asset("circle_link/a.js").to_s.chomp end test "resolves circular depend_on_asset" do assert_equal 'var A;', asset("circle_depend_on_asset/a.js").to_s.chomp end test "resolves circular requires" do assert_equal "var A;\nvar C;\nvar B;\n", asset("circle/a.js").to_s assert_equal "var B;\nvar A;\nvar C;\n", asset("circle/b.js").to_s assert_equal "var C;\nvar B;\nvar A;\n", asset("circle/c.js").to_s end test "unknown directives are ignored" do assert_equal "var Project = {\n find: function(id) {\n }\n};\n\n//\n// = Foo\n//\n// == Examples\n//\n// Foo.bar()\n// => \"baz\"\nvar Foo;\n", asset("unknown_directives.js").to_s end test "__FILE__ is properly set in templates" do assert_equal %(var filename = "#{fixture_path("asset/filename.js.erb")}";\n), asset("filename.js").to_s end test "asset inherits the format extension and content type of the original file" do asset = asset("project.js") assert_equal "application/javascript", asset.content_type end test "asset falls back to files default mime type" do asset = asset("default_mime_type.js") assert_equal "application/javascript", asset.content_type end test "asset is a rack response body" do body = "" asset("project.js").each { |part| body += part } assert_equal asset("project.js").to_s, body end test "asset length is source length with unicode characters" do assert_equal 8, asset("unicode.js").length end test "asset length is source bytesize with unicode characters" do assert_equal 8, asset("unicode.js").bytesize end test "asset digest" do assert asset("project.js").hexdigest end test "project digest path" do assert_equal "project-9f8d317511370805ee292b685e9bcc4227bb901f8fd6ce82157d1845651ff6da.js", asset("project.js").digest_path end test "multiple charset defintions are stripped from css bundle" do assert_equal "\n.foo {}\n\n.bar {}\n\n\n", asset("charset.css").to_s end test "appends missing semicolons" do expected = <<-EOS var Bar; (function() { var Foo }); EOS assert_equal expected, asset("semicolons.js").to_s end test 'keeps code in same line after multi-line comments' do expected = <<-EOS /******/ function foo() { }; EOS assert_equal expected, asset('multi_line_comment.js').to_s end test "should not fail if home is not set in environment" do begin home, ENV["HOME"] = ENV["HOME"], nil env = Sprockets::Environment.new env.append_path(fixture_path('asset')) env['application.js'] rescue Exception flunk ensure ENV["HOME"] = home end end def asset(logical_path, options = {}) @env.find_asset(logical_path, **{pipeline: @pipeline}.merge(options)) end def read(logical_path) File.read(fixture_path(logical_path)) end end class PreDigestedAssetTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('asset')) @env.cache = {} @pipeline = nil end test "digest path" do path = File.expand_path("test/fixtures/asset/application") original = "#{path}.js" digested = "#{path}-d41d8cd98f00b204e9800998ecf8427e.digested.js" FileUtils.cp(original, digested) assert_equal "application-d41d8cd98f00b204e9800998ecf8427e.digested.js", asset("application-d41d8cd98f00b204e9800998ecf8427e.digested.js").digest_path ensure FileUtils.rm(digested) if File.exist?(digested) end test "digest base32 path" do path = File.expand_path("test/fixtures/asset/application") original = "#{path}.js" digested = "#{path}-TQDC3LZV.digested.js" FileUtils.cp(original, digested) assert_equal "application-TQDC3LZV.digested.js", asset("application-TQDC3LZV.digested.js").digest_path ensure FileUtils.rm(digested) if File.exist?(digested) end def asset(logical_path, options = {}) @env.find_asset(logical_path, **{pipeline: @pipeline}.merge(options)) end end class DebugAssetTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('asset')) @env.cache = {} @pipeline = :debug @asset = @env.find_asset('application.js', pipeline: :debug) end include AssetTests test "uri" do assert_equal "file://#{fixture_path_for_uri('asset/application.js')}?type=application/javascript&pipeline=debug&id=xxx", normalize_uri(@asset.uri) end test "logical path" do assert_equal "application.debug.js", @asset.logical_path end test "digest path" do assert_equal "application.debug-dee339ce0ee2dc7cdfa0d36dff3ef946cebe1bd3e414515d40c3cafc49c0a51a.js", @asset.digest_path end test "content type" do assert_equal "application/javascript", @asset.content_type end test "length" do assert_equal 265, @asset.length end test "charset is UTF-8" do assert_equal 'utf-8', @asset.charset end test "to_s" do expected = <<-EOS var Project = { find: function(id) { } }; var Users = { find: function(id) { } }; document.on('dom:loaded', function() { $('search').focus(); }); //# sourceMappingURL=application.js-95e519d4e0f0a5c4c7d24787ded990b0d027f7ad30b39f402c4c5e3196a41e8b.map EOS assert_equal expected, @asset.to_s end def asset(logical_path, options = {}) @env.find_asset(logical_path, {pipeline: @pipeline}.merge(options)) end def read(logical_path) File.read(fixture_path(logical_path)) end end class AssetLogicalPathTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('paths')) end test "logical path" do assert_equal "empty", logical_path("empty") assert_equal "application.js", logical_path("application.js") assert_equal "application.css", logical_path("application.css") assert_equal "application.js", logical_path("application.js.erb", accept: "application/javascript") assert_equal "application.js", logical_path("application.coffee", accept: "application/javascript") assert_equal "application.css", logical_path("application.scss", accept: "text/css") assert_equal "project.js", logical_path("project.coffee.erb", accept: "application/javascript") assert_equal "store.css", logical_path("store.css.erb", accept: "text/css") assert_equal "store.foo", logical_path("store.foo") assert_equal "files.html", logical_path("files.erb", accept: "text/html") assert_equal "application.js", logical_path("application.coffee", accept: "application/javascript") assert_equal "application.css", logical_path("application.scss", accept: "text/css") assert_equal "hello.js", logical_path("hello.jst.ejs", accept: "application/javascript") assert_equal "bower/main.js", logical_path("bower/main.js") assert_equal "bower/bower.json", logical_path("bower/bower.json") assert_equal "coffee/index.js", logical_path("coffee/index.js") assert_equal "coffee/foo.js", logical_path("coffee/foo.coffee", accept: "application/javascript") assert_equal "jquery.js", logical_path("jquery.js") assert_equal "jquery.min.js", logical_path("jquery.min.js") assert_equal "jquery.csv.js", logical_path("jquery.csv.js") assert_equal "jquery.csv.min.js", logical_path("jquery.csv.min.js") assert_equal "jquery.foo.min.js", logical_path("jquery.foo.min.js") assert_equal "jquery.tmpl.js", logical_path("jquery.tmpl.js") assert_equal "jquery.tmpl.min.js", logical_path("jquery.tmpl.min.js") assert_equal "jquery.ext/index.js", logical_path("jquery.ext/index.js") assert_equal "jquery.ext/form.js", logical_path("jquery.ext/form.js") assert_equal "jquery-coffee.min.js", logical_path("jquery-coffee.min.coffee", accept: "application/javascript") assert_equal "jquery-custom.min.js", logical_path("jquery-custom.min.js.erb", accept: "application/javascript") assert_equal "jquery.js.min", logical_path("jquery.js.min") assert_equal "all.coffee/plain.js", logical_path("all.coffee/plain.js") assert_equal "all.coffee/hot.js", logical_path("all.coffee/hot.coffee", accept: "application/javascript") assert_equal "all.coffee/index.js", logical_path("all.coffee/index.coffee", accept: "application/javascript") assert_equal "sprite.css.embed", logical_path("sprite.css.embed") assert_equal "traceur.js", logical_path("traceur.es6", accept: "application/javascript") end def logical_path(path, options = {}) filename = fixture_path("paths/#{path}") assert File.exist?(filename), "#{filename} does not exist" silence_warnings do assert asset = @env.find_asset(filename, **options), "couldn't find asset: #{filename}" asset.logical_path end end end class AssetContentTypeTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('paths')) end test "content type" do assert_nil content_type("empty") assert_equal "application/javascript", content_type("application.js") assert_equal "text/css", content_type("application.css") assert_equal "application/javascript", content_type("application.js.erb", accept: "application/javascript") assert_equal "application/javascript", content_type("application.coffee", accept: "application/javascript") assert_equal "text/css", content_type("application.scss", accept: "text/css") assert_equal "application/javascript", content_type("project.coffee.erb", accept: "application/javascript") assert_equal "text/css", content_type("store.css.erb", accept: "text/css") assert_equal "text/html", content_type("files.erb", accept: "text/html") assert_nil content_type("store.foo") assert_equal "application/javascript", content_type("application.coffee", accept: "application/javascript") assert_equal "text/css", content_type("application.scss", accept: "text/css") assert_equal "application/javascript", content_type("hello.jst.ejs", accept: "application/javascript") assert_equal "application/javascript", content_type("bower/main.js") assert_equal "application/json", content_type("bower/bower.json") assert_equal "application/javascript", content_type("coffee/index.js") assert_equal "application/javascript", content_type("coffee/foo.coffee", accept: "application/javascript") assert_equal "application/javascript", content_type("jquery.js") assert_equal "application/javascript", content_type("jquery.min.js") assert_equal "application/javascript", content_type("jquery.csv.js") assert_equal "application/javascript", content_type("jquery.csv.min.js") assert_equal "application/javascript", content_type("jquery.foo.min.js") assert_equal "application/javascript", content_type("jquery.tmpl.js") assert_equal "application/javascript", content_type("jquery.tmpl.min.js") assert_equal "application/javascript", content_type("jquery.ext/index.js") assert_equal "application/javascript", content_type("jquery.ext/form.js") assert_equal "application/javascript", content_type("jquery-coffee.min.coffee", accept: "application/javascript") assert_equal "application/javascript", content_type("jquery-custom.min.js.erb", accept: "application/javascript") assert_nil content_type("jquery.js.min") assert_equal "application/javascript", content_type("all.coffee/plain.js") assert_equal "application/javascript", content_type("all.coffee/hot.coffee", accept: "application/javascript") assert_equal "application/javascript", content_type("all.coffee/index.coffee", accept: "application/javascript") assert_nil content_type("sprite.css.embed") assert_equal "application/javascript", content_type("traceur.es6", accept: "application/javascript") end def content_type(path, options = {}) filename = fixture_path("paths/#{path}") assert File.exist?(filename), "#{filename} does not exist" silence_warnings do assert asset = @env.find_asset(filename, **options), "couldn't find asset: #{filename}" asset.content_type end end end sprockets-4.2.1/test/test_babel_processor.rb000066400000000000000000000135031447572140400212540ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/babel_processor' class TestBabelProcessor < Minitest::Test def setup @env = Sprockets::Environment.new @env.append_path File.expand_path("../fixtures", __FILE__) end def test_compile_es6_features_to_es5 input = { environment: @env, content_type: 'application/ecmascript-6', data: "const square = (n) => n * n", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod.source-XYZ.es6" } assert js = Sprockets::BabelProcessor.call(input)[:data] assert_match(/var square/, js) assert_match(/function/, js) end def test_transform_arrow_function input = { environment: @env, content_type: 'application/ecmascript-6', data: "var square = (n) => n * n", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod.source-XYZ.es6" } assert js = Sprockets::BabelProcessor.call(input)[:data] assert_equal <<-JS.chomp, js.strip var square = function square(n) { return n * n; }; JS end def test_common_modules input = { environment: @env, content_type: 'application/ecmascript-6', data: "import \"foo\";", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod.source-XYZ.es6" } assert js = Sprockets::BabelProcessor.new('modules' => 'common').call(input)[:data] assert_equal <<-JS.chomp, js.strip require("foo"); JS end def test_amd_modules input = { environment: @env, content_type: 'application/ecmascript-6', data: "import \"foo\";", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod.source-XYZ.es6" } assert js = Sprockets::BabelProcessor.new('modules' => 'amd').call(input)[:data] assert_equal <<-JS.chomp, js.strip define(["exports", "foo"], function (exports, _foo) {}); JS end def test_amd_modules_with_ids input = { environment: @env, content_type: 'application/ecmascript-6', data: "import \"foo\";", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod.source-XYZ.es6" } assert js = Sprockets::BabelProcessor.new('modules' => 'amd', 'moduleIds' => true).call(input)[:data] assert_equal <<-JS.chomp, js.strip define("mod", ["exports", "foo"], function (exports, _foo) {}); JS end def test_system_modules input = { environment: @env, content_type: 'application/ecmascript-6', data: "import \"foo\";", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod.source-XYZ.es6" } assert js = Sprockets::BabelProcessor.new('modules' => 'system').call(input)[:data] assert_equal <<-JS.chomp, js.strip System.register(["foo"], function (_export) { return { setters: [function (_foo) {}], execute: function () {} }; }); JS end def test_system_modules_with_ids input = { environment: @env, content_type: 'application/ecmascript-6', data: "import \"foo\";", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod.source-XYZ.es6" } assert js = Sprockets::BabelProcessor.new('modules' => 'system', 'moduleIds' => true).call(input)[:data] assert_equal <<-JS.chomp, js.strip System.register("mod", ["foo"], function (_export) { return { setters: [function (_foo) {}], execute: function () {} }; }); JS end def test_caching_takes_filename_into_account mod1 = { environment: @env, content_type: 'application/ecmascript-6', data: "import \"foo\";", metadata: {}, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/mod1.es6", __FILE__), cache: Sprockets::Cache.new, source_path: "mod1.source-XYZ.es6" } mod2 = mod1.dup mod2[:filename] = File.expand_path("../fixtures/mod2.es6", __FILE__) assert js1 = Sprockets::BabelProcessor.new('modules' => 'system', 'moduleIds' => true).call(mod1)[:data] assert_equal <<-JS.chomp, js1.to_s.strip System.register("mod1", ["foo"], function (_export) { return { setters: [function (_foo) {}], execute: function () {} }; }); JS assert js2 = Sprockets::BabelProcessor.new('modules' => 'system', 'moduleIds' => true).call(mod2)[:data] assert_equal <<-JS.chomp, js2.to_s.strip System.register("mod2", ["foo"], function (_export) { return { setters: [function (_foo) {}], execute: function () {} }; }); JS end def test_cache_key assert Sprockets::BabelProcessor.cache_key amd_processor_1 = Sprockets::BabelProcessor.new('modules' => 'amd') amd_processor_2 = Sprockets::BabelProcessor.new('modules' => 'amd') assert_equal amd_processor_1.cache_key, amd_processor_2.cache_key system_processor = Sprockets::BabelProcessor.new('modules' => 'system') refute_equal amd_processor_1.cache_key, system_processor.cache_key end end sprockets-4.2.1/test/test_bundle.rb000066400000000000000000000076451447572140400173730ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' class TestStylesheetBundle < Sprockets::TestCase test "bundle single stylesheet file" do environment = Sprockets::Environment.new environment.append_path fixture_path('asset') filename = fixture_path('asset/project.css') assert File.exist?(filename) input = { environment: environment, uri: "file://#{filename}?type=text/css", filename: filename, load_path: fixture_path('asset'), content_type: 'text/css', metadata: {} } data = ".project {}\n" result = Sprockets::Bundle.call(input) assert_equal data, result[:data] assert_equal ["file-digest://#{uri_path(filename)}"], result[:dependencies].to_a.sort.select { |dep| dep.start_with?("file-digest:") } end test "bundle multiple stylesheet files" do environment = Sprockets::Environment.new environment.append_path fixture_path('asset') filename = fixture_path('asset/require_self.css') assert File.exist?(filename) input = { environment: environment, uri: "file://#{filename}?type=text/css", filename: filename, load_path: fixture_path('asset'), content_type: 'text/css', metadata: {} } data = "/* b.css */\nb { display: none }\n/*\n\n\n\n */\n\nbody {}\n.project {}\n" result = Sprockets::Bundle.call(input) assert_equal data, result[:data] assert_equal [ "file-digest://" + fixture_path_for_uri('asset'), "file-digest://" + fixture_path_for_uri('asset/project'), "file-digest://" + fixture_path_for_uri('asset/project.css'), "file-digest://" + fixture_path_for_uri('asset/require_self.css'), "file-digest://" + fixture_path_for_uri('asset/tree/all'), "file-digest://" + fixture_path_for_uri('asset/tree/all/b'), "file-digest://" + fixture_path_for_uri('asset/tree/all/b.css') ], result[:dependencies].to_a.sort.select { |dep| dep.start_with?("file-digest:") } end test "bundle single javascript file" do environment = Sprockets::Environment.new environment.append_path fixture_path('asset') filename = fixture_path('asset/project.js.erb') assert File.exist?(filename) input = { environment: environment, uri: "file://#{filename}?type=application/javascript", filename: filename, load_path: fixture_path('asset'), content_type: 'application/javascript', metadata: {} } data = "var Project = {\n find: function(id) {\n }\n};\n" result = Sprockets::Bundle.call(input) assert_equal data, result[:data] assert_equal ["file-digest://#{uri_path(filename)}"], result[:dependencies].to_a.sort.select { |dep| dep.start_with?("file-digest:") } end test "bundle multiple javascript files" do environment = Sprockets::Environment.new environment.append_path fixture_path('asset') filename = fixture_path('asset/application.js') assert File.exist?(filename) input = { environment: environment, uri: "file://#{filename}?type=application/javascript", filename: filename, load_path: fixture_path('asset'), content_type: 'application/javascript', metadata: {} } data = "var Project = {\n find: function(id) {\n }\n};\nvar Users = {\n find: function(id) {\n }\n};\n\n\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n" result = Sprockets::Bundle.call(input) assert_equal data, result[:data] assert_equal [ "file-digest://" + fixture_path_for_uri('asset'), "file-digest://" + fixture_path_for_uri('asset/application.js'), "file-digest://" + fixture_path_for_uri('asset/project'), "file-digest://" + fixture_path_for_uri('asset/project.js.erb'), "file-digest://" + fixture_path_for_uri('asset/users'), "file-digest://" + fixture_path_for_uri('asset/users.js.erb') ], result[:dependencies].to_a.sort.select { |dep| dep.start_with?("file-digest:") } end end sprockets-4.2.1/test/test_cache_store.rb000066400000000000000000000114501447572140400203660ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'tmpdir' require 'fileutils' require 'sprockets/cache' require 'sprockets/cache/file_store' require 'sprockets/cache/memory_store' require 'sprockets/cache/null_store' module CacheStoreNullTests def test_read refute @store.get("foo") end def test_write result = @store.set("foo", "bar") assert_equal "bar", result end def test_write_and_read_miss @store.set("foo", "bar") refute @store.get("foo") end def test_fetch result = @store.fetch("foo") { "bar" } assert_equal "bar", result end def test_clear result = @store.clear assert result, "@store.clear returned #{result} instead of true" end end module CacheStoreTests def test_read_miss refute @store.get("missing") end def test_write result = @store.set("foo", "bar") assert_equal "bar", result end def test_write_and_read_hit @store.set("foo", "bar") assert_equal "bar", @store.get("foo") end def test_multiple_write_and_read_hit @store.set("foo", "1") @store.set("bar", "2") @store.set("baz", "3") assert_equal "1", @store.get("foo") assert_equal "2", @store.get("bar") assert_equal "3", @store.get("baz") end def test_large_write_and_read_hit data = ("a"..."zzz").to_a.join @store.set("foo", data) assert_equal data, @store.get("foo") end def test_delete @store.set("foo", "bar") assert_equal "bar", @store.get("foo") @store.set("foo", nil) assert_nil @store.get("foo") end def test_fetch result = @store.fetch("user") { "josh" } assert_equal "josh", result end def test_clear @store.set("foo", "bar") assert_equal "bar", @store.get("foo") result = @store.clear assert result, "@store.clear returned #{result} instead of true" assert_nil @store.get("foo") end end class TestNullStore < Minitest::Test def setup @_store = Sprockets::Cache::NullStore.new @store = Sprockets::Cache.new(Sprockets::Cache::NullStore.new) end def test_inspect assert_equal "# store=#>", @store.inspect assert_equal "#", @_store.inspect end include CacheStoreNullTests end class TestMemoryStore < Minitest::Test def setup @_store = Sprockets::Cache::MemoryStore.new @store = Sprockets::Cache.new(@_store) end def test_inspect assert_equal "#", @_store.inspect end include CacheStoreTests def test_get_with_lru @_store.set(:a, 1) @_store.set(:b, 2) @_store.set(:c, 3) assert_equal [1, 2, 3], @_store.instance_variable_get(:@cache).values @_store.get(:a) @_store.set(:d, 4) assert_equal [2, 3, 1, 4], @_store.instance_variable_get(:@cache).values end def test_set_with_lru @_store.set(:a, 1) @_store.set(:b, 2) @_store.set(:c, 3) assert_equal [1, 2, 3], @_store.instance_variable_get(:@cache).values @_store.set(:a, 1) @_store.set(:d, 4) assert_equal [2, 3, 1, 4], @_store.instance_variable_get(:@cache).values end end class TestZeroMemoryStore < Minitest::Test def setup @_store = Sprockets::Cache::MemoryStore.new(0) @store = Sprockets::Cache.new(@_store) end def test_inspect assert_equal "#", @_store.inspect end include CacheStoreNullTests end class TestFileStore < Minitest::Test def setup @root = Dir::mktmpdir "sprockets-file-store" @_store = Sprockets::Cache::FileStore.new(@root) @store = Sprockets::Cache.new(@_store) end def teardown FileUtils.rm_rf(@root) end def test_inspect Dir::mktmpdir "sprockets-file-store-inspect" do |dir| store = Sprockets::Cache::FileStore.new(dir) assert_equal "#", store.inspect end end def test_corrupted_read File.write(File.join(@root, "corrupt.cache"), "w") do |f| f.write("corrupt") end refute @_store.get("corrupt") end def test_clear_store_dir_not_exist @cache_dir = File.join(Dir::tmpdir, 'sprockets') refute File.exist?(@cache_dir) @store = Sprockets::Cache::FileStore.new(@cache_dir) assert @store.clear end include CacheStoreTests end class TestZeroFileStore < Minitest::Test def setup @tmpdir = Dir::mktmpdir "sprockets-file-store-zero" @_store = Sprockets::Cache::FileStore.new(@tmpdir, 0) @store = Sprockets::Cache.new(@_store) end def teardown FileUtils.rm_rf @tmpdir end def test_inspect Dir.mktmpdir "sprockets-file-store-inspect" do |dir| store = Sprockets::Cache::FileStore.new(dir, 0) assert_equal "#", store.inspect end end include CacheStoreNullTests end sprockets-4.2.1/test/test_caching.rb000066400000000000000000000367671447572140400175250ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' class TestCaching < Sprockets::TestCase def setup reset end def reset @cache = {} @env1 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path(".") env.cache = @cache end @env2 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path(".") env.cache = @cache end end test "same environment instance cache objects are equal" do env = @env1 asset1 = env['gallery.js'] asset2 = env['gallery.js'] assert asset1 assert asset2 assert asset1.eql?(asset2) assert asset2.eql?(asset1) end test "same cached instance cache objects are equal" do cached = @env1.cached asset1 = cached['gallery.js'] asset2 = cached['gallery.js'] assert asset1 assert asset2 assert asset1.eql?(asset2) assert asset2.eql?(asset1) end test "same environment instance is cached at logical and expanded path" do env = @env1 asset1 = env['gallery.js'] asset2 = env[asset1.filename] assert asset1 assert asset2 assert asset1.eql?(asset2) assert asset2.eql?(asset1) end test "same cached instance is cached at logical and expanded path" do cached = @env1.cached asset1 = cached['gallery.js'] asset2 = cached[asset1.filename] assert asset1 assert asset2 assert asset1.eql?(asset2) assert asset2.eql?(asset1) end test "shared cache objects are eql" do asset1 = @env1['gallery.js'] asset2 = @env2['gallery.js'] assert asset1 assert asset2 assert asset1.eql?(asset2) assert asset2.eql?(asset1) assert !asset1.equal?(asset2) end test "keys are different if environment digest changes" do @env1['gallery.js'] old_keys = @cache.keys.sort @cache.clear @env2.version = '2.0' @env2['gallery.js'] new_keys = @cache.keys.sort refute_equal old_keys, new_keys end class MockProcessor attr_reader :cache_key def initialize(cache_key) @cache_key = cache_key end def call(input) end end test "asset ids are different if preprocessor is added" do assert asset1 = @env1['gallery.js'] @env2.register_preprocessor 'application/javascript', MockProcessor.new('1.0') assert asset2 = @env2['gallery.js'] refute_equal asset1.id, asset2.id end test "asset ids are different if postprocessor is added" do assert asset1 = @env1['gallery.js'] @env2.register_postprocessor 'application/javascript', MockProcessor.new('1.0') assert asset2 = @env2['gallery.js'] refute_equal asset1.id, asset2.id end test "asset ids are different if bundle processor is added" do assert asset1 = @env1['gallery.js'] @env2.register_bundle_processor 'application/javascript', MockProcessor.new('1.0') assert asset2 = @env2['gallery.js'] refute_equal asset1.id, asset2.id end test "asset ids are different if processor cache key changes" do @env1.register_preprocessor 'application/javascript', MockProcessor.new('1.0') assert asset1 = @env1['gallery.js'] @env2.register_preprocessor 'application/javascript', MockProcessor.new('2.0') assert asset2 = @env2['gallery.js'] refute_equal asset1.id, asset2.id end test "unknown cache keys are ignored" do @env1.register_dependency_resolver 'foo-version' do |env| 1 end @env1.depend_on 'foo-version' assert asset1 = @env1['gallery.js'] assert asset1.metadata[:dependencies].include?('foo-version') assert asset2 = @env2['gallery.js'] refute asset2.metadata[:dependencies].include?('foo-version') refute_equal asset1.id, asset2.id end test "assets from different load paths are not equal" do # Normalize test fixture mtimes mtime = File.stat(fixture_path("default/app/main.js")).mtime.to_i File.utime(mtime, mtime, fixture_path("default/vendor/gems/jquery-2-0/jquery.js")) File.utime(mtime, mtime, fixture_path("default/vendor/gems/jquery-1-9/jquery.js")) env1 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.append_path("vendor/gems/jquery-1-9") env.cache = @cache end env2 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.append_path("vendor/gems/jquery-2-0") env.cache = @cache end refute_equal env1.find_asset("main.js"), env2.find_asset("main.js") end test "stale cached asset isn't loaded if file is remove" do filename = fixture_path("default/tmp.js") sandbox filename do File.open(filename, 'w') { |f| f.write "foo;\n" } assert_equal "foo;\n", @env1["tmp.js"].to_s File.unlink(filename) assert_nil @env2["tmp.js"] end end test "stale cached asset isn't loaded if dependency is changed and removed" do foo = fixture_path("default/foo-tmp.js") bar = fixture_path("default/bar-tmp.js") sandbox foo, bar do File.open(foo, 'w') { |f| f.write "//= require bar-tmp\nfoo;\n" } File.open(bar, 'w') { |f| f.write "bar;\n" } assert_equal "bar;\nfoo;\n", @env1["foo-tmp.js"].to_s assert_equal "bar;\n", @env1["bar-tmp.js"].to_s # File.open(foo, 'w') { |f| f.write "foo;\n" } File.unlink(bar) assert_nil @env2["bar-tmp.js"] assert_raises Sprockets::FileNotFound do @env1["foo-tmp.js"].to_s end end end test "stale cached asset isn't loaded if removed from path" do env1 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.append_path("vendor") env.cache = @cache end env2 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.cache = @cache end assert_equal "jQuery;\n", env1["main.js"].to_s assert_equal "jQuery;\n", env1["jquery.js"].to_s assert_raises Sprockets::FileNotFound do env2["main.js"].to_s end end test "add/remove file to shadow vendor" do @env = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.append_path("vendor") env.cache = @cache end patched_jquery = fixture_path('default/app/jquery.js') sandbox patched_jquery do File.utime(1421000000, 1422000000, File.dirname(patched_jquery)) asset = @env["jquery.js"] assert_equal fixture_path('default/vendor/jquery.js'), asset.filename assert_equal "jQuery;\n", asset.to_s asset = @env["main.js"] assert_equal fixture_path('default/app/main.js'), asset.filename assert_equal "jQuery;\n", asset.to_s write(patched_jquery, "jQueryFixed;\n", 1422000010) asset = @env["main.js"] assert_equal fixture_path('default/app/main.js'), asset.filename assert_equal "jQueryFixed;\n", asset.to_s asset = @env["jquery.js"] assert_equal fixture_path('default/app/jquery.js'), asset.filename assert_equal "jQueryFixed;\n", asset.to_s File.unlink(patched_jquery) File.utime(1421000020, 1422000020, File.dirname(patched_jquery)) asset = @env["jquery.js"] assert_equal fixture_path('default/vendor/jquery.js'), asset.filename assert_equal "jQuery;\n", asset.to_s asset = @env["main.js"] assert_equal fixture_path('default/app/main.js'), asset.filename assert_equal "jQuery;\n", asset.to_s end end test "add/remove index file to shadow vendor" do @env = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.append_path("vendor") env.cache = @cache end patched_jquery = fixture_path('default/app/jquery/index.js') sandbox File.dirname(patched_jquery), patched_jquery do File.utime(1423000000, 1423000000, File.dirname(File.dirname(patched_jquery))) asset = @env["jquery.js"] assert_equal fixture_path('default/vendor/jquery.js'), asset.filename assert_equal "jQuery;\n", asset.to_s asset = @env["main.js"] assert_equal fixture_path('default/app/main.js'), asset.filename assert_equal "jQuery;\n", asset.to_s FileUtils.mkdir(File.dirname(patched_jquery)) write(patched_jquery, "jQueryFixed;\n", 1423000010) asset = @env["main.js"] assert_equal fixture_path('default/app/main.js'), asset.filename assert_equal "jQueryFixed;\n", asset.to_s asset = @env["jquery.js"] assert_equal fixture_path('default/app/jquery/index.js'), asset.filename assert_equal "jQueryFixed;\n", asset.to_s File.unlink(patched_jquery) File.utime(1423000020, 1423000020, File.dirname(patched_jquery)) asset = @env["jquery.js"] assert_equal fixture_path('default/vendor/jquery.js'), asset.filename assert_equal "jQuery;\n", asset.to_s asset = @env["main.js"] assert_equal fixture_path('default/app/main.js'), asset.filename assert_equal "jQuery;\n", asset.to_s end end test "separate cache for dependencies under a different load path" do env1 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.append_path("vendor/gems/jquery-1-9") env.cache = @cache end env2 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path("app") env.append_path("vendor/gems/jquery-2-0") env.cache = @cache end assert main = env1.find_asset("main.js") assert_equal "var jQuery;\n", main.to_s assert_equal fixture_path('default/app/main.js'), main.filename assert_equal main, env1.load(main.uri) assert_equal main, env1.find_asset("main.js") assert main = env2.find_asset("main.js") assert_equal "var jQuery;\n", main.to_s assert_equal fixture_path('default/app/main.js'), main.filename assert_equal main, env2.load(main.uri) assert_equal main, env2.find_asset("main.js") end test "environment cache resolver evaluated on load" do env = @env1 assert asset1 = env['rand.js'] assert asset2 = env['rand.js'] refute_equal asset1.id, asset2.id end test "cached environment cache resolver evaluated onced" do env = @env1.cached assert asset1 = env['rand.js'] assert asset2 = env['rand.js'] assert_equal asset1.id, asset2.id end end require 'tmpdir' class TestFileStoreCaching < Sprockets::TestCase def setup @cache_dir = File.join(Dir::tmpdir, 'sprockets') @cache = Sprockets::Cache::FileStore.new(@cache_dir) end def teardown FileUtils.remove_entry(@cache_dir) if File.exist?(@cache_dir) end test "shared cache objects are eql" do @env1 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path(".") env.cache = @cache end @env2 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path(".") env.cache = @cache end asset1 = @env1['gallery.js'] asset2 = @env2['gallery.js'] assert asset1 assert asset2 assert asset1.eql?(asset2) assert asset2.eql?(asset1) assert !asset1.equal?(asset2) end test "history cache is not polluted" do dependency_file = File.join(fixture_path('asset'), "dependencies", "a.js") env1 = Sprockets::Environment.new(fixture_path('asset')) do |env| env.append_path(".") env.cache = @cache end env1['required_assets.js'] sandbox dependency_file do write(dependency_file, "var aa = 2;") env1['required_assets.js'] # We must use private APIs to test this behavior # https://github.com/rails/sprockets/pull/141 cache_entries = @cache.send(:find_caches).map do |file, _| key = file.gsub(/\.cache\z/, ''.freeze).split(@cache_dir).last result = @cache.get(key) if result.is_a?(Array) result if result.first.is_a?(Set) else nil end end.compact assert cache_entries.any? cache_entries.each do |sets| sets.each do |set| refute set.any? {|uri| uri.include?(env1.root) }, "Expected entry in cache to not include absolute paths but did: #{set.inspect}" end end end end test "no absolute paths are stored in the cache by accident" do environment = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path(".") env.cache = @cache end cache = environment.cache def cache.set(key, value, local = false) if value.to_s.match?(/#{Dir.pwd}/) raise "Expected '#{value}' to not contain absolute path '#{Dir.pwd}' but did" end end environment['schneems.js'] end test "no relative paths are present in an asset loaded from cache by accident" do environment = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path(".") env.cache = @cache end environment['schneems.js'] asset = environment['schneems.js'] asset_hash = asset.to_hash refute has_relative_value?(asset_hash), "Expected asset from cache to have no relative paths, but it does:\n#{asset_hash}" end def has_relative_value?(elem) if elem.is_a?(Hash) return elem.values.detect {|e| has_relative_value?(e) } end if elem.respond_to?(:each) return elem.each.detect {|e| has_relative_value?(e) } end return elem.to_s.match(/file:\/\/[^\/]/) end test "no absolute paths are retuned from cache" do env1 = Sprockets::Environment.new(fixture_path('default')) do |env| env.append_path(".") env.cache = @cache end asset1 = env1['schneems.js'] Dir.mktmpdir do |dir| env2 = Sprockets::Environment.new(dir) do |env| env.append_path(dir) env.cache = @cache end FileUtils.cp_r(env1.root + "/.", env2.root) asset2 = env2['schneems.js'] assert asset1 assert asset2 assert_equal asset1.digest_path, asset2.digest_path assert_equal asset1.source, asset2.source assert_equal asset1.hexdigest, asset2.hexdigest # Absolute paths should be different refute_equal asset1.uri, asset2.uri refute_equal asset1.filename, asset2.filename refute_equal asset1.metadata[:included], asset2.metadata[:included] refute_equal asset1.to_hash[:load_path], asset2.to_hash[:load_path] refute_equal asset1.metadata[:dependencies], asset2.metadata[:dependencies] refute_equal asset1.metadata[:links], asset2.metadata[:links] # The metadata[:stubbed] and metadata[:required] cannot be # observed directly, they are included in the `dependencies`. # We must use private APIs to test this behavior # https://github.com/rails/sprockets/issues/96#issuecomment-133097865 cache_entries = @cache.send(:find_caches).map do |file, _| key = file.gsub(/\.cache\z/, ''.freeze).split(@cache_dir).last result = @cache.get(key) result.is_a?(Hash) ? result : nil end.compact required = cache_entries.map do |asset| asset[:metadata][:required] if asset[:metadata] end.compact required.each do |set| refute set.any? {|uri| uri.include?(env1.root) || uri.include?(env2.root)}, "Expected 'required' entry in cache to not include absolute paths but did: #{set.inspect}" end stubbed = cache_entries.map do |asset| asset[:metadata][:stubbed] if asset[:metadata] end.compact stubbed.each do |set| refute set.any? {|uri| uri.include?(env1.root) || uri.include?(env2.root)}, "Expected 'stubbed' entry in cache to not include absolute paths but did: #{set.inspect}" end end end end sprockets-4.2.1/test/test_closure_compressor.rb000066400000000000000000000011111447572140400220300ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/closure_compressor' class TestClosureCompressor < Minitest::Test def test_compress_javascript input = { data: "function foo() {\n return true;\n}", cache: Sprockets::Cache.new } output = "function foo(){return!0};\n" begin assert_equal output, Sprockets::ClosureCompressor.call(input) rescue Closure::Error skip "No Java runtime present" end end def test_cache_key assert Sprockets::ClosureCompressor.cache_key end end sprockets-4.2.1/test/test_coffee_script_processor.rb000066400000000000000000000035201447572140400230200ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/coffee_script_processor' require 'sprockets/source_map_utils' class TestCoffeeScriptProcessor < Minitest::Test def setup @env = Sprockets::Environment.new @env.append_path File.expand_path("../fixtures", __FILE__) end def test_compile_coffee_script_template_to_js input = { load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/squared.coffee", __FILE__), content_type: 'application/javascript', environment: @env, data: "square = (n) -> n * n", name: 'squared', cache: Sprockets::Cache.new, metadata: { mapping: [] } } result = Sprockets::CoffeeScriptProcessor.call(input) assert result[:data].match(/var square/) assert_equal 13, Sprockets::SourceMapUtils.decode_source_map(result[:map])[:mappings].size assert_equal ["squared.source.coffee"], result[:map]["sources"] end def test_changing_map_sources_for_files_with_same_content input = { load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/squared.coffee", __FILE__), content_type: 'application/javascript', environment: @env, data: "square = (n) -> n * n", name: 'squared', cache: Sprockets::Cache.new, metadata: { mapping: [] } } result = Sprockets::CoffeeScriptProcessor.call(input) assert_equal ["squared.source.coffee"], result[:map]["sources"] input[:filename] = File.expand_path("../fixtures/peterpan.coffee", __FILE__) result = Sprockets::CoffeeScriptProcessor.call(input) assert_equal ["peterpan.source.coffee"], result[:map]["sources"] end def test_cache_key assert Sprockets::CoffeeScriptProcessor.cache_key end end sprockets-4.2.1/test/test_context.rb000066400000000000000000000112741447572140400175770ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'yaml' class TestContext < Sprockets::TestCase def setup @env = Sprockets::Environment.new(".") @env.append_path(fixture_path('context')) end test "context environment is cached" do instances = @env["environment.js"].to_s.split("\n") assert_match "Sprockets::CachedEnvironment", instances[0] assert_equal instances[0], instances[1] end test "source file properties are exposed in context" do json = @env["properties.js"].to_s.chomp.chop assert_equal({ 'filename' => fixture_path("context/properties.js.erb"), '__FILE__' => fixture_path("context/properties.js.erb"), 'root_path' => fixture_path("context"), 'logical_path' => "properties", 'content_type' => "application/javascript" }, YAML.load(json)) end test "source file properties are exposed in context when path contains periods" do json = @env["properties.with.periods.js"].to_s.chomp.chop assert_equal({ 'filename' => fixture_path("context/properties.with.periods.js.erb"), '__FILE__' => fixture_path("context/properties.with.periods.js.erb"), 'root_path' => fixture_path("context"), 'logical_path' => "properties.with.periods", 'content_type' => "application/javascript" }, YAML.load(json)) end test "asset_data_uri encodes svg using optimized URI-escaping" do assert_equal(<<-CSS, @env["svg-embed.css"].to_s) .svg-embed { background: url("data:image/svg+xml;charset=utf-8,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='512' height='512' viewBox='0 0 512 512'%3E%3Cpath d='M224 387.814v124.186l-192-192 192-192v126.912c223.375 5.24 213.794-151.896 156.931-254.912 140.355 151.707 110.55 394.785-156.931 387.814z'%3E%3C/path%3E%3C/svg%3E"); } CSS end test "extend context" do @env.context_class.class_eval do def datauri(path) require 'base64' Base64.encode64(File.open(path, "rb") { |f| f.read }) end end assert_equal ".pow {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZoAAAEsCAMAAADNS4U5AAAAGXRFWHRTb2Z0\n", @env["helpers.css"].to_s.lines.to_a[0..1].join assert_equal 58240, @env["helpers.css"].length end test "resolve with content type" do assert_equal(<<-FILE, @env["resolve_content_type.js"].to_s) file://#{fixture_path_for_uri("context/foo.js")}?type=application/javascript; file://#{fixture_path_for_uri("context/foo.js")}?type=application/javascript; FILE end end class TestCustomProcessor < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('context')) end require 'yaml' YamlBundleProcessor = proc do |input| env = input[:environment] manifest = YAML.load(input[:data]) paths = manifest['require'].map do |logical_path| uri, _ = env.resolve(logical_path) uri end { data: "", required: paths } end test "custom processor using Context#require" do @env.register_mime_type 'text/yaml+bundle', extensions: ['.bundle.yml'] @env.register_transformer 'text/yaml+bundle', 'application/javascript', YamlBundleProcessor assert_equal "var Foo = {};\n\nvar Bar = {};\n", @env['application.js'].to_s end require 'base64' DataUriProcessor = proc do |input| env = input[:environment] data = input[:data] data.gsub(/url\(\"(.+?)\"\)/) do uri, _ = env.resolve($1) path, _ = env.parse_asset_uri(uri) data = Base64.encode64(File.open(path, "rb") { |f| f.read }) "url(data:image/png;base64,#{data})" end end test "custom processor using Context#resolve and Context#depend_on" do @env.register_mime_type 'text/css+embed', extensions: ['.css.embed'] @env.register_transformer 'text/css+embed', 'text/css', DataUriProcessor assert_equal ".pow {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZoAAAEsCAMAAADNS4U5AAAAGXRFWHRTb2Z0\n", @env["sprite.css"].to_s.lines.to_a[0..1].join assert_equal 58240, @env["sprite.css"].length end test "block custom processor" do require 'base64' @env.register_preprocessor 'text/css' do |input| env = input[:environment] input[:data].gsub(/url\(\"(.+?)\"\)/) do uri, _ = env.resolve($1) path, _ = env.parse_asset_uri(uri) data = Base64.encode64(File.open(path, "rb") { |f| f.read }) "url(data:image/png;base64,#{data})" end end assert_equal ".pow {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZoAAAEsCAMAAADNS4U5AAAAGXRFWHRTb2Z0\n", @env["sprite2.css"].to_s.lines.to_a[0..1].join assert_equal 58240, @env["sprite2.css"].length end end sprockets-4.2.1/test/test_dependency.rb000066400000000000000000000010041447572140400202170ustar00rootroot00000000000000# frozen_string_literal: true require "sprockets_test" class DependencyTest < Sprockets::TestCase def setup @old_env_value = ENV['DEPENDENCY_TEST_VALUE'] ENV['DEPENDENCY_TEST_VALUE'] = 'Hello' @env = Sprockets::Environment.new end def teardown ENV['DEPENDENCY_TEST_VALUE'] = @old_env_value end def test_env_dependency assert_equal @env.resolve_dependency('env:DEPENDENCY_TEST_VALUE'), 'Hello' assert_nil @env.resolve_dependency('env:NONEXISTANT_DEPENDENCY_TEST_VALUE') end end sprockets-4.2.1/test/test_digest_utils.rb000066400000000000000000000111251447572140400206050ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/digest_utils' class TestDigestUtils < Minitest::Test include Sprockets::DigestUtils def test_detect_digest_class sha1 = Digest::SHA1.new.digest sha256 = Digest::SHA256.new.digest sha512 = Digest::SHA512.new.digest refute detect_digest_class("0000") assert_equal Digest::SHA1, detect_digest_class(sha1) assert_equal Digest::SHA256, detect_digest_class(sha256) assert_equal Digest::SHA512, detect_digest_class(sha512) end def test_digest assert_equal "291e87109f89e59ad717aebe4ffc9657c700e74da45db789ecd19d6b797baee2", hexdigest(42) assert_equal "b6054efd9929004bdd0a1c09eb2d12961325396da749143def3e9a4050aa703e", hexdigest([[:foo, 1]]) assert_equal "79a19ffe41ecebd5dc35e95363e0b4aa79b139a22bc650384df57eb09842f099", hexdigest([{foo: 1}]) assert_equal "9bda381dac87b1c16b04f996abb623f43f1cdb89ce8be7dda3f67319dc440bc5", hexdigest(nil) assert_equal "92de503a8b413365fc38050c7dd4bacf28b0f705e744dacebcaa89f2032dcd67", hexdigest(true) assert_equal "bdfd64a7c8febcc3b0b8fb05d60c8e2a4cb6b8c081fcba20db1c9778e9beaf89", hexdigest(false) assert_equal "d1312b90a6258e9bda7d10e5e1ab1468d92786eca72a65b5ab077169e36bcb1e", hexdigest(2 ** 128) assert_equal "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", hexdigest("foo") assert_equal "dea6712e86478d2ee22a35a8c5ac9627e7cbc5ce2407a7da7c645fea2434fe9b", hexdigest(:foo) assert_equal "f0cf39d0be3efbb6f86ac2404100ff7e055c17ded946a06808d66f89ca03a811", hexdigest([]) assert_equal "ed98cc300019b22ca15e7cd5934028a79e7af4c75f7eeea810f43a3a4353a04d", hexdigest(["foo"]) assert_equal "54edcfe382f4abaa9ebe93efa9977b05b786c9058496609797989b7fdf8208d4", hexdigest({"foo" => "bar"}) assert_equal "62427aa539a0b78e90fd710dc0e15f2960771ba44214b5d41d4a93a8b2940a38", hexdigest({"foo" => "baz"}) assert_equal "94ee40cca7c2c6d2a134033d2f5a31c488cad5d3dcc61a3dbb5e2a858635874b", hexdigest((+"foo").force_encoding('UTF-8').encoding) assert_raises(TypeError) do digest(Object.new) end end def test_pack_hexdigest digest = Digest::SHA256.new.update("alert(1)") assert_equal "6e11c72f7cf6bc383152dd16ddd5903aba6bb1c99d6b6639a4bb0b838185fa92", digest.hexdigest assert_equal "6e11c72f7cf6bc383152dd16ddd5903aba6bb1c99d6b6639a4bb0b838185fa92", pack_hexdigest(digest.digest) end def test_unpack_hexdigest digest = Digest::SHA256.new.update("alert(1)") assert_equal digest.digest, unpack_hexdigest(digest.hexdigest) end def test_pack_base64_digest digest = Digest::SHA256.new.update("alert(1)") assert_equal "bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=", digest.base64digest assert_equal "bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=", pack_base64digest(digest.digest) end def test_pack_urlsafe_base64_digest digest = Digest::SHA256.new.update("alert(1)") assert_equal "bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF-pI", pack_urlsafe_base64digest(digest.digest) end def test_integrity_uri sha256 = Digest::SHA256.new.update("alert(1)") sha512 = Digest::SHA512.new.update("alert(1)") assert_equal "sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=", integrity_uri(sha256) assert_equal "sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=", integrity_uri(sha256.digest) assert_equal "sha512-+uuYUxxe7oWIShQrWEmMn/fixz/rxDP4qcAZddXLDM3nN8/tpk1ZC2jXQk6N+mXE65jwfzNVUJL/qjA3y9KbuQ==", integrity_uri(sha512) assert_equal "sha512-+uuYUxxe7oWIShQrWEmMn/fixz/rxDP4qcAZddXLDM3nN8/tpk1ZC2jXQk6N+mXE65jwfzNVUJL/qjA3y9KbuQ==", integrity_uri(sha512.digest) # echo -n "alert('Hello, world.');" | openssl dgst -sha256 -binary | openssl enc -base64 -A sha256 = Digest::SHA256.new.update("alert('Hello, world.');") assert_equal "sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=", integrity_uri(sha256) end def test_hexdigest_integrity_uri sha256 = Digest::SHA256.new.update("alert(1)").hexdigest sha512 = Digest::SHA512.new.update("alert(1)").hexdigest assert_equal "sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI=", hexdigest_integrity_uri(sha256) assert_equal "sha512-+uuYUxxe7oWIShQrWEmMn/fixz/rxDP4qcAZddXLDM3nN8/tpk1ZC2jXQk6N+mXE65jwfzNVUJL/qjA3y9KbuQ==", hexdigest_integrity_uri(sha512) end def test_already_digested refute already_digested?(nil) refute already_digested?("application-abc123.digested") refute already_digested?("application-abcd1234") assert already_digested?("application-abcd1234.digested") assert already_digested?("application-abcd1234.digested.map") end end sprockets-4.2.1/test/test_eco_processor.rb000066400000000000000000000007551447572140400207620ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/eco_processor' class TestEcoProcessor < Minitest::Test def test_compile_eco_template_to_js input = { content_type: 'application/javascript', data: "Hello, <%= name %>

", cache: Sprockets::Cache.new } assert Sprockets::EcoProcessor.call(input).match(/Hello, /) end def test_cache_key assert Sprockets::EcoProcessor.cache_key end end sprockets-4.2.1/test/test_ejs_processor.rb000066400000000000000000000007551447572140400207750ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/ejs_processor' class TestEjsProcessor < Minitest::Test def test_compile_ejs_template_to_js input = { content_type: 'application/javascript', data: "Hello, <%= name %>

", cache: Sprockets::Cache.new } assert Sprockets::EjsProcessor.call(input).match(/Hello, /) end def test_cache_key assert Sprockets::EjsProcessor.cache_key end end sprockets-4.2.1/test/test_encoding.rb000066400000000000000000000044201447572140400176740ustar00rootroot00000000000000# frozen_string_literal: true require "sprockets_test" class AssetEncodingTest < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path(fixture_path('encoding')) end test "read ASCII asset" do data = @env['ascii.js'].to_s assert_equal "var foo = \"bar\";\n", data assert_equal Encoding::UTF_8, data.encoding end test "read UTF-8 asset" do data = @env['utf8.js'].to_s assert_equal "var snowman = \"☃\";\n", data assert_equal Encoding::UTF_8, data.encoding end test "read UTF-8 asset with BOM" do data = @env['utf8_bom.js'].to_s assert_equal "var snowman = \"☃\";\n", data assert_equal Encoding::UTF_8, data.encoding end test "read UTF-16 asset" do data = @env['utf16le.js'].to_s assert_equal "var snowman = \"☃\";\n", data assert_equal Encoding::UTF_8, data.encoding end test "read ASCII + UTF-8 concatation asset" do data = @env['ascii_utf8.js'].to_s assert_equal "var foo = \"bar\";\nvar snowman = \"\342\230\203\";\n\n\n", data assert_equal Encoding::UTF_8, data.encoding end test "read static BINARY asset" do data = @env['binary.png'].to_s assert_equal (+"\x89PNG\r\n\x1A\n\x00\x00\x00").force_encoding("BINARY"), data[0..10] assert_equal Encoding::BINARY, data.encoding end test "read processed BINARY asset" do @env.register_postprocessor('image/png') { |input| input[:data] } data = @env['binary.png'].to_s assert_equal (+"\x89PNG\r\n\x1A\n\x00\x00\x00").force_encoding("BINARY"), data[0..10] assert_equal Encoding::BINARY, data.encoding end test "read css asset with charset" do expected = "\n\nh1 { color: red; }\n" assert_equal expected, @env['utf8-charset.css'].to_s assert_equal expected, @env['utf16le-charset.css'].to_s assert_equal expected, @env['utf16le-bom-charset.css'].to_s end test "read file with content type" do data = @env.read_file(fixture_path('encoding/ascii.js'), 'application/javascript') assert_equal "var foo = \"bar\";\n", data assert_equal Encoding::UTF_8, data.encoding data = @env.read_file(fixture_path('encoding/utf16le-charset.css'), 'text/css') assert_equal "\n\nh1 { color: red; }\n", data assert_equal Encoding::UTF_8, data.encoding end end sprockets-4.2.1/test/test_encoding_utils.rb000066400000000000000000000205441447572140400211210ustar00rootroot00000000000000# encoding: utf-8 # frozen_string_literal: true require 'minitest/autorun' require 'sprockets/encoding_utils' class TestDigestUtils < Minitest::Test include Sprockets::EncodingUtils def test_deflate output = deflate("foobar") assert_equal 8, output.length assert_equal [75, 203, 207, 79, 74, 44, 2, 0], output.bytes.take(8) end def test_gzip output = gzip("foobar") assert_equal 26, output.length assert_equal [31, 139, 8, 0, 1, 0, 0, 0], output.bytes.take(8) end def test_base64 assert_equal "Zm9vYmFy", base64("foobar") end def test_unmarshal str = Marshal.dump("abc") assert_equal Marshal.load(str), unmarshaled_deflated(str) end def test_unmarshal_older_minor_version old_verbose, $VERBOSE = $VERBOSE, false begin str = Marshal.dump("abc") str[1] = "\0" assert_equal Marshal.load(str), unmarshaled_deflated(str) ensure $VERBOSE = old_verbose end end def test_fail_to_unmarshal_older_major_version str = Marshal.dump("abc") str[0] = "\1" assert_raises TypeError do Marshal.load(str) end assert_raises TypeError do unmarshaled_deflated(str) end end def test_fail_to_unmarshal_not_enough_data if RUBY_ENGINE == 'truffleruby' and Gem::Version.new(RUBY_ENGINE_VERSION) < Gem::Version.new('23.0.0.a') skip "TypeError on TruffleRuby < 23.0" end assert_raises ArgumentError do Marshal.load("") end assert_raises ArgumentError do unmarshaled_deflated("") end end def test_unmarshal_deflated str = deflate(Marshal.dump("abc")) assert_equal "abc", unmarshaled_deflated(str) end def test_detect_unicode_bom path = File.expand_path("../fixtures/encoding/ascii.js", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 17, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::BINARY, str.encoding assert_equal 17, str.bytesize path = File.expand_path("../fixtures/encoding/utf8.js", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 20, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::BINARY, str.encoding assert_equal 20, str.bytesize path = File.expand_path("../fixtures/encoding/utf8_bom.js", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 23, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::UTF_8, str.encoding assert_equal 20, str.bytesize path = File.expand_path("../fixtures/encoding/utf8_bom.js", __FILE__) str = File.binread(path) str.force_encoding(Encoding::UTF_8) assert_equal 23, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::UTF_8, str.encoding assert_equal 20, str.bytesize path = File.expand_path("../fixtures/encoding/utf16le.js", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 38, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::UTF_16LE, str.encoding assert_equal 36, str.bytesize path = File.expand_path("../fixtures/encoding/utf16le.js", __FILE__) str = File.binread(path) str.force_encoding(Encoding::UTF_16LE) assert_equal 38, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::UTF_16LE, str.encoding assert_equal 36, str.bytesize path = File.expand_path("../fixtures/encoding/utf16be.js", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 38, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::UTF_16BE, str.encoding assert_equal 36, str.bytesize path = File.expand_path("../fixtures/encoding/utf32le.js", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 76, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::UTF_32LE, str.encoding assert_equal 72, str.bytesize path = File.expand_path("../fixtures/encoding/utf32be.js", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 76, str.bytesize str = detect_unicode_bom(str) assert_equal Encoding::UTF_32BE, str.encoding assert_equal 72, str.bytesize end def test_detect_css_charset path = File.expand_path("../fixtures/encoding/utf8-charset.css", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 38, str.bytesize str = detect_css(str) assert_equal Encoding::UTF_8, str.encoding assert_equal 21, str.bytesize assert_equal "\n\nh1 { color: red; }\n", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf8-charset.css", __FILE__) str = File.binread(path) str.force_encoding(Encoding::UTF_8) assert_equal 38, str.bytesize str = detect_css(str) assert_equal Encoding::UTF_8, str.encoding assert_equal 21, str.bytesize assert_equal "\n\nh1 { color: red; }\n", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf16le-charset.css", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 82, str.bytesize str = detect_css(str) assert_equal Encoding::UTF_16LE, str.encoding assert_equal 42, str.bytesize assert_equal "\n\nh1 { color: red; }\n", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf16le-charset.css", __FILE__) str = File.binread(path) str.force_encoding(Encoding::UTF_16LE) assert_equal 82, str.bytesize str = detect_css(str) assert_equal Encoding::UTF_16LE, str.encoding assert_equal 42, str.bytesize assert_equal "\n\nh1 { color: red; }\n", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf16le-bom-charset.css", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 84, str.bytesize str = detect_css(str) assert_equal Encoding::UTF_16LE, str.encoding assert_equal 42, str.bytesize assert_equal "\n\nh1 { color: red; }\n", str.encode(Encoding::UTF_8) end def test_detect_html_charset assert_equal Encoding.default_external, Encoding::UTF_8 path = File.expand_path("../fixtures/encoding/utf8-charset.html", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 10, str.bytesize str = detect_html(str) assert_equal Encoding::UTF_8, str.encoding assert_equal 10, str.bytesize assert_equal "

", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf8_bom.html", __FILE__) str = File.binread(path) str.force_encoding(Encoding::UTF_8) assert_equal 13, str.bytesize str = detect_html(str) assert_equal Encoding::UTF_8, str.encoding assert_equal 10, str.bytesize assert_equal "

", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf16le.html", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 18, str.bytesize str = detect_html(str) assert_equal Encoding::UTF_16LE, str.encoding assert_equal 16, str.bytesize assert_equal "

", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf16be.html", __FILE__) str = File.binread(path) str.force_encoding(Encoding::UTF_16BE) assert_equal 18, str.bytesize str = detect_html(str) assert_equal Encoding::UTF_16BE, str.encoding assert_equal 16, str.bytesize assert_equal "

", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf32be.html", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 36, str.bytesize str = detect_html(str) assert_equal Encoding::UTF_32BE, str.encoding assert_equal 32, str.bytesize assert_equal "

", str.encode(Encoding::UTF_8) path = File.expand_path("../fixtures/encoding/utf32le.html", __FILE__) str = File.binread(path) assert_equal Encoding::BINARY, str.encoding assert_equal 36, str.bytesize str = detect_html(str) assert_equal Encoding::UTF_32LE, str.encoding assert_equal 32, str.bytesize assert_equal "

", str.encode(Encoding::UTF_8) end end sprockets-4.2.1/test/test_environment.rb000066400000000000000000000764541447572140400204720ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'rack/mock' require 'execjs' module EnvironmentTests def self.test(name, &block) define_method("test_#{name.inspect}", &block) end test "working directory is the default root" do assert_equal Dir.pwd, @env.root end test "default logger level is set to fatal" do assert_equal Logger::FATAL, @env.logger.level end test "active css compressor" do assert_nil @env.css_compressor end test "active js compressor" do assert_nil @env.js_compressor end test "paths" do assert_equal [fixture_path("default")], @env.paths.to_a end test "register global path" do assert_equal [fixture_path("default")], new_environment.paths.to_a Sprockets.append_path(fixture_path("asset")) assert_equal [fixture_path("asset"), fixture_path("default")], new_environment.paths.to_a Sprockets.clear_paths end test "eco templates" do asset = @env["goodbye.js"] context = ExecJS.compile(asset.to_s) assert_equal "Goodbye world\n", context.call("JST['goodbye']", name: "world") end test "ejs templates" do assert asset = @env["hello.js"] context = ExecJS.compile(asset.to_s) assert_equal "hello: world\n", context.call("JST['hello']", name: "world") end test "find_asset! does not raise an exception when asset is found" do @env.find_asset!("hello.js") # assert no raise end test "find_asset! raises an error when asset is not found" do does_not_exist_file_name = "doesnotexist.blerg" error = assert_raises(Sprockets::NotFound) do @env.find_asset!(does_not_exist_file_name) end assert_match %r{#{does_not_exist_file_name}}, error.message end test "asset_data_uri helper" do assert asset = @env["with_data_uri.css"] assert_equal "body {\n background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAP%2F%2F%2FwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw%3D%3D) no-repeat;\n}\n", asset.to_s end test "lookup bundle processors" do assert_equal 1, @env.bundle_processors['application/javascript'].size assert_equal 1, @env.bundle_processors['text/css'].size end test "find asset with accept type" do assert asset = @env.find_asset("gallery.js", accept: '*/*') assert_equal fixture_path('default/gallery.js'), asset.filename assert asset = @env.find_asset("gallery", accept: 'application/javascript') assert_equal fixture_path('default/gallery.js'), asset.filename assert asset = @env.find_asset("gallery", accept: 'application/javascript, text/css') assert_equal fixture_path('default/gallery.js'), asset.filename assert asset = @env.find_asset("gallery.js", accept: 'application/javascript') assert_equal fixture_path('default/gallery.js'), asset.filename assert asset = @env.find_asset("gallery", accept: 'text/css, application/javascript') assert_equal fixture_path('default/gallery.js'), asset.filename assert asset = @env.find_asset("coffee/foo", accept: "application/javascript") assert_equal fixture_path('default/coffee/foo.coffee'), asset.filename assert asset = @env.find_asset("coffee/foo.js", accept: "application/javascript") assert_equal fixture_path('default/coffee/foo.coffee'), asset.filename assert asset = @env.find_asset("jquery.tmpl.min", accept: 'application/javascript') assert_equal fixture_path('default/jquery.tmpl.min.js'), asset.filename assert asset = @env.find_asset("jquery.tmpl.min.js", accept: 'application/javascript') assert_equal fixture_path('default/jquery.tmpl.min.js'), asset.filename assert asset = @env.find_asset('manifest.js.yml', accept: 'text/yaml') assert_equal fixture_path('default/manifest.js.yml'), asset.filename assert asset = @env.find_asset('manifest.js.yml', accept: 'text/css, */*') assert_equal fixture_path('default/manifest.js.yml'), asset.filename refute @env.find_asset("gallery.js", accept: "text/css") refute @env.find_asset(fixture_path('default/gallery.js'), accept: "text/css") refute @env.find_asset('manifest.js.yml', accept: 'application/javascript') end test "find all linked assets" do assert assets = @env.find_all_linked_assets("missing.js").to_a assert_equal 0, assets.length assert assets = @env.find_all_linked_assets("gallery.js").to_a assert_equal 1, assets.length assert_equal @env["gallery.js"], assets[0] assert assets = @env.find_all_linked_assets("gallery-link.js").to_a assert_equal 2, assets.length assert_equal @env["gallery-link.js"], assets[0] assert_equal @env["gallery.js"], assets[1] assert assets = @env.find_all_linked_assets("explore-link.js").to_a assert_equal 3, assets.length assert_equal @env["explore-link.js"], assets[0] assert_equal @env["gallery-link.js"], assets[1] assert_equal @env["gallery.js"], assets[2] end test "html subresource links" do assert asset = @env["homepage-links.html"] assert_equal "text/html", asset.content_type assert asset.to_s.match(/gallery-[a-z0-9]+\.css/) assert asset.to_s.match(/gallery-[a-z0-9]+\.js/) assert asset.links.include?(@env["gallery.css"].uri) assert asset.links.include?(@env["gallery.js"].uri) end test "web component assets" do assert asset = @env["menu/menu.html"] assert_equal "text/html", asset.content_type assert_equal "\n", asset.to_s assert asset = @env["menu/menu.js"] assert_equal "application/javascript", asset.content_type assert_equal "$.fn.menu = {};\n", asset.to_s assert asset = @env["menu/menu.css"] assert_equal "text/css", asset.content_type assert_equal ".menu {}\n", asset.to_s end test "explicit bower.json access returns json file" do assert_equal fixture_path('default/bower/bower.json'), @env["bower/bower.json"].filename end test "find default bower main" do assert_equal fixture_path('default/bower/main.js'), @env["bower"].filename assert_equal fixture_path('default/qunit/qunit.js'), @env["qunit"].filename assert_equal fixture_path('default/rails/rails.coffee'), @env["rails"].filename end test "find bower main by format extension" do assert_equal fixture_path('default/bower/main.js'), @env["bower.js"].filename refute @env.find_asset("bower.css") assert_equal fixture_path('default/qunit/qunit.js'), @env["qunit.js"].filename assert_equal fixture_path('default/qunit/qunit.css'), @env["qunit.css"].filename assert_equal fixture_path('default/rails/rails.coffee'), @env["rails.js"].filename assert_equal fixture_path('default/requirejs/require.js'), @env.find_asset("requirejs.js").filename end test "find bower main by content type" do assert_equal fixture_path('default/bower/main.js'), @env.find_asset("bower", accept: 'application/javascript').filename assert_equal fixture_path('default/bower/main.js'), @env.find_asset("bower.js", accept: 'application/javascript').filename assert_equal fixture_path('default/qunit/qunit.js'), @env.find_asset("qunit", accept: 'application/javascript').filename assert_equal fixture_path('default/qunit/qunit.js'), @env.find_asset("qunit.js", accept: 'application/javascript').filename assert_equal fixture_path('default/qunit/qunit.css'), @env.find_asset("qunit", accept: 'text/css').filename assert_equal fixture_path('default/qunit/qunit.css'), @env.find_asset("qunit.css", accept: 'text/css').filename assert_equal fixture_path('default/rails/rails.coffee'), @env.find_asset("rails", accept: 'application/javascript').filename assert_equal fixture_path('default/rails/rails.coffee'), @env.find_asset("rails.js", accept: 'application/javascript').filename assert_equal fixture_path('default/requirejs/require.js'), @env.find_asset("requirejs.js", accept: 'application/javascript').filename end test "bower main with invalid files" do assert @env.find_asset("zeroclipboard/FILEFILE") assert @env.find_asset("zeroclipboard/mimetype.unknown") assert @env.find_asset("zeroclipboard/zc.js") assert @env.find_asset("zeroclipboard.js") refute @env.find_asset("zeroclipboard.unknown") end test "explicit package.json access returns json file" do assert_equal fixture_path('default/npm/package.json'), @env["npm/package.json"].filename end test "find default npm main" do assert_equal fixture_path('default/npm/main.js'), @env["npm"].filename end test "find nested npm main" do assert_equal fixture_path('default/npm-nested/a/b/main.js'), @env["npm-nested/a/b"].filename end test "find npm main by format extension" do assert_equal fixture_path('default/npm/main.js'), @env["npm.js"].filename end test "find npm main by content type" do assert_equal fixture_path('default/npm/main.js'), @env.find_asset("npm", accept: 'application/javascript').filename assert_equal fixture_path('default/npm/main.js'), @env.find_asset("npm.js", accept: 'application/javascript').filename end test "find default npm main when main key is not set" do assert_equal fixture_path('default/express/index.js'), @env["express"].filename end test "find npm style by format extension" do assert_equal fixture_path('default/npm/style.css'), @env["npm.css"].filename end test "find npm style by content type" do assert_equal fixture_path('default/npm/style.css'), @env.find_asset("npm", accept: 'text/css').filename assert_equal fixture_path('default/npm/style.css'), @env.find_asset("npm.css", accept: 'text/css').filename end test "find bundled asset in environment" do assert_equal "var Gallery = {};\n", @env["gallery.js"].to_s end test "find bundled asset with absolute path environment" do assert_equal "var Gallery = {};\n", @env[fixture_path("default/gallery.js")].to_s end test "find bundled asset with implicit format" do assert_equal "(function() {\n var foo;\n\n foo = 'hello';\n\n}).call(this);\n", @env["coffee/foo.js"].to_s end test "find static asset in environment" do assert_equal "Hello world\n", @env["hello.txt"].to_s end test "find static asset with leading slash in environment" do assert_equal "Hello world\n", @env[fixture_path("default/hello.txt")].to_s end test "find index.js in directory" do assert_equal "var A;\nvar B;\n", @env["mobile.js"].to_s end test "find index.css in directory" do assert_equal ".c {}\n.d {}\n/*\n\n */\n", @env["mobile.css"].to_s end test "ignore index.min.js in directory" do refute @env["mobile-min.js"] end test "find bower.json in directory" do assert_equal "var bower;\n", @env["bower.js"].to_s end test "find multiple bower.json in directory" do assert_equal "var qunit;\n", @env["qunit.js"].to_s assert_equal ".qunit {}\n", @env["qunit.css"].to_s end test "find erb assets" do assert asset = @env.find_asset("erb/a.html") assert_equal "text/html", asset.content_type assert asset = @env.find_asset("erb/b.txt") assert_equal "text/plain", asset.content_type assert asset = @env.find_asset("erb/c.js") assert_equal "application/javascript", asset.content_type assert asset = @env.find_asset("erb/d.css") assert_equal "text/css", asset.content_type assert asset = @env.find_asset("erb/e.html") assert_equal "text/html", asset.content_type assert asset = @env.find_asset("erb/f.yaml") assert_equal "text/yaml", asset.content_type end test "es6 asset" do assert asset = @env.find_asset("future.js") assert_match(/var square/, asset.to_s) assert_match(/function/, asset.to_s) end test "find html builder asset" do assert asset = @env.find_asset("nokogiri-html.html") assert_equal "text/html", asset.content_type if RUBY_PLATFORM.include?('java') assert_equal 'Hello world', asset.to_s else assert_equal <<-HTML, asset.to_s Hello world HTML end end test "find xml builder asset" do assert asset = @env.find_asset("nokogiri-xml.xml") assert_equal "application/xml", asset.content_type assert_equal <<-XML, asset.to_s 10 Awesome widget XML end test "svg transformer for extension" do assert asset = @env.find_asset("logo.svg") assert_equal "image/svg+xml", asset.content_type assert_equal "logo.svg", asset.logical_path assert_equal [60, 115, 118, 103, 32, 119, 105, 100, 116, 104], asset.to_s[0, 10].bytes.to_a assert asset = @env.find_asset("logo.png") assert_equal "image/png", asset.content_type assert_equal "logo.png", asset.logical_path assert_equal [137, 80, 78, 71, 13, 10, 26, 10, 60, 115], asset.to_s[0, 10].bytes.to_a end test "svg transformer for accept" do assert asset = @env.find_asset("logo", accept: "image/svg+xml") assert_equal "image/svg+xml", asset.content_type assert_equal "logo.svg", asset.logical_path assert_equal [60, 115, 118, 103, 32, 119, 105, 100, 116, 104], asset.to_s[0, 10].bytes.to_a assert asset = @env.find_asset("logo", accept: "image/png") assert_equal "image/png", asset.content_type assert_equal "logo.png", asset.logical_path assert_equal [137, 80, 78, 71, 13, 10, 26, 10, 60, 115], asset.to_s[0, 10].bytes.to_a end test "full path svg transformer" do assert @env.find_asset(fixture_path("default/logo.svg")) refute @env.find_asset(fixture_path("default/logo.png")) end test "full path svg transformer for accept" do assert asset = @env.find_asset(fixture_path("default/logo.svg"), accept: "image/svg+xml") assert_equal "logo.svg", asset.logical_path assert_equal "image/svg+xml", asset.content_type assert_equal [60, 115, 118, 103, 32, 119, 105, 100, 116, 104], asset.to_s[0, 10].bytes.to_a assert asset = @env.find_asset(fixture_path("default/logo.svg"), accept: "image/png") assert_equal "image/png", asset.content_type assert_equal "logo.png", asset.logical_path assert_equal [137, 80, 78, 71, 13, 10, 26, 10, 60, 115], asset.to_s[0, 10].bytes.to_a end test "asset with + character" do assert asset = @env.find_asset("+plus.js") assert_equal "+plus.js", asset.logical_path assert_equal "application/javascript", asset.content_type end test "missing static path returns nil" do assert_nil @env[fixture_path("default/missing.png")] end test "find static directory returns nil" do assert_nil @env["images"] end test "missing asset returns nil" do assert_nil @env["missing.js"] end test "missing asset path returns nil" do refute @env[fixture_path("default/missing.js")] end test "asset filename outside of load paths" do path = File.expand_path("../../bin/sprockets", __FILE__) assert File.exist?(path), "#{path} didn't exist" refute @env[path] end test "non-existent asset filename outside of load paths" do path = File.expand_path("../../bin/sprockets2", __FILE__) refute File.exist?(path), "#{path} exists" refute @env[path] end test "can't require files outside the load path" do path = fixture_path("default/../asset/project.css") assert File.exist?(path) refute @env[path] end test "asset with missing requires raises an exception" do assert_raises Sprockets::FileNotFound do @env["missing_require.js"] end end test "asset with missing depend_on raises an exception" do assert_raises Sprockets::FileNotFound do @env["missing_depend_on.js"] end end test "asset with missing absolute depend_on raises an exception" do assert_raises Sprockets::FileOutsidePaths do @env["missing_absolute_depend_on.js"] end end test "asset logical path for absolute path" do assert_equal "gallery.js", @env.find_asset(fixture_path("default/gallery.js")).logical_path assert_equal "application.js", @env.find_asset(fixture_path("default/application.coffee"), accept: "application/javascript").logical_path assert_equal "mobile/a.js", @env.find_asset(fixture_path("default/mobile/a.js")).logical_path end test "mobile index logical path shorthand" do assert_equal "mobile/index.js", @env[fixture_path("default/mobile/index.js")].logical_path assert_equal "mobile-min/index.min.js", @env[fixture_path("default/mobile-min/index.min.js")].logical_path end test "CoffeeScript files are compiled in a closure" do script = @env["coffee"].to_s assert_equal "undefined", ExecJS.exec(script) end test "source pipeline skips all processoring" do assert asset = @env.find_asset("missing_require.js", pipeline: :source) assert_equal "// =require \"notfound\"\n", asset.source assert_equal "application/javascript", asset.content_type assert_equal "missing_require.source.js", asset.logical_path assert asset = @env.find_asset("missing_require.source.js") assert_equal "// =require \"notfound\"\n", asset.source assert_equal "application/javascript", asset.content_type assert_equal "missing_require.source.js", asset.logical_path end test "source pipeline on existing source asset" do assert asset = @env.find_asset("hello.txt", pipeline: :source) assert_equal "Hello world\n", asset.source assert_equal "text/plain", asset.content_type assert_equal "hello.source.txt", asset.logical_path assert asset = @env.find_asset("hello.source.txt") assert_equal "Hello world\n", asset.source assert_equal "text/plain", asset.content_type assert_equal "hello.source.txt", asset.logical_path end test "find source for concatenated asset" do assert asset = @env.find_asset("application.source.coffee") assert_equal "text/coffeescript", asset.content_type assert_equal "application.source.coffee", asset.logical_path assert asset = @env.find_asset("application.js") assert_equal "application/javascript", asset.content_type assert_equal "application.js", asset.logical_path assert asset = @env.find_asset("application.self.js") assert_equal "application/javascript", asset.content_type assert_equal "application.self.js", asset.logical_path assert asset = @env.find_asset("project.self.js") assert_equal "application/javascript", asset.content_type assert_equal "project.self.js", asset.logical_path assert asset = @env.find_asset("application.js.map") assert_equal "application/js-sourcemap+json", asset.content_type assert_equal "application.js.map", asset.logical_path end end class WhitespaceProcessor def self.call(input) input[:data].gsub(/\s+/, "") end end class WhitespaceCompressor def self.compress(source) source.gsub(/\s+/, "") end end class TestEnvironment < Sprockets::TestCase include EnvironmentTests def new_environment Sprockets::Environment.new(".") do |env| env.append_path(fixture_path('default')) env.cache = {} yield env if block_given? end end def setup @env = new_environment end test "changing logger" do @env.logger = Logger.new($stderr) end test "changing paths" do @env.clear_paths @env.append_path(fixture_path('asset')) end test "default gzip" do assert_equal true, @env.gzip? end test "change jst template namespace" do @env.register_transformer 'application/javascript+function', 'application/javascript', Sprockets::JstProcessor.new(namespace: 'this.JST2') assert asset = @env["hello.js"] context = ExecJS.compile(asset.to_s) assert_equal "hello: world\n", context.call("JST2['hello']", name: "world") end test "register bundle processor" do old_size = @env.bundle_processors['text/css'].size @env.register_bundle_processor 'text/css', WhitespaceProcessor assert_equal old_size+1, @env.bundle_processors['text/css'].size end test "register compressor" do assert !@env.compressors['text/css'][:whitespace] @env.register_compressor 'text/css', :whitespace, WhitespaceCompressor assert @env.compressors['text/css'][:whitespace] end test "register global bundle processor" do old_size = Sprockets.bundle_processors['text/css'].size Sprockets.register_bundle_processor 'text/css', WhitespaceProcessor assert_equal old_size+1, Sprockets.bundle_processors['text/css'].size env = new_environment assert_equal old_size+1, env.bundle_processors['text/css'].size Sprockets.unregister_bundle_processor 'text/css', WhitespaceProcessor assert_equal old_size, Sprockets.bundle_processors['text/css'].size end test "setting css compressor to nil clears current compressor" do @env.css_compressor = WhitespaceCompressor assert @env.css_compressor @env.css_compressor = nil assert_nil @env.css_compressor end test "setting js compressor to nil clears current compressor" do @env.js_compressor = WhitespaceCompressor assert @env.js_compressor @env.js_compressor = nil assert_nil @env.js_compressor end test "setting js compressor to template handler" do assert_nil @env.js_compressor @env.js_compressor = Sprockets::UglifierCompressor assert_equal Sprockets::UglifierCompressor, @env.js_compressor @env.js_compressor = nil assert_nil @env.js_compressor end test "setting css compressor to template handler" do silence_warnings do require 'sprockets/sass_compressor' end assert_nil @env.css_compressor @env.css_compressor = Sprockets::SassCompressor assert_equal Sprockets::SassCompressor, @env.css_compressor @env.css_compressor = nil assert_nil @env.css_compressor end test "setting js compressor to sym" do assert_nil @env.js_compressor @env.js_compressor = :uglifier assert_equal 'Sprockets::UglifierCompressor', @env.js_compressor.name @env.js_compressor = nil assert_nil @env.js_compressor end test "setting css compressor to sym" do silence_warnings do require 'sprockets/sass_compressor' end assert_nil @env.css_compressor @env.css_compressor = :sass assert_equal 'Sprockets::SassCompressor', @env.css_compressor.name @env.css_compressor = nil assert_nil @env.css_compressor end test "pre/post processors on transformed asset" do @env.register_preprocessor 'image/svg+xml', proc { |input| { data: input[:data], test: Array(input[:metadata][:test]) + [:pre_svg] } } @env.register_postprocessor 'image/svg+xml', proc { |input| { data: input[:data], test: Array(input[:metadata][:test]) + [:post_svg] } } @env.register_preprocessor 'image/png', proc { |input| { data: input[:data], test: Array(input[:metadata][:test]) + [:pre_png] } } @env.register_postprocessor 'image/png', proc { |input| { data: input[:data], test: Array(input[:metadata][:test]) + [:post_png] } } @env.register_preprocessor 'image/gif', proc { |input| { data: input[:data], test: Array(input[:metadata][:test]) + [:pre_gif] } } @env.register_postprocessor 'image/gif', proc { |input| { data: input[:data], test: Array(input[:metadata][:test]) + [:post_gif] } } assert asset = @env.find_asset("logo.svg") assert_equal "image/svg+xml", asset.content_type assert_equal [:pre_svg, :post_svg], asset.metadata[:test] assert asset = @env.find_asset("logo.png") assert_equal "image/png", asset.content_type assert_equal [:pre_svg, :post_svg, :pre_png, :post_png], asset.metadata[:test] assert asset = @env.find_asset("logo.gif") assert_equal "image/gif", asset.content_type assert_equal [:pre_svg, :post_svg, :pre_png, :post_png, :pre_gif, :post_gif], asset.metadata[:test] end test "processor returning a non-string data" do @env.register_postprocessor 'application/javascript', proc { |input| { data: 42 } } assert_raises TypeError do @env.find_asset("application.js") end end test "processor returning a subclassed string data" do my_string = Class.new(String) @env.register_postprocessor 'application/javascript', proc { |input| { data: my_string.new("foo") } } assert_raises TypeError do @env.find_asset("application.js") end end test "processor returning a complex metadata type" do @env.register_postprocessor 'application/javascript', proc { |input| { data: "hello", foo: Object.new } } assert_raises TypeError do @env.find_asset("application.js") end end test "access selector count metadata" do assert asset = @env.find_asset("mobile.css") assert_equal 2, asset.metadata[:selector_count] end test "changing version changes the digest_path of the asset" do old_asset_digest = @env["gallery.js"].digest_path @env.version = 'v2' refute_equal old_asset_digest, @env["gallery.js"].digest_path end test "changing version changes the digest_path of the asset when there is no preposessor" do old_asset_digest = @env["blank.gif"].digest_path @env.version = 'v2' refute_equal old_asset_digest, @env["blank.gif"].digest_path end test "changing version changes the etag of the asset" do old_asset_etag = @env["gallery.js"].etag @env.version = 'v2' new_asset_etag = @env["gallery.js"].etag refute_equal old_asset_etag, new_asset_etag assert_equal old_asset_etag.size, new_asset_etag.size end test "changing version to nil does not break etag" do old_asset_etag = @env["gallery.js"].etag @env.version = nil new_asset_etag = @env["gallery.js"].etag assert_equal old_asset_etag, new_asset_etag assert_equal old_asset_etag.size, new_asset_etag.size end test "changing version does not changes the digest of the asset" do old_asset_digest = @env["gallery.js"].digest @env.version = 'v2' assert_equal old_asset_digest, @env["gallery.js"].digest end test "changing version does not changes the hexdigest of the asset" do old_asset_hexdigest = @env["gallery.js"].hexdigest @env.version = 'v2' assert_equal old_asset_hexdigest, @env["gallery.js"].hexdigest end test "changing version does not changes the base64digest of the asset" do old_asset_base64digest = @env["gallery.js"].base64digest @env.version = 'v2' assert_equal old_asset_base64digest, @env["gallery.js"].base64digest end test "changing version does not changes the integrity of the asset" do old_asset_integrity = @env["gallery.js"].integrity @env.version = 'v2' assert_equal old_asset_integrity, @env["gallery.js"].integrity end test "bundled asset is stale if its mtime is updated or deleted" do filename = File.join(fixture_path("default"), "tmp.js") sandbox filename do assert_nil @env["tmp.js"] File.open(filename, 'w') { |f| f.write "foo;\n" } assert_equal "foo;\n", @env["tmp.js"].to_s File.open(filename, 'w') { |f| f.write "bar;\n" } time = Time.now + 60 File.utime(time, time, filename) assert_equal "bar;\n", @env["tmp.js"].to_s File.unlink(filename) assert_nil @env["tmp.js"] end end test "static asset is stale if its mtime is updated or deleted" do filename = File.join(fixture_path("default"), "tmp.png") sandbox filename do assert_nil @env["tmp.png"] File.open(filename, 'wb') { |f| f.write "\x01\x02\x03" } assert_equal "\x01\x02\x03", @env["tmp.png"].to_s File.open(filename, 'wb') { |f| f.write "\x04\x05\x06" } time = Time.now + 60 File.utime(time, time, filename) assert_equal "\x04\x05\x06", @env["tmp.png"].to_s File.unlink(filename) assert_nil @env["tmp.png"] end end test "bundled asset cached if theres an error building it" do @env.cache = nil filename = File.join(fixture_path("default"), "tmp.coffee") sandbox filename do File.open(filename, 'w') { |f| f.write "-->" } begin @env["tmp.js"].to_s rescue ExecJS::Error => e assert e else flunk "nothing raised" end File.open(filename, 'w') { |f| f.write "->" } time = Time.now + 60 File.utime(time, time, filename) assert_equal "(function() {\n (function() {});\n\n}).call(this);\n", @env["tmp.js"].to_s end end test "separate contexts classes for each instance" do e1 = new_environment e2 = new_environment assert_raises(NameError) { e1.context_class.instance_method(:foo) } assert_raises(NameError) { e2.context_class.instance_method(:foo) } e1.context_class.class_eval do def foo; end end e1.context_class.instance_method(:foo) assert_raises(NameError) { e2.context_class.instance_method(:foo) } end test "disabling default directive preprocessor" do assert processors = @env.preprocessors['application/javascript'] processor = processors.detect {|p| p.is_a?(Sprockets::DirectiveProcessor) } assert processor @env.unregister_preprocessor('application/javascript', processor) assert_equal "// =require \"notfound\";\n", @env["missing_require.js"].to_s end test "disabling processors by class name also disables processors which are instances of that class" do space_and_whitespace_compressor = Class.new(WhitespaceCompressor) @env.register_preprocessor('text/html', WhitespaceCompressor) @env.register_preprocessor('text/html', WhitespaceCompressor.new) @env.register_preprocessor('text/html', space_and_whitespace_compressor) assert_equal 3, @env.preprocessors['text/html'].size @env.unregister_preprocessor('text/html', WhitespaceCompressor) assert_equal 1, @env.preprocessors['text/html'].size assert_equal space_and_whitespace_compressor, @env.preprocessors['text/html'][0] end end class TestCachedEnvironment < Sprockets::TestCase include EnvironmentTests def new_environment Sprockets::Environment.new(".") do |env| env.append_path(fixture_path('default')) env.cache = {} env.gzip = false yield env if block_given? end.cached end def setup @env = new_environment end test "inherit the gzip option" do assert_equal false, @env.gzip? end test "does not allow to change the gzip option" do assert_raises RuntimeError do @env.gzip = true end end test "does not allow new mime types to be added" do assert_raises RuntimeError do @env.register_mime_type "application/javascript", extensions: [".jst"] end end test "does not allow new bundle processors to be added" do assert_raises RuntimeError do @env.register_bundle_processor 'text/css', WhitespaceProcessor end end test "does not allow bundle processors to be removed" do assert_raises RuntimeError do @env.unregister_bundle_processor 'text/css', WhitespaceProcessor end end test "change in environment bundle_processors does not affect cache" do env = Sprockets::Environment.new(".") cached = env.cached assert !cached.bundle_processors['text/css'].include?(WhitespaceProcessor) env.register_bundle_processor 'text/css', WhitespaceProcessor assert !cached.bundle_processors['text/css'].include?(WhitespaceProcessor) end test "does not allow css compressor to be changed" do assert_raises RuntimeError do @env.css_compressor = WhitespaceCompressor end end test "does not allow js compressor to be changed" do assert_raises RuntimeError do @env.js_compressor = WhitespaceCompressor end end end sprockets-4.2.1/test/test_erb_processor.rb000066400000000000000000000117041447572140400207600ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets' require 'sprockets/cache' require 'sprockets/erb_processor' require 'sass' class TestERBProcessor < Minitest::Test def uri_path(path) path = '/' + path if path[1] == ':' # Windows path / drive letter path end def test_compile_js_erb_template environment = Sprockets::Environment.new input = { environment: environment, filename: "foo.js.erb", content_type: 'application/javascript', data: "var data = <%= JSON.generate({foo: true}) %>;", metadata: {}, cache: Sprockets::Cache.new } output = "var data = {\"foo\":true};" assert_equal output, Sprockets::ERBProcessor.call(input)[:data] end def test_compile_erb_template_that_depends_on_env old_env_value = ::ENV['ERB_ENV_TEST_VALUE'] ::ENV['ERB_ENV_TEST_VALUE'] = 'success' if RUBY_ENGINE == 'truffleruby' and Gem::Version.new(RUBY_ENGINE_VERSION) < Gem::Version.new('23.0.0.a') skip 'https://github.com/oracle/truffleruby/issues/2810' end root = File.expand_path("../fixtures", __FILE__) environment = Sprockets::Environment.new(root) environment.append_path 'default' input = { environment: environment, filename: "foo.js.erb", content_type: 'application/javascript', data: "<%= ENV['ERB_ENV_TEST_VALUE'] %>;", metadata: {}, cache: Sprockets::Cache.new } output = "success;" result = Sprockets::ERBProcessor.call(input) assert_equal output, result[:data] assert_equal "env:ERB_ENV_TEST_VALUE", result[:dependencies].first ensure ::ENV['ERB_ENV_TEST_VALUE'] = old_env_value end def test_compile_erb_template_that_depends_on_empty_env old_env_value = ::ENV.delete('ERB_ENV_TEST_VALUE') if RUBY_ENGINE == 'truffleruby' and Gem::Version.new(RUBY_ENGINE_VERSION) < Gem::Version.new('23.0.0.a') skip 'https://github.com/oracle/truffleruby/issues/2810' end root = File.expand_path("../fixtures", __FILE__) environment = Sprockets::Environment.new(root) environment.append_path 'default' input = { environment: environment, filename: "foo.js.erb", content_type: 'application/javascript', data: "<%= ENV['ERB_ENV_TEST_VALUE'] %>;", metadata: {}, cache: Sprockets::Cache.new } output = ";" result = Sprockets::ERBProcessor.call(input) assert_equal output, result[:data] assert_equal "env:ERB_ENV_TEST_VALUE", result[:dependencies].first ensure ::ENV['ERB_ENV_TEST_VALUE'] = old_env_value end def test_compile_erb_template_with_depend_on_call root = File.expand_path("../fixtures", __FILE__) environment = Sprockets::Environment.new(root) environment.append_path 'default' path = "#{root}/default/gallery.js" input = { environment: environment, filename: "foo.js.erb", content_type: 'application/javascript', data: "<%= depend_on('#{path}') %>\nvar data = 'DATA';", metadata: {}, cache: Sprockets::Cache.new } output = "var data = 'DATA';" result = Sprockets::ERBProcessor.call(input) assert_equal output, result[:data] assert_equal "file-digest://#{uri_path(path)}", result[:dependencies].first end def test_compile_erb_template_with_depend_on_call_outside_load_paths root = File.expand_path("../fixtures", __FILE__) environment = Sprockets::Environment.new(root) environment.append_path 'default' path = "#{root}/asset/application.js" assert File.exist?(path) input = { environment: environment, filename: "foo.js.erb", content_type: 'application/javascript', data: "<%= depend_on('#{path}') %>\nvar data = 'DATA';", metadata: {}, cache: Sprockets::Cache.new } output = "var data = 'DATA';" result = Sprockets::ERBProcessor.call(input) assert_equal output, result[:data] assert_equal "file-digest://#{uri_path(path)}", result[:dependencies].first end def test_pass_custom_erb_helpers_to_template environment = Sprockets::Environment.new template = Sprockets::ERBProcessor.new do def foo :bar end end input = { environment: environment, filename: "foo.js.erb", content_type: 'application/javascript', data: "var foo = <%= foo %>;", metadata: {}, cache: Sprockets::Cache.new } output = "var foo = bar;" assert_equal output, template.call(input)[:data] end def test_compile_js_erb_template_with_top_level_constant_access environment = Sprockets::Environment.new Sprockets.const_set(:Sass, Class.new) input = { environment: environment, filename: "foo.js.erb", content_type: 'application/javascript', data: "var sass_version = '<%= Sass::VERSION %>';", metadata: {}, cache: Sprockets::Cache.new } assert_match(/sass_version/, Sprockets::ERBProcessor.call(input)[:data]) ensure Sprockets.send(:remove_const, :Sass) end end sprockets-4.2.1/test/test_exporting.rb000066400000000000000000000070621447572140400201320ustar00rootroot00000000000000require 'sprockets_test' class TestExporting < Sprockets::TestCase def setup @dir = Dir.mktmpdir FileUtils.mkdir_p(@dir) @env = Sprockets::Environment.new(".") do |env| env.append_path(fixture_path('default')) end @manifest = Sprockets::Manifest.new(@env, @dir) end def teardown FileUtils.remove_entry_secure(@dir) if File.exist?(@dir) assert Dir["#{@dir}/*"].empty? end test 'extension exporters' do @env.register_exporter 'application/javascript', ReverseExtensionExporter @env.register_exporter '*/*', DoubleExtensionExporter @manifest.compile('application.js') @manifest.compile('blank.gif') js_path = @env['application.js'].digest_path gif_path = @env['blank.gif'].digest_path reverse_js_path = "#{@dir}/#{js_path[0..-4]}.sj" double_js_path = "#{@dir}/#{js_path}js" reverse_gif_path = "#{@dir}/#{gif_path[0..-5]}.fig" double_gif_path = "#{@dir}/#{gif_path}gif" assert File.exist?(reverse_js_path), "Expected #{reverse_js_path} to exist, but it didn't" assert !File.exist?(reverse_gif_path), "Expected #{reverse_gif_path} to not exist, but it didn't" assert File.exist?(double_js_path), "Expected #{double_js_path} to exist, but it didn't" assert File.exist?(double_gif_path), "Expected #{double_gif_path} to exist, but it didn't" end test 'unregistering exporter without mime type' do @env.register_exporter '*/*', DoubleExtensionExporter @env.unregister_exporter DoubleExtensionExporter @manifest.compile('application.js') js_path = @env['application.js'].digest_path double_js_path = "#{@dir}/#{js_path}js" assert !File.exist?(double_js_path) end test 'unregistering exporter with mime type' do @env.register_exporter 'application/javascript', DoubleExtensionExporter @env.unregister_exporter 'application/javascript', DoubleExtensionExporter @manifest.compile('application.js') js_path = @env['application.js'].digest_path double_js_path = "#{@dir}/#{js_path}js" assert !File.exist?(double_js_path) end test 'unregistering exporter with multiple mime types' do @env.register_exporter %w(application/javascript image/gif), ReverseExtensionExporter @env.unregister_exporter %w(application/javascript image/gif), ReverseExtensionExporter @manifest.compile('application.js') @manifest.compile('blank.gif') js_path = @env['application.js'].digest_path gif_path = @env['blank.gif'].digest_path reverse_js_path = "#{@dir}/#{js_path[0..-4]}.sj" double_js_path = "#{@dir}/#{js_path}js" reverse_gif_path = "#{@dir}/#{gif_path[0..-5]}.fig" double_gif_path = "#{@dir}/#{gif_path}gif" assert !File.exist?(reverse_js_path), "Expected #{ reverse_js_path } to not exist, but it did" assert !File.exist?(reverse_gif_path), "Expected #{ reverse_gif_path } to not exist, but it did" assert !File.exist?(double_js_path), "Expected #{ double_js_path } to not exist, but it did" assert !File.exist?(double_gif_path), "Expected #{ double_gif_path } to not exist, but it did" end end class ReverseExtensionExporter < Sprockets::Exporters::Base def call split = target.split('.') split[1] = split[1].reverse new_target = split.join('.') write(new_target) do |f| f.write(asset.source) end return new_target end end class DoubleExtensionExporter < Sprockets::Exporters::Base def call split = target.split('.') new_target = target + split[1] write(new_target) do |f| f.write(asset.source) end return new_target end end sprockets-4.2.1/test/test_http_utils.rb000066400000000000000000000107271447572140400203140ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/http_utils' class TestHTTPUtils < Minitest::Test include Sprockets::HTTPUtils def test_match_mime_type assert match_mime_type?("text/html", "text/*") assert match_mime_type?("text/plain", "*") refute match_mime_type?("text/html", "application/json") end def test_match_mime_type_keys h = { "text/html" => 1, "text/plain" => 2, "application/json" => 3, "text/*" => 4, "*/*" => 5, "*" => 6 } assert_equal [6, 5, 4, 1], match_mime_type_keys(h, "text/html") assert_equal [6, 5, 4, 2], match_mime_type_keys(h, "text/plain") assert_equal [6, 5, 3], match_mime_type_keys(h, "application/json") assert_equal [6, 5], match_mime_type_keys(h, "application/javascript") end def test_parse_q_values assert_equal [], parse_q_values(nil) assert_equal [["audio/*", 0.2], ["audio/basic", 1.0]], parse_q_values("audio/*; q=0.2, audio/basic") assert_equal [["text/plain", 0.5], ["text/html", 1.0], ["text/x-dvi", 0.8], ["text/x-c", 1.0]], parse_q_values("text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c") assert_equal [["text/*", 1.0], ["text/html", 1.0], ["text/html", 1.0], ["*/*", 1.0]], parse_q_values("text/*, text/html, text/html;level=1, */*") assert_equal [["text/*", 0.3], ["text/html", 0.7], ["text/html", 1.0], ["text/html", 1.0], ["*/*", 0.5]], parse_q_values("text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5") assert_equal [["iso-8859-5", 1.0], ["unicode-1-1", 0.8]], parse_q_values("iso-8859-5, unicode-1-1;q=0.8") assert_equal [["compress", 1.0], ["gzip", 1.0]], parse_q_values("compress, gzip") assert_equal [["*", 1.0]], parse_q_values("*") assert_equal [["compress", 0.5], ["gzip", 1.0]], parse_q_values("compress;q=0.5, gzip;q=1.0") assert_equal [["gzip", 1.0], ["identity", 0.5], ["*", 0.0]], parse_q_values("gzip;q=1.0, identity; q=0.5, *;q=0") end def test_find_q_matches accept = "text/plain; q=0.5, image/*" assert_equal ["text/plain"], find_mime_type_matches(accept, ["text/plain"]) assert_equal ["image/svg+xml"], find_mime_type_matches(accept, ["image/svg+xml"]) assert_equal ["image/svg+xml"], find_mime_type_matches(accept, ["image/svg+xml", "image/png"]) assert_equal ["image/svg+xml", "text/plain"], find_mime_type_matches(accept, ["image/svg+xml", "text/plain"]) assert_equal [], find_mime_type_matches(accept, ["text/css"]) end def test_find_matches_with_parsed_q_values accept = [["text/plain", 0.5], ["image/*", 1.0]] assert_equal ["text/plain"], find_mime_type_matches(accept, ["text/plain"]) assert_equal ["image/svg+xml"], find_mime_type_matches(accept, ["image/svg+xml"]) assert_equal ["image/svg+xml"], find_mime_type_matches(accept, ["image/svg+xml", "image/png"]) assert_equal ["image/svg+xml", "text/plain"], find_mime_type_matches(accept, ["image/svg+xml", "text/plain"]) assert_equal [], find_mime_type_matches(accept, ["text/css"]) end def test_find_best_q_match accept = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c" assert_equal "text/plain", find_best_mime_type_match(accept, ["text/plain"]) assert_equal "text/html", find_best_mime_type_match(accept, ["text/html"]) assert_equal "text/html", find_best_mime_type_match(accept, ["text/plain", "text/html"]) assert_equal "text/html", find_best_mime_type_match(accept, ["text/html", "text/plain"]) refute find_best_mime_type_match(accept, ["text/yaml"]) refute find_best_mime_type_match(accept, []) accept = "sdch, gzip, deflate" assert_equal "sdch", find_best_q_match(accept, ["sdch", "gzip"]) assert_equal "gzip", find_best_q_match(accept, ["gzip"]) assert_equal "deflate", find_best_q_match(accept, ["deflate"]) refute find_best_q_match(accept, ["base64"]) refute find_best_q_match(accept, []) refute find_best_q_match(nil, ["gzip"]) end def test_find_best_q_match_with_parsed_q_values assert accept = parse_q_values("text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c") assert_equal "text/plain", find_best_mime_type_match(accept, ["text/plain"]) assert_equal "text/html", find_best_mime_type_match(accept, ["text/html"]) assert_equal "text/html", find_best_mime_type_match(accept, ["text/plain", "text/html"]) assert_equal "text/html", find_best_mime_type_match(accept, ["text/html", "text/plain"]) end end sprockets-4.2.1/test/test_jsminc_compressor.rb000066400000000000000000000011511447572140400216430ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/cache' unless RUBY_PLATFORM.include?('java') require 'sprockets/jsminc_compressor' class TestJSMincCompressor < Minitest::Test def test_compress_javascript input = { content_type: 'application/javascript', data: "function foo() {\n return true;\n}", cache: Sprockets::Cache.new } output = "function foo(){return true;}" assert_equal output, Sprockets::JSMincCompressor.call(input) end def test_cache_key assert Sprockets::JSMincCompressor.cache_key end end endsprockets-4.2.1/test/test_jst_processor.rb000066400000000000000000000021561447572140400210110ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/jst_processor' class TestJstProcessor < Minitest::Test def test_export_js_template_in_JST input = { name: 'users/show', content_type: 'application/javascript', data: "function(obj) {\n return 'Hello, '+obj.name;\n}", cache: Sprockets::Cache.new } output = <<-EOS (function() { this.JST || (this.JST = {}); this.JST["users/show"] = function(obj) { return 'Hello, '+obj.name; }; }).call(this); EOS assert_equal output, Sprockets::JstProcessor.call(input) end def test_export_js_template_in_TEMPLATES input = { name: 'users/show', content_type: 'application/javascript', data: "function(obj) {\n return 'Hello, '+obj.name;\n}", cache: Sprockets::Cache.new } output = <<-EOS (function() { this.TEMPLATES || (this.TEMPLATES = {}); this.TEMPLATES["users/show"] = function(obj) { return 'Hello, '+obj.name; }; }).call(this); EOS assert_equal output, Sprockets::JstProcessor.new(namespace: 'this.TEMPLATES').call(input) end end sprockets-4.2.1/test/test_loader.rb000066400000000000000000000060761447572140400173650ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'pathname' class TestLoader < Sprockets::TestCase def setup @env = Sprockets::Environment.new(".") @env.append_path(fixture_path('default')) end test "load asset by uri" do assert asset = @env.load("file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript") assert_equal fixture_path('default/gallery.js'), asset.filename assert_equal 'application/javascript', asset.content_type assert_equal '828e4be75f8bf69529b5d618dd12a6144d58d47cf4c3a9e3f64b0b8812008dab', asset.etag assert asset = @env.load(asset.uri) assert_equal fixture_path('default/gallery.js'), asset.filename assert_equal 'application/javascript', asset.content_type assert_equal '828e4be75f8bf69529b5d618dd12a6144d58d47cf4c3a9e3f64b0b8812008dab', asset.etag assert asset = @env.load("file://#{fixture_path_for_uri('default/gallery.css.erb')}?type=text/css") assert_equal fixture_path('default/gallery.css.erb'), asset.filename assert_equal 'text/css', asset.content_type assert asset = @env.load(Pathname.new("file://#{fixture_path_for_uri('default/gallery.css.erb')}?type=text/css")) assert_equal fixture_path('default/gallery.css.erb'), asset.filename assert_equal 'text/css', asset.content_type bad_id = "0000000000000000000000000000000000000000" assert asset = @env.load("file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript&id=#{bad_id}") assert_equal fixture_path('default/gallery.js'), asset.filename assert_equal 'application/javascript', asset.content_type assert_raises Sprockets::FileNotFound do @env.load("file://#{fixture_path_for_uri('default/missing.js')}?type=application/javascript") end assert_raises Sprockets::ConversionError do @env.load("file://#{fixture_path_for_uri('default/gallery.js')}?type=text/css") end assert asset = @env.load("file://#{fixture_path_for_uri('default/blue_jpg.jpg')}?type=image/jpeg") assert_equal fixture_path('default/blue_jpg.jpg'), asset.filename assert_equal 'blue_jpg.jpg', asset.logical_path assert asset = @env.load("file://#{fixture_path_for_uri('default/blue_jpeg.jpeg')}?type=image/jpeg") assert_equal fixture_path('default/blue_jpeg.jpeg'), asset.filename assert_equal 'blue_jpeg.jpeg', asset.logical_path end test 'load outside asset' do assert_raises Sprockets::FileOutsidePaths do @env.load("file://#{fixture_path_for_uri('asset/one.css')}?type=text%2Fcss") end end test 'load uri with index alias' do filename = fixture_path('default/coffee/index.js') index_alias = fixture_path('default/coffee.js') assert asset = @env.load("file://#{uri_path(filename)}?type=application/javascript&index_alias=#{Rack::Utils.escape(index_alias)}") assert_equal filename, asset.filename, asset.inspect assert_equal 'coffee.js', asset.logical_path, asset.inspect assert_equal fixture_path('default'), asset.to_hash[:load_path], asset.inspect assert_equal 'application/javascript', asset.content_type end end sprockets-4.2.1/test/test_manifest.rb000066400000000000000000000600041447572140400177140ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'fileutils' require 'tmpdir' require 'securerandom' class TestManifest < Sprockets::TestCase def setup @env = Sprockets::Environment.new(".") do |env| env.append_path(fixture_path('default')) end @dir = Dir.mktmpdir FileUtils.mkdir_p(@dir) end def teardown FileUtils.remove_entry_secure(@dir) if File.exist?(@dir) assert Dir["#{@dir}/*"].empty?, "Expected #{Dir["#{@dir}/*"]} to be empty but it was not" end test 'linking same file twice does not raise an error' do config_manifest = 'link_same_file_twice.js' @env.append_path(fixture_path('double')) output_manifest_json = File.join(@dir, "manifest.json") manifest = Sprockets::Manifest.new(@env, @dir, output_manifest_json) manifest.compile(config_manifest) end test 'double rendering with link_directory raises an error' do config_manifest = 'link_directory_manifest.js' @env.append_path(fixture_path('double')) output_manifest_json = File.join(@dir, "manifest.json") manifest = Sprockets::Manifest.new(@env, @dir, output_manifest_json) assert_raises(Sprockets::DoubleLinkError) do manifest.compile(config_manifest) end end test "specify full manifest filename" do directory = Dir::tmpdir filename = File.join(directory, 'manifest.json') manifest = Sprockets::Manifest.new(@env, filename) assert_equal directory, manifest.directory assert_equal filename, manifest.filename assert_equal filename, manifest.path end test "specify manifest directory yields random .sprockets-manifest-*.json" do manifest = Sprockets::Manifest.new(@env, @dir) assert_equal @dir, manifest.directory assert_match(/^\.sprockets-manifest-[a-f0-9]{32}.json/, File.basename(manifest.filename)) manifest.save assert_match(/^\.sprockets-manifest-[a-f0-9]{32}.json/, File.basename(manifest.filename)) end test "specify manifest directory with existing .sprockets-manifest-*.json" do path = File.join(@dir, ".sprockets-manifest-#{SecureRandom.hex(16)}.json") File.open(path, 'w') { |f| f.write "{}" } assert File.exist?(path) manifest = Sprockets::Manifest.new(@env, @dir) assert_equal @dir, manifest.directory assert_equal path, manifest.filename end test "specify manifest directory and separate location" do root = File.join(Dir::tmpdir, 'public') dir = File.join(root, 'assets') path = File.join(root, 'manifest-123.json') system "rm -rf #{root}" assert !File.exist?(root) manifest = Sprockets::Manifest.new(@env, dir, path) assert_equal dir, manifest.directory assert_equal path, manifest.filename end test "compile asset" do @env.version = '1.1.2' manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) digest_path = @env['application.js'].digest_path assert !File.exist?("#{@dir}/#{digest_path}") manifest.compile('application.js') assert File.directory?(manifest.directory) assert File.file?(manifest.filename) assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][digest_path] assert_equal "application.js", data['files'][digest_path]['logical_path'] assert data['files'][digest_path]['size'] > 230 assert_equal digest_path, data['assets']['application.js'] end test "compile index asset" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) digest_path = @env['coffee/index.js'].digest_path assert_match(/coffee\/index-\w+.js/, digest_path) assert !File.exist?("#{@dir}/#{digest_path}") manifest.compile('coffee/index.js') assert File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][digest_path] assert_equal "coffee/index.js", data['files'][digest_path]['logical_path'] assert_equal digest_path, data['assets']['coffee/index.js'] end test "compile index asset by alias" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) digest_path = @env['coffee.js'].digest_path assert_match(/coffee-\w+.js/, digest_path) assert !File.exist?("#{@dir}/#{digest_path}") manifest.compile('coffee.js') assert File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][digest_path] assert_equal "coffee.js", data['files'][digest_path]['logical_path'] assert !data['assets']['coffee/index.js'] assert_equal digest_path, data['assets']['coffee.js'] end test "compile asset with aliased index links" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) main_digest_path = @env['alias-index-link.js'].digest_path dep_digest_path = @env['coffee.js'].digest_path assert !File.exist?("#{@dir}/#{main_digest_path}") assert !File.exist?("#{@dir}/#{dep_digest_path}") manifest.compile('alias-index-link.js') assert File.directory?(manifest.directory) assert File.file?(manifest.filename) assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{main_digest_path}") assert File.exist?("#{@dir}/#{dep_digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][main_digest_path] assert data['files'][dep_digest_path] assert_equal "alias-index-link.js", data['files'][main_digest_path]['logical_path'] assert_equal "coffee.js", data['files'][dep_digest_path]['logical_path'] assert_equal main_digest_path, data['assets']['alias-index-link.js'] assert_equal dep_digest_path, data['assets']['coffee.js'] end test "compile to directory and separate location" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) root = File.join(Dir::tmpdir, 'public') dir = File.join(root, 'assets') path = File.join(root, 'manifests', 'manifest-123.json') system "rm -rf #{root}" assert !File.exist?(root) manifest = Sprockets::Manifest.new(@env, dir, path) manifest.compile('application.js') assert File.directory?(manifest.directory) assert File.file?(manifest.filename) end test "compile with legacy manifest" do root = File.join(Dir::tmpdir, 'public') dir = File.join(root, 'assets') path = File.join(root, "manifest-#{SecureRandom.hex(16)}.json") system "rm -rf #{root}" assert !File.exist?(root) system "rm -rf #{dir}/.sprockets-manifest*.json" system "rm -rf #{dir}/manifest*.json" FileUtils.mkdir_p(dir) File.open(path, 'w') { |f| f.write "{}" } manifest = Sprockets::Manifest.new(@env, dir) manifest.compile('application.js') assert File.directory?(manifest.directory) assert File.file?(manifest.filename) assert_match %r{.sprockets-manifest-[a-f0-9]{32}.json}, manifest.filename end test "compile asset with absolute path" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) digest_path = @env['gallery.js'].digest_path assert !File.exist?("#{@dir}/#{digest_path}") manifest.compile(fixture_path('default/gallery.js')) assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][digest_path] assert_equal digest_path, data['assets']['gallery.js'] end test "compile multiple assets" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) app_digest_path = @env['application.js'].digest_path gallery_digest_path = @env['gallery.css'].digest_path assert !File.exist?("#{@dir}/#{app_digest_path}") assert !File.exist?("#{@dir}/#{gallery_digest_path}") manifest.compile('application.js', 'gallery.css') assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{app_digest_path}") assert File.exist?("#{@dir}/#{gallery_digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][app_digest_path] assert data['files'][gallery_digest_path] assert_equal app_digest_path, data['assets']['application.js'] assert_equal gallery_digest_path, data['assets']['gallery.css'] end test "compile with transformed asset" do assert svg_digest_path = @env['logo.svg'].digest_path assert png_digest_path = @env['logo.png'].digest_path assert !File.exist?("#{@dir}/#{svg_digest_path}") assert !File.exist?("#{@dir}/#{png_digest_path}") manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) manifest.compile('logo.svg', 'logo.png') assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{svg_digest_path}") assert File.exist?("#{@dir}/#{png_digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][svg_digest_path] assert data['files'][png_digest_path] assert_equal svg_digest_path, data['assets']['logo.svg'] assert_equal png_digest_path, data['assets']['logo.png'] end test "compile asset with links" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) main_digest_path = @env['gallery-link.js'].digest_path dep_digest_path = @env['gallery.js'].digest_path assert !File.exist?("#{@dir}/#{main_digest_path}") assert !File.exist?("#{@dir}/#{dep_digest_path}") manifest.compile('gallery-link.js') assert File.directory?(manifest.directory) assert File.file?(manifest.filename) assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{main_digest_path}") assert File.exist?("#{@dir}/#{dep_digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][main_digest_path] assert data['files'][dep_digest_path] assert_equal "gallery-link.js", data['files'][main_digest_path]['logical_path'] assert_equal "gallery.js", data['files'][dep_digest_path]['logical_path'] assert_equal main_digest_path, data['assets']['gallery-link.js'] assert_equal dep_digest_path, data['assets']['gallery.js'] end test "compile nested asset with links" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) main_digest_path = @env['explore-link.js'].digest_path dep_digest_path = @env['gallery-link.js'].digest_path subdep_digest_path = @env['gallery.js'].digest_path assert !File.exist?("#{@dir}/#{main_digest_path}") assert !File.exist?("#{@dir}/#{dep_digest_path}") assert !File.exist?("#{@dir}/#{subdep_digest_path}") manifest.compile('explore-link.js') assert File.directory?(manifest.directory) assert File.file?(manifest.filename) assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{main_digest_path}") assert File.exist?("#{@dir}/#{dep_digest_path}") assert File.exist?("#{@dir}/#{subdep_digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][main_digest_path] assert data['files'][dep_digest_path] assert data['files'][subdep_digest_path] assert_equal "explore-link.js", data['files'][main_digest_path]['logical_path'] assert_equal "gallery-link.js", data['files'][dep_digest_path]['logical_path'] assert_equal "gallery.js", data['files'][subdep_digest_path]['logical_path'] assert_equal main_digest_path, data['assets']['explore-link.js'] assert_equal dep_digest_path, data['assets']['gallery-link.js'] assert_equal subdep_digest_path, data['assets']['gallery.js'] end test "recompile asset when environment version is changed" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) digest_path = @env['application.js'].digest_path filename = fixture_path('default/application.coffee') sandbox filename do assert !File.exist?("#{@dir}/#{digest_path}"), Dir["#{@dir}/*"].inspect manifest.compile('application.js') assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{digest_path}") @env.version = '1.1.3' new_digest_path = @env['application.js'].digest_path assert !File.exist?("#{@dir}/#{new_digest_path}"), Dir["#{@dir}/*"].inspect manifest.compile('application.js') assert File.exist?("#{@dir}/#{new_digest_path}") end end test "recompile asset" do manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) digest_path = @env['application.js'].digest_path filename = fixture_path('default/application.coffee') sandbox filename do assert !File.exist?("#{@dir}/#{digest_path}"), Dir["#{@dir}/*"].inspect manifest.compile('application.js') assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][digest_path] assert_equal digest_path, data['assets']['application.js'] File.open(filename, 'w') { |f| f.write "change;" } mtime = Time.now + 1 File.utime(mtime, mtime, filename) new_digest_path = @env['application.js'].digest_path manifest.compile('application.js') assert File.exist?("#{@dir}/manifest.json") assert File.exist?("#{@dir}/#{digest_path}") assert File.exist?("#{@dir}/#{new_digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][digest_path] assert data['files'][new_digest_path] assert_equal new_digest_path, data['assets']['application.js'] end end test "remove asset" do manifest = Sprockets::Manifest.new(@env, @dir) digest_path = @env['application.js'].digest_path manifest.compile('application.js') assert File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert data['files'][digest_path] assert data['assets']['application.js'] manifest.remove(digest_path) assert !File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert !data['files'][digest_path] assert !data['assets']['application.js'] end test "remove old asset" do manifest = Sprockets::Manifest.new(@env, @dir) digest_path = @env['application.js'].digest_path filename = fixture_path('default/application.coffee') sandbox filename do manifest.compile('application.js') assert File.exist?("#{@dir}/#{digest_path}") File.open(filename, 'w') { |f| f.write "change;" } mtime = Time.now + 1 File.utime(mtime, mtime, filename) new_digest_path = @env['application.js'].digest_path manifest.compile('application.js') assert File.exist?("#{@dir}/#{new_digest_path}") manifest.remove(digest_path) assert !File.exist?("#{@dir}/#{digest_path}") data = JSON.parse(File.read(manifest.filename)) assert !data['files'][digest_path] assert data['files'][new_digest_path] assert_equal new_digest_path, data['assets']['application.js'] end end test "remove old backups(count)" do manifest = Sprockets::Manifest.new(@env, @dir) digest_path = @env['application.js'].digest_path filename = fixture_path('default/application.coffee') sandbox filename do manifest.compile('application.js') assert File.exist?("#{@dir}/#{digest_path}") Timecop.travel(Time.now + 1) File.open(filename, 'w') { |f| f.write "a;" } mtime = Time.now File.utime(mtime, mtime, filename) new_digest_path1 = @env['application.js'].digest_path manifest.compile('application.js') assert File.exist?("#{@dir}/#{new_digest_path1}") Timecop.travel(Time.now + 1) File.open(filename, 'w') { |f| f.write "b;" } mtime = Time.now File.utime(mtime, mtime, filename) new_digest_path2 = @env['application.js'].digest_path manifest.compile('application.js') assert File.exist?("#{@dir}/#{new_digest_path2}") Timecop.travel(Time.now + 1) File.open(filename, 'w') { |f| f.write "c;" } mtime = Time.now File.utime(mtime, mtime, filename) new_digest_path3 = @env['application.js'].digest_path manifest.compile('application.js') assert File.exist?("#{@dir}/#{new_digest_path3}") manifest.clean(1, 0) assert !File.exist?("#{@dir}/#{digest_path}") assert !File.exist?("#{@dir}/#{new_digest_path1}") assert File.exist?("#{@dir}/#{new_digest_path2}") assert File.exist?("#{@dir}/#{new_digest_path3}") data = JSON.parse(File.read(manifest.filename)) assert !data['files'][digest_path] assert !data['files'][new_digest_path1] assert data['files'][new_digest_path2] assert data['files'][new_digest_path3] assert_equal new_digest_path3, data['assets']['application.js'] end ensure Timecop.return end test "test manifest does not exist" do assert !File.exist?("#{@dir}/manifest.json") manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) manifest.compile('application.js') assert File.exist?("#{@dir}/manifest.json") data = JSON.parse(File.read(manifest.filename)) assert data['assets']['application.js'] end test "test blank manifest" do assert !File.exist?("#{@dir}/manifest.json") FileUtils.mkdir_p(@dir) File.open("#{@dir}/manifest.json", 'w') { |f| f.write "" } assert_equal "", File.read("#{@dir}/manifest.json") manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) manifest.compile('application.js') assert File.exist?("#{@dir}/manifest.json") data = JSON.parse(File.read(manifest.filename)) assert data['assets']['application.js'] end test "test skip invalid manifest" do assert !File.exist?("#{@dir}/manifest.json") FileUtils.mkdir_p(@dir) File.open("#{@dir}/manifest.json", 'w') { |f| f.write "not valid json;" } assert_equal "not valid json;", File.read("#{@dir}/manifest.json") manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) manifest.compile('application.js') assert File.exist?("#{@dir}/manifest.json") data = JSON.parse(File.read(manifest.filename)) assert data['assets']['application.js'] end test "nil environment raises compilation error" do assert !File.exist?("#{@dir}/manifest.json") manifest = Sprockets::Manifest.new(nil, File.join(@dir, 'manifest.json')) assert_raises Sprockets::Error do manifest.compile('application.js') end end test "no environment raises compilation error" do assert !File.exist?("#{@dir}/manifest.json") manifest = Sprockets::Manifest.new(File.join(@dir, 'manifest.json')) assert_raises Sprockets::Error do manifest.compile('application.js') end end test "find_sources with environment" do manifest = Sprockets::Manifest.new(@env, @dir) result = manifest.find_sources("mobile/a.js", "mobile/b.js") assert_equal ["var A;\n", "var B;\n"], result.to_a.sort result = manifest.find_sources("not_existent.js", "also_not_existent.js") assert_equal [], result.to_a result = manifest.find_sources("mobile/a.js", "also_not_existent.js") assert_equal ["var A;\n"], result.to_a end test "find_sources without environment" do manifest = Sprockets::Manifest.new(@env, @dir) manifest.compile('mobile/a.js', 'mobile/b.js') manifest = Sprockets::Manifest.new(nil, @dir) result = manifest.find_sources("mobile/a.js", "mobile/b.js") assert_equal ["var A;\n", "var B;\n"], result.to_a result = manifest.find_sources("not_existent.js", "also_not_existent.js") assert_equal [], result.to_a result = manifest.find_sources("mobile/a.js", "also_not_existent.js") assert_equal ["var A;\n"], result.to_a end test "compress non-binary assets" do manifest = Sprockets::Manifest.new(@env, @dir) %W{ gallery.css application.js logo.svg favicon.ico }.each do |file_name| original_path = @env[file_name].digest_path manifest.compile(file_name) assert File.exist?("#{@dir}/#{original_path}.gz"), "Expecting '#{original_path}' to generate gzipped file: '#{original_path}.gz' but it did not" end end test "writes gzip files even if files were already on disk" do @env.gzip = false manifest = Sprockets::Manifest.new(@env, @dir) files = %W{ gallery.css application.js logo.svg favicon.ico} files.each do |file_name| original_path = @env[file_name].digest_path manifest.compile(file_name) assert File.exist?("#{@dir}/#{original_path}"), "Expecting \"#{@dir}/#{original_path}\" to exist but did not" end @env.gzip = true files.each do |file_name| original_path = @env[file_name].digest_path manifest.compile(file_name) assert File.exist?("#{@dir}/#{original_path}.gz"), "Expecting '#{original_path}' to generate gzipped file: '#{original_path}.gz' but it did not" end end test "disable file gzip" do @env.gzip = false manifest = Sprockets::Manifest.new(@env, @dir) %W{ gallery.css application.js logo.svg favicon.ico }.each do |file_name| original_path = @env[file_name].digest_path manifest.compile(file_name) refute File.exist?("#{@dir}/#{original_path}.gz"), "Expecting '#{original_path}' to not generate gzipped file: '#{original_path}.gz' but it did" end end test "do not compress binary assets" do manifest = Sprockets::Manifest.new(@env, @dir) %W{ blank.gif }.each do |file_name| original_path = @env[file_name].digest_path manifest.compile(file_name) refute File.exist?("#{@dir}/#{original_path}.gz"), "Expecting '#{original_path}' to not generate gzipped file: '#{original_path}.gz' but it did" end end test 'raises exception when gzip fails' do manifest = Sprockets::Manifest.new(@env, @dir) Zlib::GzipWriter.stub(:new, -> (io, level) { fail 'kaboom' }) do ex = assert_raises(RuntimeError) { manifest.compile('application.js') } assert_equal 'kaboom', ex.message end end # Sleep duration to context switch between concurrent threads. CONTEXT_SWITCH_DURATION = 0.1 # Record Exporter sequence with a delay to test concurrency. class SlowExporter < Sprockets::Exporters::Base class << self attr_accessor :seq end def call SlowExporter.seq << '0' sleep CONTEXT_SWITCH_DURATION SlowExporter.seq << '1' end end class SlowExporter2 < SlowExporter end test 'concurrent exporting' do # Register 2 exporters and compile 2 files to ensure that # all 4 exporting tasks run concurrently. SlowExporter.seq = [] @env.register_exporter 'image/png',SlowExporter @env.register_exporter 'image/png',SlowExporter2 Sprockets::Manifest.new(@env, @dir).compile('logo.png', 'troll.png') refute_equal %w(0 1 0 1 0 1 0 1), SlowExporter.seq end test 'sequential exporting' do @env.export_concurrent = false SlowExporter.seq = [] @env.register_exporter 'image/png',SlowExporter @env.register_exporter 'image/png',SlowExporter2 Sprockets::Manifest.new(@env, @dir).compile('logo.png', 'troll.png') assert_equal %w(0 1 0 1 0 1 0 1), SlowExporter.seq end # Record Processor sequence with a delay to test concurrency. class SlowProcessor attr_reader :seq def initialize @seq = [] end def call(_) seq << '0' sleep CONTEXT_SWITCH_DURATION seq << '1' nil end end test 'concurrent processing' do processor = SlowProcessor.new @env.register_postprocessor 'image/png', processor Sprockets::Manifest.new(@env, @dir).compile('logo.png', 'troll.png') refute_equal %w(0 1 0 1), processor.seq end test 'sequential processing' do @env.export_concurrent = false processor = SlowProcessor.new @env.register_postprocessor 'image/png', processor Sprockets::Manifest.new(@env, @dir).compile('logo.png', 'troll.png') assert_equal %w(0 1 0 1), processor.seq end test 'clobber works when cache_dir not created' do @cache_dir = File.join(Dir::tmpdir, 'sprockets') refute File.exist?(@cache_dir) @env.cache = Sprockets::Cache::FileStore.new(@cache_dir) manifest = Sprockets::Manifest.new(@env, File.join(@dir, 'manifest.json')) manifest.clobber end end sprockets-4.2.1/test/test_manifest_utils.rb000066400000000000000000000023411447572140400211340ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/manifest_utils' require 'logger' class TestManifestUtils < Minitest::Test include Sprockets::ManifestUtils def test_generate_manifest_path assert_match(MANIFEST_RE, generate_manifest_path) end def test_find_directory_manifest root = File.expand_path("../fixtures/manifest_utils", __FILE__) assert_match MANIFEST_RE, File.basename(find_directory_manifest(root)) assert_equal "#{root}/default/.sprockets-manifest-f4bf345974645583d284686ddfb7625e.json", find_directory_manifest("#{root}/default") end def test_warn_on_two root = File.expand_path("../fixtures/manifest_utils", __FILE__) assert_match MANIFEST_RE, File.basename(find_directory_manifest(root)) r, w = IO.pipe logger = Logger.new(w) # finds the first one alphabetically assert_equal "#{root}/with_two_manifests/.sprockets-manifest-00000000000000000000000000000000.json", find_directory_manifest("#{root}/with_two_manifests", logger) output = r.gets assert_match(/W, \[[^\]]+\] WARN -- : Found multiple manifests: .+ Choosing the first alphabetically: \.sprockets-manifest-00000000000000000000000000000000\.json/, output) end end sprockets-4.2.1/test/test_path_dependency_utils.rb000066400000000000000000000062001447572140400224560ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/path_dependency_utils' class TestPathDependencyUtils < Minitest::Test include Sprockets::PathDependencyUtils def test_entries_with_dependencies path = File.expand_path("../fixtures", __FILE__) filenames, deps = entries_with_dependencies(path) assert_kind_of Array, filenames assert filenames.size > 1 assert_equal [build_file_digest_uri(path)], deps.to_a path = "/tmp/sprockets/missingdir" filenames, deps = entries_with_dependencies(path) assert_kind_of Array, filenames assert filenames.empty? assert_equal [build_file_digest_uri(path)], deps.to_a end FILES_IN_SERVER = Dir["#{File.expand_path("../fixtures/server", __FILE__)}/*"] def test_stat_directory_with_dependencies dirname = File.expand_path("../fixtures/server", __FILE__) filenames, deps = stat_directory_with_dependencies(dirname) assert_equal FILES_IN_SERVER.size, filenames.size assert_equal [build_file_digest_uri(dirname)], deps.to_a path, stat = filenames.first assert_equal File.expand_path("../fixtures/server/app", __FILE__), path assert_kind_of File::Stat, stat dirname = File.expand_path("../fixtures/missing", __FILE__) filenames, deps = stat_directory_with_dependencies(dirname) assert_equal [], filenames assert_equal [build_file_digest_uri(dirname)], deps.to_a end def test_stat_sorted_tree_with_dependencies dirname = File.expand_path("../fixtures/asset/tree/all", __FILE__) filenames, deps = stat_sorted_tree_with_dependencies(dirname) assert_equal 11, filenames.size assert_equal [ File.expand_path("../fixtures/asset/tree/all", __FILE__), File.expand_path("../fixtures/asset/tree/all/b", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c", __FILE__), File.expand_path("../fixtures/asset/tree/all/d", __FILE__) ].map { |p| build_file_digest_uri(p) }, deps.to_a path, stat = filenames.first assert_equal File.expand_path("../fixtures/asset/tree/all/README.md", __FILE__), path assert_kind_of File::Stat, stat assert_equal [ File.expand_path("../fixtures/asset/tree/all/README.md", __FILE__), File.expand_path("../fixtures/asset/tree/all/b.css", __FILE__), File.expand_path("../fixtures/asset/tree/all/b.js.erb", __FILE__), File.expand_path("../fixtures/asset/tree/all/b", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c/d.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c/e.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/d", __FILE__), File.expand_path("../fixtures/asset/tree/all/d/c.coffee", __FILE__), File.expand_path("../fixtures/asset/tree/all/d/e.js", __FILE__), ], filenames.map(&:first) dirname = File.expand_path("../fixtures/missing", __FILE__) filenames, deps = stat_sorted_tree_with_dependencies(dirname) assert_equal [], filenames assert_equal [build_file_digest_uri(dirname)], deps.to_a end end sprockets-4.2.1/test/test_path_digest_utils.rb000066400000000000000000000051211447572140400216200ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/path_digest_utils' class TestPathDigestUtils < Minitest::Test include Sprockets::PathDigestUtils def test_file_stat_digest path = File.expand_path("../fixtures/default/hello.txt", __FILE__) assert_equal "81491ac958ab51a3bc7f34cae434cf00c49861402bf6c8961e8ee32afa7c4cf8", stat_digest(path, File.stat(path)).unpack("h*")[0] assert_equal "81491ac958ab51a3bc7f34cae434cf00c49861402bf6c8961e8ee32afa7c4cf8", file_digest(path).unpack("h*")[0] end def test_directory_stat_digest path = File.expand_path("../fixtures/default/app", __FILE__) assert_equal "8514e7f087b1666549d97352c8b80925de62e6e27b5a61c3dab780366e2b19a6", stat_digest(path, File.stat(path)).unpack("h*")[0] assert_equal "8514e7f087b1666549d97352c8b80925de62e6e27b5a61c3dab780366e2b19a6", file_digest(path).unpack("h*")[0] end def test_symlink_stat_digest skip "no symlinks available" unless File.symlink?(File.expand_path("../fixtures/default/symlink", __FILE__)) path = File.expand_path("../fixtures/default/mobile", __FILE__) assert_equal "e571f54b8982049817ee30d0bf0dcf5dd8c09252b50696f7ccb44019c9229ccd", stat_digest(path, File.stat(path)).unpack("h*")[0] path = File.expand_path("../fixtures/default/symlink", __FILE__) assert_equal "e571f54b8982049817ee30d0bf0dcf5dd8c09252b50696f7ccb44019c9229ccd", stat_digest(path, File.stat(path)).unpack("h*")[0] assert_equal "e571f54b8982049817ee30d0bf0dcf5dd8c09252b50696f7ccb44019c9229ccd", file_digest(path).unpack("h*")[0] end def test_unix_device_stat_digest if File.exist?("/dev/stdin") && File.stat("/dev/stdin").chardev? assert_raises(TypeError) do stat_digest("/dev/stdin", File.stat("/dev/stdin")) end assert_raises(TypeError) do file_digest("/dev/stdin") end else skip "no unix device available" end end def test_missing_file_digest path = "./filedoesnotexist" refute File.exist?(path) refute file_digest(path) end def test_multiple_file_digests skip "no symlinks available" unless File.symlink?(File.expand_path("../fixtures/default/symlink", __FILE__)) paths = [] paths << File.expand_path("../fixtures/default/hello.txt", __FILE__) paths << File.expand_path("../fixtures/default/app", __FILE__) paths << File.expand_path("../fixtures/default/symlink", __FILE__) paths << "./filedoesnotexist" assert_equal "95f41ef27ae30ceaaa726f85b3298e1be4ff4bf5bf83deec0f760b50e3ffc09f", files_digest(paths).unpack("h*")[0] end end sprockets-4.2.1/test/test_path_utils.rb000066400000000000000000000277661447572140400203040ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/path_utils' class TestPathUtils < Minitest::Test include Sprockets::PathUtils DOSISH = File::ALT_SEPARATOR != nil DOSISH_DRIVE_LETTER = File.dirname("A:") == "A:." DOSISH_UNC = File.dirname("//") == "//" def test_stat assert_kind_of File::Stat, stat(File.expand_path("../fixtures", __FILE__)) refute stat("/tmp/sprockets/missingfile") end def test_file assert_equal true, file?(File.expand_path("../fixtures/default/hello.txt", __FILE__)) assert_equal false, file?(File.expand_path("../fixtures", __FILE__)) end def test_directory assert_equal true, directory?(File.expand_path("../fixtures", __FILE__)) assert_equal false, directory?(File.expand_path("../fixtures/default/hello.txt", __FILE__)) end def test_entries assert_equal [ "asset", "compass", "context", "default", "directives", "double", "encoding", "errors", "index-assets", "manifest_utils", "octicons", "paths", "public", "resolve", "sass", "server", "source-maps", "symlink" ], entries(File.expand_path("../fixtures", __FILE__)) [ ['a', 'b'], ['a', 'b', '.', '..'] ].each do |dir_contents| Dir.stub :entries, dir_contents do assert_equal ['a', 'b'], entries(Dir.tmpdir) end end assert_equal [], entries("/tmp/sprockets/missingdir") end def test_check_absolute_path assert absolute_path?(Dir.pwd) assert absolute_path?("/foo.rb") refute absolute_path?("foo.rb") refute absolute_path?("./foo.rb") refute absolute_path?("../foo.rb") if DOSISH_DRIVE_LETTER assert absolute_path?("A:foo.rb") assert absolute_path?("A:/foo.rb") assert absolute_path?("A:\\foo.rb") end if DOSISH assert absolute_path?("/foo.rb") assert absolute_path?("\\foo.rb") end end def test_check_relative_path assert relative_path?(".") assert relative_path?("..") assert relative_path?("./") assert relative_path?("../") assert relative_path?("./foo.rb") assert relative_path?("../foo.rb") if DOSISH assert relative_path?(".\\") assert relative_path?("..\\") assert relative_path?(".\\foo.rb") assert relative_path?("..\\foo.rb") end refute relative_path?(Dir.pwd) refute relative_path?("/foo.rb") refute relative_path?("foo.rb") refute relative_path?(".foo.rb") refute relative_path?("..foo.rb") end def test_split_subpath_from_root_path path = File.expand_path("../fixtures/default", __FILE__) subpath = File.expand_path("../fixtures/default/application.js", __FILE__) assert_equal "application.js", split_subpath(path, subpath) subpath = File.expand_path("../fixtures/default/application.js", __FILE__) assert_equal "application.js", split_subpath(path + "/", subpath) subpath = File.expand_path("../fixtures/default/app/application.js", __FILE__) assert_equal "app/application.js", split_subpath(path, subpath) assert_nil split_subpath(path, nil) subpath = File.expand_path("../fixtures/default", __FILE__) assert_equal "", split_subpath(path, subpath) subpath = File.expand_path("../fixtures/other/app/application.js", __FILE__) refute split_subpath(path, subpath) end def test_split_paths_root_from_base paths = [File.expand_path("../fixtures/default", __FILE__)] filename = File.expand_path("../fixtures/default/application.js", __FILE__) expected = [paths.first, "application.js"] assert_equal expected, paths_split(paths, filename) filename = File.expand_path("../fixtures/default/app/application.js", __FILE__) expected = [paths.first, "app/application.js"] assert_equal expected, paths_split(paths, filename) filename = File.expand_path("../fixtures/default", __FILE__) expected = [paths.first, ""] assert_equal expected, paths_split(paths, filename) filename = File.expand_path("../fixtures/other/app/application.js", __FILE__) refute paths_split(paths, filename) end def test_path_extensions assert_equal [".txt"], path_extnames("hello.txt") assert_equal [".txt"], path_extnames("sub/hello.txt") assert_equal [".txt"], path_extnames("sub.dir/hello.txt") assert_equal [".js"], path_extnames("jquery.js") assert_equal [".min", ".js"], path_extnames("jquery.min.js") assert_equal [".js", ".erb"], path_extnames("jquery.js.erb") assert_equal [".min", ".js", ".erb"], path_extnames("jquery.min.js.erb") end def test_match_path_extname extensions = { ".txt" => "text/plain" } assert_equal [".txt", "text/plain"], match_path_extname("hello.txt", extensions) assert_equal [".txt", "text/plain"], match_path_extname("sub/hello.txt", extensions) refute match_path_extname("hello.text", extensions) extensions = { ".js" => "application/javascript" } assert_equal [".js", "application/javascript"], match_path_extname("jquery.js", extensions) assert_equal [".js", "application/javascript"], match_path_extname("jquery.min.js", extensions) refute match_path_extname("jquery.js.erb", extensions) refute match_path_extname("jquery.min.js.erb", extensions) extensions = { ".js" => "application/javascript", ".js.erb" => "application/javascript+ruby" } assert_equal [".js", "application/javascript"], match_path_extname("jquery.js", extensions) assert_equal [".js", "application/javascript"], match_path_extname("jquery.min.js", extensions) assert_equal [".js.erb", "application/javascript+ruby"], match_path_extname("jquery.js.erb", extensions) assert_equal [".js.erb", "application/javascript+ruby"], match_path_extname("jquery.min.js.erb", extensions) refute match_path_extname("jquery.min.coffee.erb", extensions) extensions = { ".js.map" => "application/json", ".css.map" => "application/json" } assert_equal [".js.map", "application/json"], match_path_extname("jquery.js.map", extensions) assert_equal [".js.map", "application/json"], match_path_extname("jquery.min.js.map", extensions) assert_equal [".css.map", "application/json"], match_path_extname("jquery-ui.css.map", extensions) assert_equal [".css.map", "application/json"], match_path_extname("jquery-ui.min.css.map", extensions) refute match_path_extname("jquery.map", extensions) refute match_path_extname("jquery.map.js", extensions) refute match_path_extname("jquery.map.css", extensions) extensions = { ".coffee" => "application/coffeescript", ".js" => "application/javascript", ".js.jsx.coffee" => "application/jsx+coffee" } assert_equal [".js.jsx.coffee", "application/jsx+coffee"], match_path_extname("component.js.jsx.coffee", extensions) end def test_find_matching_path_for_extensions dirname = File.expand_path("../fixtures/default", __FILE__) extensions = { ".js" => "application/javascript", ".coffee" => "text/coffeescript" } assert_equal [ ["#{dirname}/application.coffee", "text/coffeescript"] ], find_matching_path_for_extensions(dirname, "application", extensions) extensions = { ".txt" => "text/plain", ".jst.ejs" => "application/ejs" } assert_equal [ ["#{dirname}/hello.jst.ejs", "application/ejs"], ["#{dirname}/hello.txt", "text/plain"] ], find_matching_path_for_extensions(dirname, "hello", extensions) end def test_path_parents root = File.expand_path("../..", __FILE__) assert_kind_of Array, path_parents(File.expand_path(__FILE__)) assert_equal ["#{root}/test", root], path_parents(File.expand_path(__FILE__), root) assert_equal ["#{root}/test", root], path_parents("#{root}/test/fixtures/", root) assert_equal ["#{root}/test/fixtures", "#{root}/test", root], path_parents("#{root}/test/fixtures/default", root) assert_equal ["#{root}/test/fixtures/default", "#{root}/test/fixtures", "#{root}/test", root], path_parents("#{root}/test/fixtures/default/POW.png", root) assert_equal ["#{root}/test/fixtures/default", "#{root}/test/fixtures", "#{root}/test"], path_parents("#{root}/test/fixtures/default/POW.png", "#{root}/test") assert_equal ["#{root}/test/fixtures/default"], path_parents("#{root}/test/fixtures/default/POW.png", "#{root}/test/fixtures/default") end def test_find_upwards root = File.expand_path("../..", __FILE__) assert_equal "#{root}/Gemfile", find_upwards("Gemfile", File.expand_path(__FILE__)) assert_equal "#{root}/Gemfile", find_upwards("Gemfile", "#{root}/test/fixtures/") assert_equal "#{root}/Gemfile", find_upwards("Gemfile", "#{root}/test/fixtures/default/POW.png") assert_equal "#{root}/test/sprockets_test.rb", find_upwards("sprockets_test.rb", "#{root}/test/fixtures/default/POW.png") end FILES_IN_SERVER = Dir["#{File.expand_path("../fixtures/server", __FILE__)}/*"] def test_stat_directory files = stat_directory(File.expand_path("../fixtures/server", __FILE__)).to_a assert_equal FILES_IN_SERVER.size, files.size path, stat = stat_directory(File.expand_path("../fixtures/server", __FILE__)).first assert_equal File.expand_path("../fixtures/server/app", __FILE__), path assert_kind_of File::Stat, stat assert_equal [], stat_directory(File.expand_path("../fixtures/missing", __FILE__)).to_a end def test_stat_tree files = stat_tree(File.expand_path("../fixtures/asset/tree/all", __FILE__)).to_a assert_equal 11, files.size path, stat = files.first assert_equal File.expand_path("../fixtures/asset/tree/all/README.md", __FILE__), path assert_kind_of File::Stat, stat assert_equal [ File.expand_path("../fixtures/asset/tree/all/README.md", __FILE__), File.expand_path("../fixtures/asset/tree/all/b", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c/d.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c/e.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/b.css", __FILE__), File.expand_path("../fixtures/asset/tree/all/b.js.erb", __FILE__), File.expand_path("../fixtures/asset/tree/all/d", __FILE__), File.expand_path("../fixtures/asset/tree/all/d/c.coffee", __FILE__), File.expand_path("../fixtures/asset/tree/all/d/e.js", __FILE__) ], files.map(&:first) assert_equal [], stat_tree("#{File.expand_path("../fixtures", __FILE__)}/missing").to_a end def test_stat_sorted_tree files = stat_sorted_tree(File.expand_path("../fixtures/asset/tree/all", __FILE__)).to_a assert_equal 11, files.size path, stat = files.first assert_equal File.expand_path("../fixtures/asset/tree/all/README.md", __FILE__), path assert_kind_of File::Stat, stat assert_equal [ File.expand_path("../fixtures/asset/tree/all/README.md", __FILE__), File.expand_path("../fixtures/asset/tree/all/b.css", __FILE__), File.expand_path("../fixtures/asset/tree/all/b.js.erb", __FILE__), File.expand_path("../fixtures/asset/tree/all/b", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c/d.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/b/c/e.js", __FILE__), File.expand_path("../fixtures/asset/tree/all/d", __FILE__), File.expand_path("../fixtures/asset/tree/all/d/c.coffee", __FILE__), File.expand_path("../fixtures/asset/tree/all/d/e.js", __FILE__) ], files.map(&:first) assert_equal [], stat_tree(File.expand_path("../fixtures/missing", __FILE__)).to_a end def test_atomic_write_without_errors filename = "atomic.file" begin contents = "Atomic Text" atomic_write(filename) do |file| file.write(contents) assert !File.exist?(filename) end assert File.exist?(filename) assert_equal contents, File.read(filename) ensure File.unlink(filename) rescue nil end end end sprockets-4.2.1/test/test_performance.rb000066400000000000000000000354441447572140400204210ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' $file_stat_calls = nil $file_exist_calls = nil class << File alias_method :original_stat, :stat def stat(filename) if $file_stat_calls $file_stat_calls[filename.to_s] ||= [] $file_stat_calls[filename.to_s] << caller end original_stat(filename) end alias_method :original_exist?, :exist? def exist?(filename) if $file_exist_calls $file_exist_calls[filename.to_s] ||= [] $file_exist_calls[filename.to_s] << caller end original_exist?(filename) end end $dir_entires_calls = nil class << Dir alias_method :original_entries, :entries def entries(dirname, **args) if $dir_entires_calls $dir_entires_calls[dirname.to_s] ||= [] $dir_entires_calls[dirname.to_s] << caller end original_entries(dirname, **args) end end class TestPerformance < Sprockets::TestCase class Cache def initialize @cache = {} end def get(key) $cache_get_calls[key] ||= [] $cache_get_calls[key] << caller @cache[key] end def set(key, value) $cache_set_calls[key] ||= [] $cache_set_calls[key] << caller @cache[key] = value end end def setup @env = new_environment reset_stats! end def teardown $file_stat_calls = nil $dir_entires_calls = nil $processor_calls = nil $bundle_processor_calls = nil $cache_get_calls = nil $cache_set_calls = nil $file_exist_calls = nil end test "simple file" do @env["gallery.js"].to_s assert_no_redundant_stat_calls assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls end test "cached simple file" do @env.cached["gallery.js"].to_s assert_no_redundant_stat_calls assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls end test "file with deps" do @env["mobile.js"].to_s assert_no_redundant_stat_calls assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls end test "cached file with deps" do @env.cached["mobile.js"].to_s assert_no_redundant_stat_calls assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls end test "loading from backend cache" do env1, env2 = new_environment, new_environment cache = Cache.new env1.cache = cache env2.cache = cache env1["mobile.js"] assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls reset_stats! env2["mobile.js"] assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls end test "moving root of project after generation is still freaky fast" do env1 = new_environment env1.cache = Cache.new env1["mobile.js"] assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls Dir.mktmpdir do |dir| Dir.chdir(dir) do `cp -R #{File.join(fixture_path("default"), "*")} .` env2 = new_environment("./default") env2.cache = env1.cache reset_stats! env2["mobile.js"] assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls end end end test "loading from instance cache" do env = @env.cached env["mobile.js"] assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls reset_stats! env["mobile.js"] assert_no_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls end test "loading from cached with backend cache" do env1, env2 = new_environment, new_environment cache = Cache.new env1.cache = cache env2.cache = cache env1.cached["mobile.js"] assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls reset_stats! env2.cached["mobile.js"] assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls end test "rollback version" do env = new_environment env.cache = Cache.new env.version = "1" assert asset = env["mobile.js"] id1 = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls reset_stats! env.version = "2" assert asset = env["mobile.js"] id2 = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls reset_stats! env.version = "1" assert asset = env["mobile.js"] assert_equal id1, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls reset_stats! env.version = "2" assert asset = env["mobile.js"] assert_equal id2, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls end test "rollback path change" do env = new_environment env.cache = Cache.new env.clear_paths env.append_path(fixture_path('default')) assert asset = env["mobile.js"] path1 = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls reset_stats! env.clear_paths env.append_path(fixture_path('asset')) env.append_path(fixture_path('default')) assert asset = env["mobile.js"] path2 = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls reset_stats! env.clear_paths env.append_path(fixture_path('default')) assert asset = env["mobile.js"] assert_equal path1, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls reset_stats! env.clear_paths env.append_path(fixture_path('asset')) env.append_path(fixture_path('default')) assert asset = env["mobile.js"] assert_equal path2, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls end test "rollback file change" do env = new_environment env.cache = Cache.new filename = fixture_path("default/tmp.js") sandbox filename do write(filename, "a;", 1421000000) reset_stats! assert asset = env["tmp.js"] assert_equal "a;\n", asset.source ida = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls write(filename, "b;", 1421000001) reset_stats! assert asset = env["tmp.js"] assert_equal "b;\n", asset.source idb = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls write(filename, "a;", 1421000000) reset_stats! assert asset = env["tmp.js"] assert_equal "a;\n", asset.source assert_equal ida, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls write(filename, "b;", 1421000001) reset_stats! assert asset = env["tmp.js"] assert_equal "b;\n", asset.source assert_equal idb, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls end end test "rollback file dependency change" do env = new_environment env.cache = Cache.new main = fixture_path("default/tmp-main.js") dep = fixture_path("default/tmp-dep.js") sandbox main, dep do write(main, "//= require ./tmp-dep", 1421000000) write(dep, "a;", 1421000000) reset_stats! assert asset = env["tmp-main.js"] assert_equal "a;\n", asset.source ida = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls write(dep, "b;", 1421000001) reset_stats! assert asset = env["tmp-main.js"] assert_equal "b;\n", asset.source idb = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls write(dep, "a;", 1421000000) reset_stats! assert asset = env["tmp-main.js"] assert_equal "a;\n", asset.source assert_equal ida, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls write(dep, "b;", 1421000001) reset_stats! assert asset = env["tmp-main.js"] assert_equal "b;\n", asset.source assert_equal idb, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_cache_set_calls end end test "rollback file dependency add/remove" do env = new_environment env.cache = Cache.new main = fixture_path("default/tmp.js") deps = fixture_path("default/tmp") depa = fixture_path("default/tmp/a.js") depb = fixture_path("default/tmp/b.js") sandbox main, deps, depa, depb do FileUtils.mkdir_p(deps) write(main, "//= require_directory ./tmp", 1421000000) write(depa, "a;", 1421000000) File.utime(1421000000, 1421000000, deps) reset_stats! assert asset = env["tmp.js"] assert_equal "a;\n", asset.source ida = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls write(depb, "b;", 142100001) File.utime(1421000001, 1421000001, deps) reset_stats! assert asset = env["tmp.js"] assert_equal "a;\nb;\n", asset.source idab = asset.id assert_no_redundant_processor_calls assert_no_redundant_bundle_processor_calls assert_no_redundant_cache_set_calls FileUtils.rm(depb) File.utime(1421000000, 1421000000, deps) reset_stats! assert asset = env["tmp.js"] assert_equal "a;\n", asset.source assert_equal ida, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_redundant_cache_set_calls write(depb, "b;", 142100001) File.utime(1421000001, 1421000001, deps) reset_stats! assert asset = env["tmp.js"] assert_equal "a;\nb;\n", asset.source assert_equal idab, asset.id assert_no_redundant_stat_calls assert_no_processor_calls assert_no_bundle_processor_calls assert_no_redundant_cache_get_calls assert_no_redundant_cache_set_calls end end def new_environment(path = fixture_path('default')) Sprockets::Environment.new(".") do |env| env.cache = Cache.new env.append_path(path) env.register_preprocessor 'application/javascript', proc { |input| $processor_calls[input[:filename]] ||= [] $processor_calls[input[:filename]] << caller nil } env.register_bundle_processor 'application/javascript', proc { |input| $bundle_processor_calls[input[:filename]] ||= [] $bundle_processor_calls[input[:filename]] << caller nil } end end def reset_stats! $file_stat_calls = {} $file_exist_calls = {} $dir_entires_calls = {} $processor_calls = {} $bundle_processor_calls = {} $cache_get_calls = {} $cache_set_calls = {} end def assert_no_stat_calls $file_stat_calls.each do |path, callers| assert_equal 0, callers.size, "File.stat(#{path.inspect}) called #{callers.size} times\n\n#{format_callers(callers)}" end $dir_entires_calls.each do |path, callers| assert_equal 0, callers.size, "Dir.entries(#{path.inspect}) called #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_redundant_stat_calls $file_stat_calls.each do |path, callers| assert_equal 1, callers.size, "File.stat(#{path.inspect}) called #{callers.size} times\n\n#{format_callers(callers)}" end $file_exist_calls.each do |path, callers| assert_equal 1, callers.size, "File.exist?(#{path.inspect}) called #{callers.size} times\n\n#{format_callers(callers)}" end $dir_entires_calls.each do |path, callers| assert_equal 1, callers.size, "Dir.entries(#{path.inspect}) called #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_processor_calls $processor_calls.each do |path, callers| assert_equal 0, callers.size, "Processor ran on #{path.inspect} #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_redundant_processor_calls $processor_calls.each do |path, callers| assert_equal 1, callers.size, "Processor ran on #{path.inspect} #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_bundle_processor_calls $bundle_processor_calls.each do |path, callers| assert_equal 0, callers.size, "Bundle Processor ran on #{path.inspect} #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_redundant_bundle_processor_calls $bundle_processor_calls.each do |path, callers| assert_equal 1, callers.size, "Bundle Processor ran on #{path.inspect} #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_redundant_cache_get_calls $cache_get_calls.each do |key, callers| assert_equal 1, callers.size, "cache get #{key.inspect} #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_cache_set_calls $cache_set_calls.each do |key, callers| assert_equal 0, callers.size, "cache set #{key.inspect} #{callers.size} times\n\n#{format_callers(callers)}" end end def assert_no_redundant_cache_set_calls $cache_set_calls.each do |key, callers| assert_equal 1, callers.size, "cache set #{key.inspect} #{callers.size} times\n\n#{format_callers(callers)}" end end def format_callers(callers) callers.map { |c| c.join("\n") }.join("\n\n\n") end end sprockets-4.2.1/test/test_processor_utils.rb000066400000000000000000000245771447572140400213640ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/processor_utils' require 'sprockets/cache' require 'sprockets/coffee_script_processor' require 'sprockets/uglifier_compressor' require 'sprockets_test' class TestCallingProcessors < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env.append_path fixture_path('default') end def test_charset_supersedes_default_when_nil processor = Proc.new do |input| {data: input[:data], charset: nil} end @env.register_processor('image/png' , processor) asset = @env['troll.png'] assert_nil asset.charset ensure @env.unregister_preprocessor('image/png' , processor) end def test_charset_supersedes_default processor = Proc.new do |input| {data: input[:data], charset: 'foo'} end @env.register_processor('image/png' , processor) asset = @env['troll.png'] assert_equal 'foo', asset.charset ensure @env.unregister_preprocessor('image/png' , processor) end end class TestProcessorUtils < Minitest::Test include Sprockets::ProcessorUtils class Processor def initialize(cache_key = nil, &block) @cache_key, @proc = cache_key, block end attr_reader :cache_key def call(*args) @proc.call(*args) end end def setup @env = Sprockets::Environment.new @env.append_path File.expand_path("../fixtures", __FILE__) end def test_call_nothing a = proc {} input = { data: " " }.freeze assert result = call_processor(a, input) assert_equal " ", result[:data] end def test_call_function a = proc { |input| { data: input[:data] + ",a" } } input = { data: " " }.freeze assert result = call_processor(a, input) assert_equal " ,a", result[:data] end def test_call_single_function a = proc { |input| { data: input[:data] + ",a" } } input = { data: " " }.freeze assert result = call_processors([a], input) assert_equal " ,a", result[:data] end def test_call_hash_return a = proc { |input| { data: input[:data] + ",a" } } b = proc { |input| { data: input[:data] + ",b" } } input = { data: " " }.freeze assert result = call_processors([b, a], input) assert_equal " ,a,b", result[:data] end def test_call_string_return a = proc { |input| input[:data] + ",a" } b = proc { |input| input[:data] + ",b" } input = { data: " " }.freeze assert result = call_processors([b, a], input) assert_equal " ,a,b", result[:data] end def test_call_noop_return a = proc { |input| input[:data] + ",a" } b = proc { |input| nil } input = { data: " " }.freeze assert result = call_processors([a, b], input) assert_equal " ,a", result[:data] assert result = call_processors([b, a], input) assert_equal " ,a", result[:data] end def test_call_metadata a = proc { |input| { a: true } } b = proc { |input| { b: true } } input = {} assert result = call_processors([a, b], input) assert result[:a] assert result[:b] end def test_call_metadata_merge a = proc { |input| { trace: input[:metadata][:trace] + [:a] } } b = proc { |input| { trace: input[:metadata][:trace] + [:b] } } input = { metadata: { trace: [] }.freeze }.freeze assert result = call_processors([b, a], input) assert_equal [:a, :b], result[:trace] end def test_compose_nothing a = compose_processors() input = { data: " " }.freeze assert result = a.call(input) assert_equal " ", result[:data] end def test_compose_single_function a = proc { |input| { data: input[:data] + ",a" } } b = compose_processors(a) input = { data: " " }.freeze assert result = b.call(input) assert_equal " ,a", result[:data] end def test_compose_hash_return a = proc { |input| { data: input[:data] + ",a" } } b = proc { |input| { data: input[:data] + ",b" } } c = compose_processors(b, a) input = { data: " " }.freeze assert result = c.call(input) assert_equal " ,a,b", result[:data] end def test_compose_string_return a = proc { |input| input[:data] + ",a" } b = proc { |input| input[:data] + ",b" } c = compose_processors(b, a) input = { data: " " }.freeze assert result = c.call(input) assert_equal " ,a,b", result[:data] end def test_compose_noop_return a = proc { |input| input[:data] + ",a" } b = proc { |input| nil } c = compose_processors(a, b) d = compose_processors(b, a) input = { data: " " }.freeze assert result = c.call(input) assert_equal " ,a", result[:data] assert result = d.call(input) assert_equal " ,a", result[:data] end def test_compose_metadata a = proc { |input| { a: true } } b = proc { |input| { b: true } } c = compose_processors(a, b) input = {} assert result = c.call(input) assert result[:a] assert result[:b] end def test_compose_metadata_merge a = proc { |input| { trace: input[:metadata][:trace] + [:a] } } b = proc { |input| { trace: input[:metadata][:trace] + [:b] } } c = compose_processors(b, a) input = { metadata: { trace: [] }.freeze }.freeze assert result = c.call(input) assert_equal [:a, :b], result[:trace] end def test_multiple_functional_compose a = proc { |input| { data: input[:data] + ",a" } } b = proc { |input| { data: input[:data] + ",b" } } c = proc { |input| { data: input[:data] + ",c" } } d = proc { |input| { data: input[:data] + ",d" } } e = compose_processors(d, compose_processors(c, compose_processors(b, compose_processors(a)))) input = { data: " " }.freeze assert result = e.call(input) assert_equal " ,a,b,c,d", result[:data] end def test_multiple_functional_compose_metadata a = proc { |input| { trace: input[:metadata][:trace] + [:a] } } b = proc { |input| { trace: input[:metadata][:trace] + [:b] } } c = proc { |input| { trace: input[:metadata][:trace] + [:c] } } d = proc { |input| { trace: input[:metadata][:trace] + [:d] } } e = compose_processors(d, compose_processors(c, compose_processors(b, compose_processors(a)))) input = { metadata: { trace: [].freeze }.freeze }.freeze assert result = e.call(input) assert_equal [:a, :b, :c, :d], result[:trace] end def test_multiple_array_compose a = proc { |input| { data: input[:data] + ",a" } } b = proc { |input| { data: input[:data] + ",b" } } c = proc { |input| { data: input[:data] + ",c" } } d = proc { |input| { data: input[:data] + ",d" } } e = compose_processors(d, c, b, a) input = { data: " " } assert result = e.call(input) assert_equal " ,a,b,c,d", result[:data] end def test_multiple_array_compose_metadata a = proc { |input| { trace: input[:metadata][:trace] + [:a] } } b = proc { |input| { trace: input[:metadata][:trace] + [:b] } } c = proc { |input| { trace: input[:metadata][:trace] + [:c] } } d = proc { |input| { trace: input[:metadata][:trace] + [:d] } } e = compose_processors(d, c, b, a) input = { metadata: { trace: [].freeze }.freeze }.freeze assert result = e.call(input) assert_equal [:a, :b, :c, :d], result[:trace] end def test_compose_coffee_and_uglifier processor = compose_processors(Sprockets::UglifierCompressor, Sprockets::CoffeeScriptProcessor) input = { environment: @env, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/compose.coffee", __FILE__), content_type: 'application/javascript', data: "self.square = (n) -> n * n", cache: Sprockets::Cache.new }.freeze assert result = processor.call(input) assert_match "self.square=function", result[:data] end def test_bad_processor_return_type a = proc { |input| Object.new } b = compose_processors(a) input = { data: " " }.freeze assert_raises(TypeError) do b.call(input) end end def test_compose_class_processors a = Processor.new { |input| { data: input[:data] + ",a" } } b = Processor.new { |input| { data: input[:data] + ",b" } } c = compose_processors(b, a) input = { data: " " }.freeze assert result = c.call(input) assert_equal " ,a,b", result[:data] end def test_compose_processors_cache_keys a = Processor.new("a") b = Processor.new("b") c = compose_processors(b, a) assert_equal "a", a.cache_key assert_equal "b", b.cache_key assert_equal ["b", "a"], c.cache_key end def test_compose_processors_missing_cache_keys a = Processor.new("a") b = proc {} c = Processor.new("c") e = compose_processors(c, b, a) assert_equal "a", a.cache_key assert_equal "c", c.cache_key assert_equal ["c", nil, "a"], e.cache_key end def test_multiple_array_compose_cache_keys a = Processor.new("a") b = Processor.new("b") c = Processor.new("c") d = Processor.new("d") e = compose_processors(d, c, b, a) assert_equal "a", a.cache_key assert_equal "b", b.cache_key assert_equal "c", c.cache_key assert_equal "d", d.cache_key assert_equal ["d", "c", "b", "a"], e.cache_key end def test_multiple_functional_compose_cache_keys a = Processor.new("a") b = Processor.new("b") c = Processor.new("c") d = Processor.new("d") e = compose_processors(d, compose_processors(c, compose_processors(b, compose_processors(a)))) assert_equal "a", a.cache_key assert_equal "b", b.cache_key assert_equal "c", c.cache_key assert_equal "d", d.cache_key assert_equal ["d", ["c", ["b", ["a"]]]], e.cache_key end def test_validate_processor_result validate_processor_result!({data: "hello"}) validate_processor_result!({data: "hello", foo: nil}) validate_processor_result!({data: "hello", foo: 1}) validate_processor_result!({data: "hello", foo: "bye"}) validate_processor_result!({data: "hello", foo: :bye}) validate_processor_result!({data: "hello", foo: [1, 2, 3]}) validate_processor_result!({data: "hello", foo: Set.new([1, 2, 3])}) validate_processor_result!({data: "hello", foo: {bar: 1}}) my_string = Class.new(String) assert_raises(TypeError) { validate_processor_result!(nil) } assert_raises(TypeError) { validate_processor_result!({}) } assert_raises(TypeError) { validate_processor_result!({data: 123}) } assert_raises(TypeError) { validate_processor_result!({data: "hello", "foo" => 1}) } assert_raises(TypeError) { validate_processor_result!({data: my_string.new("hello")}) } end end sprockets-4.2.1/test/test_rake_task.rb000066400000000000000000000066531447572140400200640ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'rake/sprocketstask' require 'rake' class TestRakeTask < Sprockets::TestCase def setup @rake = Rake::Application.new Rake.application = @rake @env = Sprockets::Environment.new(".") do |env| env.append_path(fixture_path('default')) end @dir = Dir.mktmpdir FileUtils.mkdir_p(@dir) @manifest_custom_dir = Sprockets::Manifest.new(@env, @dir) @manifest_custom_path = Sprockets::Manifest.new(@env, @dir, File.join(@dir, 'manifest.json')) Rake::SprocketsTask.new do |t| t.environment = @env t.output = @dir t.assets = ['application.js'] t.log_level = :fatal end end def teardown Rake.application = nil FileUtils.remove_entry_secure(@dir) if File.exist?(@dir) assert Dir["#{@dir}/*"].empty? end test "assets" do digest_path = @env['application.js'].digest_path assert !File.exist?("#{@dir}/#{digest_path}") @rake[:assets].invoke assert Dir["#{@dir}/.sprockets-manifest-*.json"].first assert File.exist?("#{@dir}/#{digest_path}") end test "clobber" do digest_path = @env['application.js'].digest_path @rake[:assets].invoke assert File.exist?("#{@dir}/#{digest_path}") # set a cache key result = @env.cache.set('foo', 'bar') assert_equal result, @env.cache.get('foo') @rake[:clobber_assets].invoke assert !File.exist?("#{@dir}/#{digest_path}") # verify the cache key was cleared assert_nil @env.cache.get('foo') end test "clean" do digest_path = @env['application.js'].digest_path filename = fixture_path('default/application.coffee') sandbox filename do @rake[:assets].invoke assert File.exist?("#{@dir}/#{digest_path}") File.open(filename, 'w') { |f| f.write "change;" } changed_digest_path = @env['application.js'].digest_path @rake[:assets].invoke @rake[:clean_assets].invoke(1, 0) assert File.exist?("#{@dir}/#{digest_path}") refute File.exist?("#{@dir}/#{changed_digest_path}") end end test "custom manifest directory" do Rake::SprocketsTask.new do |t| t.environment = nil t.manifest = @manifest_custom_dir t.assets = ['application.js'] t.log_level = :fatal end digest_path = @env['application.js'].digest_path assert !File.exist?("#{@dir}/#{digest_path}") @rake[:assets].invoke assert Dir["#{@dir}/.sprockets-manifest-*.json"].first assert File.exist?("#{@dir}/#{digest_path}") end test "custom manifest path" do Rake::SprocketsTask.new do |t| t.environment = nil t.manifest = @manifest_custom_path t.assets = ['application.js'] t.log_level = :fatal end digest_path = @env['application.js'].digest_path assert !File.exist?("#{@dir}/#{digest_path}") @rake[:assets].invoke assert Dir["#{@dir}/manifest.json"].first assert File.exist?("#{@dir}/#{digest_path}") end test "lazy custom manifest" do Rake::SprocketsTask.new do |t| t.environment = nil t.manifest = lambda { @manifest_custom_dir } t.assets = ['application.js'] t.log_level = :fatal end digest_path = @env['application.js'].digest_path assert !File.exist?("#{@dir}/#{digest_path}") @rake[:assets].invoke assert Dir["#{@dir}/.sprockets-manifest-*.json"].first assert File.exist?("#{@dir}/#{digest_path}") end end sprockets-4.2.1/test/test_require.rb000066400000000000000000000010571447572140400175650ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' class TestRequire < Minitest::Test parallelize_me! ROOT = File.expand_path("../..", __FILE__) Dir["#{ROOT}/lib/**/*.rb"].each do |fn| basename = File.basename(fn) next if basename == "version.rb" next if RUBY_PLATFORM.include?('java') && ['zopfli.rb', 'jsminc.rb', 'sassc.rb'].include?(basename) define_method "test_require_individual_library_files: #{fn}" do system "ruby", "-I#{ROOT}/lib", fn assert $?.success?, "Failed to load #{fn.inspect}" end end end sprockets-4.2.1/test/test_resolve.rb000066400000000000000000000322451447572140400175730ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' class TestResolve < Sprockets::TestCase def setup @env = Sprockets::Environment.new(".") end test "resolve in default environment" do @env.append_path(fixture_path('default')) assert_equal "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript", resolve("gallery.js") assert_equal "file://#{fixture_path_for_uri('default/coffee/foo.coffee')}?type=application/javascript", resolve("coffee/foo.js") assert_equal "file://#{fixture_path_for_uri('default/jquery.tmpl.min.js')}?type=application/javascript", resolve("jquery.tmpl.min") assert_equal "file://#{fixture_path_for_uri('default/jquery.tmpl.min.js')}?type=application/javascript", resolve("jquery.tmpl.min.js") assert_equal "file://#{fixture_path_for_uri('default/manifest.js.yml')}?type=text/yaml", resolve('manifest.js.yml') refute resolve("null") end test "resolve index assets" do @env.append_path(fixture_path('index-assets')) assert_equal "file://#{fixture_path_for_uri('index-assets/bar/index.js')}?type=application/javascript", resolve("bar/index.js") assert_equal "file://#{fixture_path_for_uri('index-assets/bar/index.js')}?type=application/javascript&index_alias=#{@env.compress_from_root(fixture_path('index-assets/bar.js'))}", resolve("bar.js") assert_equal "file://#{fixture_path_for_uri('index-assets/index/foo/index.js')}?type=application/javascript", resolve("index/foo/index.js") assert_equal "file://#{fixture_path_for_uri('index-assets/index/foo/index.js')}?type=application/javascript&index_alias=#{@env.compress_from_root(fixture_path('index-assets/index/foo.js'))}", resolve("index/foo.js") assert_equal "file://#{fixture_path_for_uri('index-assets/baz/index.js.erb')}?type=application/javascript", resolve("baz/index.js") assert_equal "file://#{fixture_path_for_uri('index-assets/baz/index.js.erb')}?type=application/javascript&index_alias=#{@env.compress_from_root(fixture_path('index-assets/baz.js.erb'))}", resolve("baz.js") end test "resolve accept type list before paths" do @env.append_path(fixture_path('resolve/javascripts')) @env.append_path(fixture_path('resolve/stylesheets')) foo_js_uri = "file://#{fixture_path_for_uri('resolve/javascripts/foo.js')}?type=application/javascript" foo_css_uri = "file://#{fixture_path_for_uri('resolve/stylesheets/foo.css')}?type=text/css" assert_equal foo_js_uri, resolve('foo', accept: 'application/javascript') assert_equal foo_css_uri, resolve('foo', accept: 'text/css') assert_equal foo_js_uri, resolve('foo', accept: 'application/javascript, text/css') assert_equal foo_js_uri, resolve('foo', accept: 'text/css, application/javascript') assert_equal foo_js_uri, resolve('foo', accept: 'application/javascript; q=0.8, text/css') assert_equal foo_js_uri, resolve('foo', accept: 'text/css; q=0.8, application/javascript') assert_equal foo_js_uri, resolve('foo', accept: '*/*; q=0.8, application/javascript') assert_equal foo_js_uri, resolve('foo', accept: '*/*; q=0.8, text/css') end test "resolve under load path" do @env.append_path(scripts = fixture_path('resolve/javascripts')) @env.append_path(styles = fixture_path('resolve/stylesheets')) foo_js_uri = "file://#{fixture_path_for_uri('resolve/javascripts/foo.js')}?type=application/javascript" foo_css_uri = "file://#{fixture_path_for_uri('resolve/stylesheets/foo.css')}?type=text/css" assert_equal foo_js_uri, resolve('foo.js', load_paths: [scripts]) assert_equal foo_css_uri, resolve('foo.css', load_paths: [styles]) refute resolve('foo.js', load_paths: [styles]) refute resolve('foo.css', load_paths: [scripts]) end test "resolve absolute" do @env.append_path(fixture_path('default')) gallery_js_uri = "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript" assert_equal gallery_js_uri, resolve(fixture_path('default/gallery.js')) assert_equal gallery_js_uri, resolve(fixture_path('default/app/../gallery.js')) assert_equal gallery_js_uri, resolve(fixture_path('default/gallery.js'), accept: 'application/javascript') assert_equal "file://#{fixture_path_for_uri('default/blank.gif')}?type=image/gif", resolve(fixture_path('default/blank.gif')) assert_equal "file://#{fixture_path_for_uri('default/hello.txt')}?type=text/plain", resolve(fixture_path('default/hello.txt')) assert_equal "file://#{fixture_path_for_uri('default/README.md')}", resolve(fixture_path('default/README.md')) refute resolve(fixture_path('asset/POW.png')) refute resolve(fixture_path('default/missing')) refute resolve(fixture_path('default/gallery.js'), accept: 'text/css') end test "resolve absolute identity" do @env.append_path(fixture_path('default')) @env.stat_tree(fixture_path('default')).each do |expected_path, stat| next unless stat.file? actual_path, _ = @env.parse_asset_uri(resolve(expected_path)) assert_equal expected_path, actual_path end end test "resolve relative" do @env.append_path(fixture_path('default')) gallery_js_uri = "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript" assert_equal gallery_js_uri, resolve("./gallery.js", base_path: fixture_path('default')) assert_equal gallery_js_uri, resolve("../gallery.js", base_path: fixture_path('default/app')) assert_equal gallery_js_uri, resolve("./../gallery.js", base_path: fixture_path('default/app')) assert_equal gallery_js_uri, resolve("../../gallery.js", base_path: fixture_path('default/vendor/gems')) refute resolve("./missing.js", base_path: fixture_path('default')) refute resolve("../asset/application.js", base_path: fixture_path('default')) refute resolve("../default/gallery.js", base_path: fixture_path('app')) end test "resolve extension before accept type" do @env.append_path(fixture_path('resolve/javascripts')) @env.append_path(fixture_path('resolve/stylesheets')) foo_js_uri = "file://#{fixture_path_for_uri('resolve/javascripts/foo.js')}?type=application/javascript" foo_css_uri = "file://#{fixture_path_for_uri('resolve/stylesheets/foo.css')}?type=text/css" assert_equal foo_js_uri, resolve('foo.js', accept: 'application/javascript') assert_equal foo_css_uri, resolve('foo.css', accept: 'text/css') refute resolve('foo.js', accept: 'text/css') refute resolve('foo.css', accept: 'application/javascript') assert_equal foo_js_uri, resolve('foo.js', accept: '*/*') assert_equal foo_css_uri, resolve('foo.css', accept: '*/*') assert_equal foo_js_uri, resolve('foo.js', accept: 'text/css, */*') assert_equal foo_css_uri, resolve('foo.css', accept: 'application/javascript, */*') end test "resolve accept type quality in paths" do @env.append_path(fixture_path('resolve/javascripts')) bar_js_uri = "file://#{fixture_path_for_uri('resolve/javascripts/bar.js')}?type=application/javascript" bar_css_uri = "file://#{fixture_path_for_uri('resolve/javascripts/bar.css')}?type=text/css" assert_equal bar_js_uri, resolve('bar', accept: 'application/javascript') assert_equal bar_css_uri, resolve('bar', accept: 'text/css') assert_equal bar_js_uri, resolve('bar', accept: 'application/javascript, text/css') assert_equal bar_css_uri, resolve('bar', accept: 'text/css, application/javascript') assert_equal bar_css_uri, resolve('bar', accept: 'application/javascript; q=0.8, text/css') assert_equal bar_js_uri, resolve('bar', accept: 'text/css; q=0.8, application/javascript') assert_equal bar_js_uri, resolve('bar', accept: '*/*; q=0.8, application/javascript') assert_equal bar_css_uri, resolve('bar', accept: '*/*; q=0.8, text/css') end test "resolve with dependencies" do @env.append_path(fixture_path('default')) uri, deps = @env.resolve("gallery.js") assert_equal "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript", uri assert_includes deps, "file-digest://#{fixture_path_for_uri('default/gallery.js')}" assert_includes deps, "file-digest://#{fixture_path_for_uri('default')}" uri, deps = @env.resolve("coffee/foo.js") assert_equal "file://#{fixture_path_for_uri('default/coffee/foo.coffee')}?type=application/javascript", uri assert_includes deps, "file-digest://#{fixture_path_for_uri('default/coffee/foo.coffee')}" assert_includes deps, "file-digest://#{fixture_path_for_uri('default/coffee')}" uri, deps = @env.resolve("manifest.js.yml") assert_equal "file://#{fixture_path_for_uri('default/manifest.js.yml')}?type=text/yaml", uri assert_includes deps, "file-digest://#{fixture_path_for_uri('default/manifest.js.yml')}" assert_includes deps, "file-digest://#{fixture_path_for_uri('default')}" uri, deps = @env.resolve("gallery", accept: 'application/javascript') assert_equal "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript", uri assert_includes deps, "file-digest://#{fixture_path_for_uri('default/gallery.js')}" assert_includes deps, "file-digest://#{fixture_path_for_uri('default')}" end test "resolve under load path with dependencies" do @env.append_path(scripts = fixture_path('resolve/javascripts')) @env.append_path(styles = fixture_path('resolve/stylesheets')) uri, deps = @env.resolve('foo.js', load_paths: [scripts]) assert_equal "file://#{fixture_path_for_uri('resolve/javascripts/foo.js')}?type=application/javascript", uri assert_includes deps, "file-digest://#{fixture_path_for_uri('resolve/javascripts/foo.js')}" assert_includes deps, "file-digest://#{fixture_path_for_uri('resolve/javascripts')}" uri, deps = @env.resolve('foo.css', load_paths: [styles]) assert_equal "file://#{fixture_path_for_uri('resolve/stylesheets/foo.css')}?type=text/css", uri assert_includes deps, "file-digest://#{fixture_path_for_uri('resolve/stylesheets/foo.css')}" assert_includes deps, "file-digest://#{fixture_path_for_uri('resolve/stylesheets')}" uri, deps = @env.resolve('foo.js', load_paths: [styles]) refute uri assert_includes deps, "file-digest://#{fixture_path_for_uri('resolve/stylesheets')}" uri, deps = @env.resolve('foo.css', load_paths: [scripts]) refute uri assert_includes deps, "file-digest://#{fixture_path_for_uri('resolve/javascripts')}" end test "resolve absolute with dependencies" do @env.append_path(fixture_path('default')) uri, deps = @env.resolve(fixture_path('default/gallery.js')) assert_equal "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript", uri assert_includes deps, "file-digest://#{fixture_path_for_uri('default/gallery.js')}" end test "resolve uri identity with dependencies" do @env.append_path(fixture_path('default')) uri1 = "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript" uri2, deps = @env.resolve(uri1) assert_equal uri1, uri2 assert_includes deps, "file-digest://#{fixture_path_for_uri('default/gallery.js')}" end test "resolve with pipeline" do @env.append_path(fixture_path('default')) assert_equal "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript&pipeline=source", resolve("gallery.js", pipeline: :source) assert_equal "file://#{fixture_path_for_uri('default/coffee/foo.coffee')}?type=application/javascript&pipeline=source", resolve("coffee/foo.js", pipeline: :source) assert_equal "file://#{fixture_path_for_uri('default/jquery.tmpl.min.js')}?type=application/javascript&pipeline=source", resolve("jquery.tmpl.min", pipeline: :source) assert_equal "file://#{fixture_path_for_uri('default/jquery.tmpl.min.js')}?type=application/javascript&pipeline=source", resolve("jquery.tmpl.min.js", pipeline: :source) assert_equal "file://#{fixture_path_for_uri('default/manifest.js.yml')}?type=text/yaml&pipeline=source", resolve('manifest.js.yml', pipeline: :source) refute resolve("null", pipeline: :source) assert_equal "file://#{fixture_path_for_uri('default/gallery.js')}?type=application/javascript&pipeline=source", resolve("gallery.source.js") assert_equal "file://#{fixture_path_for_uri('default/coffee/foo.coffee')}?type=application/javascript&pipeline=source", resolve("coffee/foo.source.js") assert_equal "file://#{fixture_path_for_uri('default/jquery.tmpl.min.js')}?type=application/javascript&pipeline=source", resolve("jquery.tmpl.min.source") assert_equal "file://#{fixture_path_for_uri('default/jquery.tmpl.min.js')}?type=application/javascript&pipeline=source", resolve("jquery.tmpl.min.source.js") assert_equal "file://#{fixture_path_for_uri('default/manifest.js.yml')}?type=text/yaml&pipeline=source", resolve('manifest.js.source.yml') end test "adds paths to exceptions" do random_path = SecureRandom.hex @env.append_path(random_path) error = assert_raises(Sprockets::FileNotFound) do uri, _ = @env.resolve!("thisfiledoesnotexistandshouldraiseerrors", **{}) uri end assert_match(/#{ random_path }/, error.message) end def resolve(path, **options) uri, _ = @env.resolve(path, **options) uri end end sprockets-4.2.1/test/test_sass.rb000066400000000000000000000054341447572140400170650ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'shared_sass_tests' silence_warnings do require 'sass' end require 'sprockets/sass_processor' require 'sprockets/sass_compressor' class TestBaseSass < Sprockets::TestCase CACHE_PATH = File.expand_path("../../.sass-cache", __FILE__) def sass ::Sass end def sass_functions ::Sass::Script::Functions end def sass_engine ::Sass::Engine end def compressor Sprockets::SassCompressor end def teardown refute ::Sass::Script::Functions.instance_methods.include?(:asset_path) FileUtils.rm_r(CACHE_PATH) if File.exist?(CACHE_PATH) assert !File.exist?(CACHE_PATH) end end class TestNoSassFunctionSass < TestBaseSass module ::Sass::Script::Functions def javascript_path(path) ::Sass::Script::String.new("/js/#{path.value}", :string) end module Compass def stylesheet_path(path) ::Sass::Script::String.new("/css/#{path.value}", :string) end end include Compass end include SharedSassTestNoFunction end class TestSprocketsSass < TestBaseSass def setup super @env = Sprockets::Environment.new(".") do |env| env.cache = {} env.append_path(fixture_path('.')) env.append_path(fixture_path('compass')) env.append_path(fixture_path('octicons')) env.register_transformer 'text/sass', 'text/css', Sprockets::SassProcessor.new env.register_transformer 'text/scss', 'text/css', Sprockets::ScssProcessor.new end end def teardown assert !File.exist?(CACHE_PATH) end def render(path) path = fixture_path(path) silence_warnings do @env.find_asset(path, accept: 'text/css').to_s end end test "raise sass error with line number" do begin ::Sass::Util.silence_sass_warnings do render('sass/error.sass') end flunk rescue Sass::SyntaxError => error assert error.message.include?("invalid") trace = error.backtrace[0] assert trace.include?("error.sass") assert trace.include?(":5") end end test "track sass dependencies metadata" do asset = nil silence_warnings do asset = @env.find_asset('sass/import_partial.css') end assert asset assert_equal [ fixture_path('sass/_rounded.scss'), fixture_path('sass/import_partial.sass') ], asset.metadata[:sass_dependencies].to_a.sort end include SharedSassTestSprockets end class TestSassCompressor < TestBaseSass include SharedSassTestCompressor end class TestSassFunctions < TestSprocketsSass def setup super define_asset_path end def define_asset_path @env.context_class.class_eval do def asset_path(path, options = {}) link_asset(path) "/#{path}" end end end include SharedSassTestFunctions end sprockets-4.2.1/test/test_sassc.rb000066400000000000000000000056151447572140400172310ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'shared_sass_tests' silence_warnings do require 'sassc' end require 'sprockets/sassc_processor' require 'sprockets/sassc_compressor' class TestBaseSassc < Sprockets::TestCase CACHE_PATH = File.expand_path("../../.sass-cache", __FILE__) def sass ::SassC end def sass_functions ::SassC::Script::Functions end def sass_engine ::SassC::Engine end def syntax_error SassC::SyntaxError end def compressor Sprockets::SasscCompressor end def teardown refute ::SassC::Script::Functions.instance_methods.include?(:asset_path) FileUtils.rm_r(CACHE_PATH) if File.exist?(CACHE_PATH) assert !File.exist?(CACHE_PATH) end end class TestNoSassFunctionSassC < TestBaseSassc module ::SassC::Script::Functions def javascript_path(path) ::SassC::Script::Value::String.new("/js/#{path.value}", :string) end module Compass def stylesheet_path(path) ::SassC::Script::Value::String.new("/css/#{path.value}", :string) end end include Compass end include SharedSassTestNoFunction end class TestSprocketsSassc < TestBaseSassc def setup super @env = Sprockets::Environment.new(".") do |env| env.cache = {} env.append_path(fixture_path('.')) env.append_path(fixture_path('compass')) env.append_path(fixture_path('octicons')) env.register_transformer 'text/sass', 'text/css', Sprockets::SasscProcessor.new env.register_transformer 'text/scss', 'text/css', Sprockets::ScsscProcessor.new end end def teardown assert !File.exist?(CACHE_PATH) end def render(path) path = fixture_path(path) silence_warnings do @env.find_asset(path, accept: 'text/css').to_s end end test "raise sass error with line number" do begin render('sass/error.sass') flunk rescue SassC::SyntaxError => error # this is not exactly consistent with ruby sass assert error.message.include?("invalid") assert error.message.include?("error.sass") assert error.message.include?("line 5") end end test "track sass dependencies metadata" do skip "not consistent with ruby sass" asset = nil silence_warnings do asset = @env.find_asset('sass/import_partial.css') end assert asset assert_equal [ fixture_path('sass/_rounded.scss'), fixture_path('sass/import_partial.sass') ], asset.metadata[:sass_dependencies].to_a.sort end include SharedSassTestSprockets end class TestSasscCompressor < TestBaseSassc include SharedSassTestCompressor end class TestSasscFunctions < TestSprocketsSassc def setup super define_asset_path end def define_asset_path @env.context_class.class_eval do def asset_path(path, options = {}) link_asset(path) "/#{path}" end end end include SharedSassTestFunctions end sprockets-4.2.1/test/test_server.rb000066400000000000000000000274521447572140400174260ustar00rootroot00000000000000# -*- coding: utf-8 -*- # frozen_string_literal: true require 'sprockets_test' require 'rack/builder' require 'rack/test' class TestServer < Sprockets::TestCase include Rack::Test::Methods def setup @env = Sprockets::Environment.new @env.append_path(fixture_path("server/app/javascripts")) @env.append_path(fixture_path("server/app/images")) @env.append_path(fixture_path("server/vendor/javascripts")) @env.append_path(fixture_path("server/vendor/stylesheets")) end def default_app env = @env Rack::Builder.new do map "/assets" do run env end map "/cached/javascripts" do run env.cached end end end def app @app ||= Rack::Lint.new(default_app) end test "serve single source file" do get "/assets/foo.js" assert_equal 200, last_response.status assert_equal "9", last_response.headers['content-length'] assert_equal "Accept-Encoding", last_response.headers['vary'] assert_equal "var foo;\n", last_response.body end test "serve single self file" do get "/assets/foo.self.js" assert_equal 200, last_response.status assert_equal "9", last_response.headers['content-length'] assert_equal "var foo;\n", last_response.body end test "serve single source file from cached environment" do get "/cached/javascripts/foo.js" assert_equal "var foo;\n", last_response.body end test "serve source with dependencies" do get "/assets/application.js" assert_equal "var foo;\n\n(function() {\n application.boot();\n})();\n", last_response.body end test "serve source file self that has dependencies" do get "/assets/application.self.js" assert_equal 200, last_response.status assert_equal "\n(function() {\n application.boot();\n})();\n", last_response.body assert_equal "43", last_response.headers['content-length'] end test "serve source with content type headers" do get "/assets/application.js" assert_equal "application/javascript", last_response.headers['content-type'] get "/assets/bootstrap.css" assert_equal "text/css; charset=utf-8", last_response.headers['content-type'] end test "serve source with etag headers" do digest = @env['application.js'].etag get "/assets/application.js" assert_equal "\"#{digest}\"", last_response.headers['etag'] end test "not modified partial response when if-none-match etags match" do get "/assets/application.js" assert_equal 200, last_response.status etag, cache_control, expires, vary = last_response.headers.values_at( 'etag', 'cache-control', 'expires', 'vary' ) assert_nil expires get "/assets/application.js", {}, 'HTTP_IF_NONE_MATCH' => etag assert_equal 304, last_response.status # Allow 304 headers assert_equal cache_control, last_response.headers['cache-control'] assert_equal etag, last_response.headers['etag'] assert_nil last_response.headers['Expires'] assert_equal vary, last_response.headers['vary'] # Disallowed 304 headers refute last_response.headers['content-type'] refute last_response.headers['content-length'] refute last_response.headers['content-encoding'] end test "response when if-none-match etags don't match" do get "/assets/application.js", {}, 'HTTP_IF_NONE_MATCH' => "nope" assert_equal 200, last_response.status assert_equal '"b452c9ae1d5c8d9246653e0d93bc83abce0ee09ef725c0f0a29a41269c217b83"', last_response.headers['etag'] assert_equal '52', last_response.headers['content-length'] end test "not modified partial response with fingerprint and if-none-match etags match" do get "/assets/application.js" assert_equal 200, last_response.status etag = last_response.headers['etag'] digest = etag[/"(.+)"/, 1] get "/assets/application-#{digest}.js", {}, 'HTTP_IF_NONE_MATCH' => etag assert_equal 304, last_response.status end test "200 response for prehashed asset with etag digest by sprockets" do get "/assets/prehashed-988881adc9fc3655077dc2d4d757d480b5ea0e11.js" assert_equal 200, last_response.status etag = last_response.headers['etag'] digest = etag[/"(.+)"/, 1] assert_equal 'edabfd0f1ac5fcdae82cc7d92d1c52abb671797a3948fa9040aec1db8e61c327', digest end test "200 response for prehashed esbuild asset with etag digest by sprockets" do get "/assets/esbuild-TQDC3LZV.digested.js" assert_equal 200, last_response.status etag = last_response.headers['etag'] digest = etag[/"(.+)"/, 1] assert_equal '3ebac3dc00b383de6cbdfa470d105f5a9f22708fb72c63db917ad37f288ac708', digest end test "ok response with fingerprint and if-nonematch etags don't match" do get "/assets/application.js" assert_equal 200, last_response.status etag = last_response.headers['etag'] digest = etag[/"(.+)"/, 1] get "/assets/application-#{digest}.js", {}, 'HTTP_IF_NONE_MATCH' => "nope" assert_equal 200, last_response.status end test "not found with if-none-match" do get "/assets/missing.js", {}, 'HTTP_IF_NONE_MATCH' => '"000"' assert_equal 404, last_response.status end test "not found fingerprint with if-none-match" do get "/assets/missing-b452c9ae1d5c8d9246653e0d93bc83abce0ee09ef725c0f0a29a41269c217b83.js", {}, 'HTTP_IF_NONE_MATCH' => '"b452c9ae1d5c8d9246653e0d93bc83abce0ee09ef725c0f0a29a41269c217b83"' assert_equal 404, last_response.status end test "not found with response with incorrect fingerprint and matching if-none-match etags" do get "/assets/application.js" assert_equal 200, last_response.status etag = last_response.headers['etag'] get "/assets/application-0000000000000000000000000000000000000000.js", {}, 'HTTP_IF_NONE_MATCH' => etag assert_equal 404, last_response.status end test "ok partial response when if-match etags match" do get "/assets/application.js" assert_equal 200, last_response.status etag = last_response.headers['etag'] get "/assets/application.js", {}, 'HTTP_IF_MATCH' => etag assert_equal 200, last_response.status assert_equal '"b452c9ae1d5c8d9246653e0d93bc83abce0ee09ef725c0f0a29a41269c217b83"', last_response.headers['etag'] assert_equal '52', last_response.headers['content-length'] end test "precondition failed with if-match is a mismatch" do get "/assets/application.js", {}, 'HTTP_IF_MATCH' => '"000"' assert_equal 412, last_response.status refute last_response.headers['etag'] end test "not found with if-match" do get "/assets/missing.js", {}, 'HTTP_IF_MATCH' => '"000"' assert_equal 404, last_response.status end test "if sources didnt change the server shouldnt rebundle" do get "/assets/application.js" asset_before = @env["application.js"] assert asset_before get "/assets/application.js" asset_after = @env["application.js"] assert asset_after assert asset_before.eql?(asset_after) end test "fingerprint digest sets expiration to the future" do get "/assets/application.js" digest = last_response.headers['etag'][/"(.+)"/, 1] get "/assets/application-#{digest}.js" assert_equal 200, last_response.status assert_match %r{max-age}, last_response.headers['cache-control'] assert_match %r{immutable}, last_response.headers['cache-control'] end test "fingerprint digest of file self" do get "/assets/application.self.js" digest = last_response.headers['etag'][/"(.+)"/, 1] get "/assets/application.self-#{digest}.js" assert_equal 200, last_response.status assert_equal "\n(function() {\n application.boot();\n})();\n", last_response.body assert_equal "43", last_response.headers['content-length'] assert_match %r{max-age}, last_response.headers['cache-control'] end test "bad fingerprint digest returns a 404" do get "/assets/application-0000000000000000000000000000000000000000.js" assert_equal 404, last_response.status head "/assets/application-0000000000000000000000000000000000000000.js" assert_equal 404, last_response.status assert_equal "0", last_response.headers['content-length'] assert_equal "", last_response.body end test "missing source" do get "/assets/none.js" assert_equal 404, last_response.status assert_equal "pass", last_response.headers['x-cascade'] end test "re-throw JS exceptions in the browser" do get "/assets/missing_require.js" assert_equal 200, last_response.status assert_match(/Sprockets::FileNotFound: couldn't find file 'notfound' with type 'application\/javascript'/, last_response.body) assert_match(/(in #{fixture_path("server/vendor/javascripts/missing_require.js")}:1)/, last_response.body) end test "display CSS exceptions in the browser" do get "/assets/missing_require.css" assert_equal 200, last_response.status assert_match %r{content: ".*?Sprockets::FileNotFound}, last_response.body end test "serve encoded utf-8 filename" do get "/assets/%E6%97%A5%E6%9C%AC%E8%AA%9E.js" assert_equal "var japanese = \"日本語\";\n", last_response.body end test "illegal require outside load path" do get "/assets//etc/passwd" assert_equal 403, last_response.status get "/assets/%2fetc/passwd" assert_equal 403, last_response.status get "/assets//%2fetc/passwd" assert_equal 403, last_response.status get "/assets/%2f/etc/passwd" assert_equal 403, last_response.status get "/assets/../etc/passwd" assert_equal 403, last_response.status get "/assets/%2e%2e/etc/passwd" assert_equal 403, last_response.status get "/assets/.-0000000./etc/passwd" assert_equal 403, last_response.status head "/assets/.-0000000./etc/passwd" assert_equal 403, last_response.status assert_equal "0", last_response.headers['content-length'] assert_equal "", last_response.body end test "illegal access of a file asset" do absolute_path = fixture_path("server/app/javascripts") get "assets/file:%2f%2f//#{absolute_path}/foo.js" assert_equal 403, last_response.status end test "add new source to tree" do filename = fixture_path("server/app/javascripts/baz.js") sandbox filename do get "/assets/tree.js" assert_equal %[var foo;\n\n(function() {\n application.boot();\n})();\nvar bar;\nconsole.log(\"I was hashed by esbuild!\");\nconsole.log("I was already hashed!");\nvar japanese = \"日本語\";\n], last_response.body File.open(filename, "w") do |f| f.write "var baz;\n" end path = fixture_path "server/app/javascripts" mtime = Time.now + 60 File.utime(mtime, mtime, path) get "/assets/tree.js" assert_equal %[var foo;\n\n(function() {\n application.boot();\n})();\nvar bar;\nvar baz;\nconsole.log(\"I was hashed by esbuild!\");\nconsole.log("I was already hashed!");\nvar japanese = \"日本語\";\n], last_response.body end end test "serving static assets" do get "/assets/logo.png" assert_equal 200, last_response.status assert_equal "image/png", last_response.headers['content-type'] refute last_response.headers['content-encoding'] assert_equal File.binread(fixture_path("server/app/images/logo.png")), last_response.body end test "disallow non-get methods" do get "/assets/foo.js" assert_equal 200, last_response.status head "/assets/foo.js" assert_equal 200, last_response.status assert_equal "application/javascript", last_response.headers['content-type'] assert_equal "0", last_response.headers['content-length'] assert_equal "", last_response.body post "/assets/foo.js" assert_equal 405, last_response.status put "/assets/foo.js" assert_equal 405, last_response.status delete "/assets/foo.js" assert_equal 405, last_response.status end test "invalid URLs" do get "/assets/%E2%EF%BF%BD%A6.js" assert_equal 400, last_response.status end end sprockets-4.2.1/test/test_source_map_utils.rb000066400000000000000000000636611447572140400214770ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/source_map_utils' class TestSourceMapUtils < Minitest::Test def deep_dup(object) Marshal.load( Marshal.dump(object) ) end def test_map source_map = { 'version' => 3, 'file' => "script.min.js", 'mappings' => "AAEAA,QAASA,MAAK,EAAG,CACfC,OAAAC,IAAA,CAAY,eAAZ,CADe", 'sources' => ["script.js"], 'names' => ["hello", "console", "log"] } mappings = Sprockets::SourceMapUtils.decode_vlq_mappings(source_map['mappings'], sources: source_map['sources'], names: source_map['names']) assert mapping = mappings.first assert_equal 1, mapping[:generated].first assert_equal 0, mapping[:generated].last assert_equal 3, mapping[:original].first assert_equal 0, mapping[:original].last assert_equal 'script.js', mapping[:source] assert_equal 'hello', mapping[:name] assert mapping = mappings.last assert_equal 1, mapping[:generated].first assert_equal 45, mapping[:generated].last assert_equal 3, mapping[:original].first assert_equal 17, mapping[:original].last assert_equal 'script.js', mapping[:source] assert_nil mapping[:name] end def test_map2 source_map = { 'version' => 3, 'file' => "example.js", 'mappings' => ";;;;;EACAA;;EACAC;;EAGA;IAAA;;;EAGAC;IAAS;;;EAGTC;;EAGAC;IACE;IACA;IACA;MAAQ;;;;EAGVC;;;IACE;;;EAGF;IAAA;;;EAGAC;;;IAAQ;;MAAA", 'sources' => ["example.coffee"], 'names' => ["number", "opposite", "square", "list", "math", "race", "cubes"] } mappings = Sprockets::SourceMapUtils.decode_vlq_mappings(source_map['mappings'], sources: source_map['sources'], names: source_map['names']) assert mapping = mappings.first assert_equal 6, mapping[:generated].first assert_equal 2, mapping[:generated].last assert_equal 2, mapping[:original].first assert_equal 0, mapping[:original].last assert_equal 'example.coffee', mapping[:source] assert_equal 'number', mapping[:name] assert mapping = mappings.last assert_equal 43, mapping[:generated].first assert_equal 6, mapping[:generated].last assert_equal 28, mapping[:original].first assert_equal 8, mapping[:original].last assert_equal 'example.coffee', mapping[:source] assert_nil mapping[:name] end def test_map3 source_map = { 'version' => 3, 'file' => "example.min.js", 'mappings' => "AACC,SAAQ,EAAG,CAAA,IACCA,CADD,CACOC,CADP,CACaC,CADb,CAC0CC,CAWpDA,EAAA,CAASA,QAAQ,CAACC,CAAD,CAAI,CACnB,MAAOA,EAAP,CAAWA,CADQ,CAIrBJ,EAAA,CAAO,CAAC,CAAD,CAAI,CAAJ,CAAO,CAAP,CAAU,CAAV,CAAa,CAAb,CAEPC,EAAA,CAAO,MACCI,IAAAC,KADD,QAEGH,CAFH,MAGCI,QAAQ,CAACH,CAAD,CAAI,CAChB,MAAOA,EAAP,CAAWD,CAAA,CAAOC,CAAP,CADK,CAHb,CAcc,YAArB,GAAI,MAAOI,MAAX,EAA8C,IAA9C,GAAoCA,KAApC,EACEC,KAAA,CAAM,YAAN,CAGO,UAAQ,EAAG,CAAA,IACdC,CADc,CACVC,CADU,CACJC,CACdA,EAAA,CAAW,EACNF,EAAA,CAAK,CAAV,KAAaC,CAAb,CAAoBX,CAAAa,OAApB,CAAiCH,CAAjC,CAAsCC,CAAtC,CAA4CD,CAAA,EAA5C,CACER,CACA,CADMF,CAAA,CAAKU,CAAL,CACN,CAAAE,CAAAE,KAAA,CAAcb,CAAAM,KAAA,CAAUL,CAAV,CAAd,CAEF,OAAOU,EAPW,CAAX,CAAA,EApCC,CAAX,CAAAG,KAAA,CA8CO,IA9CP", 'sources' => ["example.js"], 'names' => ["list","math","num","square","x","Math","sqrt","cube","elvis","alert","_i","_len","_results","length","push","call"] } mappings = Sprockets::SourceMapUtils.decode_vlq_mappings(source_map['mappings'], sources: source_map['sources'], names: source_map['names']) assert mapping = mappings.first assert_equal 1, mapping[:generated].first assert_equal 0, mapping[:generated].last assert_equal 2, mapping[:original].first assert_equal 1, mapping[:original].last assert_equal 'example.js', mapping[:source] assert_nil mapping[:name] assert mapping = mappings.last assert_equal 1, mapping[:generated].first assert_equal 289, mapping[:generated].last assert_equal 2, mapping[:original].first assert_equal 1, mapping[:original].last assert_equal 'example.js', mapping[:source] assert_nil mapping[:name] end def test_concat_empty_source_map_returns_original abc_mappings = [ { source: 'a.js', generated: [rand(1..100), rand(0..100)], original: [rand(1..100), rand(0..100)] }, { source: 'b.js', generated: [rand(1..100), rand(0..100)], original: [rand(1..100), rand(0..100)] }, { source: 'c.js', generated: [rand(1..100), rand(0..100)], original: [rand(1..100), rand(0..100)] } ].freeze linecount = abc_mappings.count(";") linecount += 1 if abc_mappings[-1] == ";" map = { "version" => 3, "file" => "a.js", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(abc_mappings), "sources" => ["a.js", "b.js", "c.js"], "names" => [], "x_sprockets_linecount" => linecount } empty_map = { "version" => 3, "file" => 'a.js', "sections" => [] } index_map = { "version" => 3, "file" => "a.js", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => map } ] } assert_equal map, Sprockets::SourceMapUtils.concat_source_maps(nil, deep_dup(map)) assert_equal map, Sprockets::SourceMapUtils.concat_source_maps(deep_dup(map), nil) assert_equal index_map, Sprockets::SourceMapUtils.concat_source_maps(deep_dup(empty_map), map.dup) expected_index_map = deep_dup(index_map) expected_index_map["sections"].first["map"].delete("x_sprockets_linecount") assert_equal expected_index_map, Sprockets::SourceMapUtils.concat_source_maps(deep_dup(map), deep_dup(empty_map)) end def test_concat_source_maps abc_mappings = [ { source: 'a.js', generated: [1, 0], original: [1, 0] }, { source: 'b.js', generated: [2, 0], original: [20, 0] }, { source: 'c.js', generated: [3, 0], original: [30, 0] } ].freeze d_mapping = [ { source: 'd.js', generated: [1, 0], original: [1, 0] } ].freeze abc_map = { "version" => 3, "file" => "a.js", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(abc_mappings), "sources" => ["a.js", "b.js", "c.js"], "names" => [], "x_sprockets_linecount" => 3 } d_map = { "version" => 3, "file" => "d.js", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(d_mapping), "sources" => ["d.js"], "names" => [], "x_sprockets_linecount" => 1 } combined = Sprockets::SourceMapUtils.concat_source_maps(deep_dup(abc_map), deep_dup(d_map)) assert_equal [ { source: 'a.js', generated: [1, 0], original: [1, 0] }, { source: 'b.js', generated: [2, 0], original: [20, 0] }, { source: 'c.js', generated: [3, 0], original: [30, 0] }, { source: 'd.js', generated: [4, 0], original: [1, 0] } ], Sprockets::SourceMapUtils.decode_source_map(combined)[:mappings] combined = Sprockets::SourceMapUtils.concat_source_maps(deep_dup(d_map), deep_dup(abc_map)) assert_equal [ { source: 'd.js', generated: [1, 0], original: [1, 0] }, { source: 'a.js', generated: [2, 0], original: [1, 0] }, { source: 'b.js', generated: [3, 0], original: [20, 0] }, { source: 'c.js', generated: [4, 0], original: [30, 0] } ], Sprockets::SourceMapUtils.decode_source_map(combined)[:mappings] end def test_concat_without_x_sprockets_linecount_works abc_mappings = [ { source: 'a.js', generated: [1, 0], original: [1, 0] }, { source: 'b.js', generated: [2, 0], original: [20, 0] }, { source: 'c.js', generated: [3, 0], original: [30, 0] } ].freeze d_mapping = [ { source: 'd.js', generated: [1, 0], original: [1, 0] } ].freeze abc_map = { "version" => 3, "file" => "a.js", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(abc_mappings), "sources" => ["a.js", "b.js", "c.js"], "names" => [], "x_sprockets_linecount" => 3 } d_map = { "version" => 3, "file" => "d.js", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(d_mapping), "sources" => ["d.js"], "names" => [], } combined = Sprockets::SourceMapUtils.concat_source_maps(deep_dup(abc_map), deep_dup(d_map)) assert_equal [ { source: 'a.js', generated: [1, 0], original: [1, 0] }, { source: 'b.js', generated: [2, 0], original: [20, 0] }, { source: 'c.js', generated: [3, 0], original: [30, 0] }, { source: 'd.js', generated: [4, 0], original: [1, 0] } ], Sprockets::SourceMapUtils.decode_source_map(combined)[:mappings] combined = Sprockets::SourceMapUtils.concat_source_maps(deep_dup(d_map), deep_dup(abc_map)) assert_equal [ { source: 'd.js', generated: [1, 0], original: [1, 0] }, { source: 'a.js', generated: [2, 0], original: [1, 0] }, { source: 'b.js', generated: [3, 0], original: [20, 0] }, { source: 'c.js', generated: [4, 0], original: [30, 0] } ], Sprockets::SourceMapUtils.decode_source_map(combined)[:mappings] end def test_combine_source_maps_returns_original abc_mappings = [ { source: 'a.js', generated: [1, 0], original: [0, 0] }, { source: 'b.js', generated: [2, 0], original: [20, 0] }, { source: 'c.js', generated: [3, 0], original: [30, 0] } ] map = { "version" => 3, "file" => "a.js", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(abc_mappings), "sources" => ["a.js", "b.js", "c.js"], "names" => [] } assert_equal map, Sprockets::SourceMapUtils.combine_source_maps(nil, map.dup) end def test_combine_source_maps first_mapping =[ { :source => "index.coffee", :generated => [2, 0], :original => [1, 0] }, { :source => "index.coffee", :generated => [3, 0], :original => [1, 0] }, { :source => "index.coffee", :generated => [3, 6], :original => [1, 0] }, { :source => "index.coffee", :generated => [3, 10], :original => [1, 0] }, { :source => "index.coffee", :generated => [5, 0], :original => [1, 0] }, { :source => "index.coffee", :generated => [5, 2], :original => [1, 0] }, { :source => "index.coffee", :generated => [5, 6], :original => [1, 0] }, { :source => "index.coffee", :generated => [5, 9], :original => [1, 7] }, { :source => "index.coffee", :generated => [5, 18], :original => [1, 7] }, { :source => "index.coffee", :generated => [5, 21], :original => [1, 7] }, { :source => "index.coffee", :generated => [6, 11], :original => [2, 2] }, { :source => "index.coffee", :generated => [6, 16], :original => [2, 2] }, { :source => "index.coffee", :generated => [6, 17], :original => [2, 8] }, { :source => "index.coffee", :generated => [6, 30], :original => [2, 2] }, { :source => "index.coffee", :generated => [6, 32], :original => [1, 7] }, { :source => "index.coffee", :generated => [7, 2], :original => [1, 7] }, { :source => "index.coffee", :generated => [7, 3], :original => [1, 0] }, { :source => "index.coffee", :generated => [7, 4], :original => [1, 0] }, { :source => "index.coffee", :generated => [9, 0], :original => [4, 0] }, { :source => "index.coffee", :generated => [9, 2], :original => [4, 0] }, { :source => "index.coffee", :generated => [9, 6], :original => [4, 11] }, { :source => "index.coffee", :generated => [9, 10], :original => [4, 0] }, { :source => "index.coffee", :generated => [10, 0], :original => [4, 0] }, { :source => "index.coffee", :generated => [10, 4], :original => [4, 3] }, { :source => "index.coffee", :generated => [10, 8], :original => [4, 0] }, { :source => "index.coffee", :generated => [10, 9], :original => [4, 0] }, { :source => "index.coffee", :generated => [10, 10], :original => [4, 0] }, { :source => "index.coffee", :generated => [10, 11], :original => [4, 0] }, { :source => "index.coffee", :generated => [11, 3], :original => [1, 0] }, { :source => "index.coffee", :generated => [12, 0], :original => [1, 0] } ] second_mapping = [ { :source => "index.js", :generated => [1, 1], :original => [2, 0] }, { :source => "index.js", :generated => [1, 12], :original => [3, 2] }, { :source => "index.js", :generated => [1, 15], :original => [3, 6], :name => "test" }, { :source => "index.js", :generated => [1, 20], :original => [5, 2], :name => "test" }, { :source => "index.js", :generated => [1, 26], :original => [5, 9] }, { :source => "index.js", :generated => [1, 37], :original => [6, 4] }, { :source => "index.js", :generated => [1, 43], :original => [6, 11], :name => "alert" }, { :source => "index.js", :generated => [1, 50], :original => [6, 17] }, { :source => "index.js", :generated => [1, 65], :original => [9, 2] }, { :source => "index.js", :generated => [1, 69], :original => [9, 6] }, { :source => "index.js", :generated => [1, 74], :original => [9, 12] }, { :source => "index.js", :generated => [1, 75], :original => [10, 4], :name => "test" }, { :source => "index.js", :generated => [1, 84], :original => [13, 3], :name => "call" }, { :source => "index.js", :generated => [1, 89], :original => [13, 8], :name => "this" } ] expected_mapping = [ { :source => "index.coffee", :generated => [1, 1], :original => [1, 0]}, { :source => "index.coffee", :generated => [1, 12], :original => [1, 0]}, { :source => "index.coffee", :generated => [1, 15], :original => [1, 0]}, { :source => "index.coffee", :generated => [1, 20], :original => [1, 0]}, { :source => "index.coffee", :generated => [1, 26], :original => [1, 7]}, { :source => "index.coffee", :generated => [1, 37], :original => [1, 7]}, { :source => "index.coffee", :generated => [1, 43], :original => [2, 2]}, { :source => "index.coffee", :generated => [1, 50], :original => [2, 8]}, { :source => "index.coffee", :generated => [1, 65], :original => [4, 0]}, { :source => "index.coffee", :generated => [1, 69], :original => [4, 11]}, { :source => "index.coffee", :generated => [1, 74], :original => [4, 0]}, { :source => "index.coffee", :generated => [1, 75], :original => [4, 3]}, { :source => "index.coffee", :generated => [1, 84], :original => [1, 0]}, { :source => "index.coffee", :generated => [1, 89], :original => [1, 0]} ] first_map = { "version" => 3, "file" => "index.coffee", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(first_mapping), "sources" => ["index.coffee"], "names" => [] } second_map = { "version" => 3, "file" => "index.js", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(second_mapping), "sources" => ["index.js"], "names" => ["test", "alert", "call", "this"] } expected_map = { "version" => 3, "file" => "index.coffee", "mappings" => Sprockets::SourceMapUtils.encode_vlq_mappings(expected_mapping), "sources" => ["index.coffee"], "names" => [] } combined_map = Sprockets::SourceMapUtils.combine_source_maps(first_map, second_map) assert_equal expected_map, combined_map assert_equal 'CAAA,WAAA,GAAA,KAAA,MAAO,WAAA,MACL,OAAM,eAER,IAAW,KAAX,CAAG,SAHH,KAAA', Sprockets::SourceMapUtils.encode_source_map(Sprockets::SourceMapUtils.decode_source_map(expected_map))["mappings"] end def test_compare_offsets assert_equal( 0, Sprockets::SourceMapUtils.compare_source_offsets([1, 5], [1, 5])) assert_equal(-1, Sprockets::SourceMapUtils.compare_source_offsets([1, 5], [2, 0])) assert_equal(-1, Sprockets::SourceMapUtils.compare_source_offsets([1, 5], [1, 6])) assert_equal(-1, Sprockets::SourceMapUtils.compare_source_offsets([1, 5], [5, 6])) assert_equal( 1, Sprockets::SourceMapUtils.compare_source_offsets([1, 5], [1, 4])) assert_equal( 1, Sprockets::SourceMapUtils.compare_source_offsets([2, 0], [1, 4])) assert_equal( 1, Sprockets::SourceMapUtils.compare_source_offsets([5, 0], [1, 4])) end def test_bsearch_mappings mappings = [ { source: 'a.js', generated: [1, 0], original: [1, 0] }, { source: 'b.js', generated: [2, 0], original: [20, 0] }, { source: 'c.js', generated: [3, 0], original: [30, 0] }, { source: 'f.js', generated: [5, 0], original: [40, 0] }, ] assert_equal [1, 0], Sprockets::SourceMapUtils.bsearch_mappings(mappings, [1, 0])[:original] assert_equal [1, 0], Sprockets::SourceMapUtils.bsearch_mappings(mappings, [1, 5])[:original] assert_equal [20, 0], Sprockets::SourceMapUtils.bsearch_mappings(mappings, [2, 0])[:original] assert_equal [20, 0], Sprockets::SourceMapUtils.bsearch_mappings(mappings, [2, 1])[:original] assert_equal [30, 0], Sprockets::SourceMapUtils.bsearch_mappings(mappings, [3, 0])[:original] assert_equal [30, 0], Sprockets::SourceMapUtils.bsearch_mappings(mappings, [4, 0])[:original] assert_equal [40, 0], Sprockets::SourceMapUtils.bsearch_mappings(mappings, [6, 0])[:original] end def test_decode_source_map expected = { version: 3, file: "hello.js", mappings: [ { source: 'b.js', generated: [1, 0], original: [20, 0] }, { source: 'c.js', generated: [2, 0], original: [30, 0] } ], sources: ["a.js", "b.js", "c.js"], names: [] } actual = Sprockets::SourceMapUtils.decode_source_map(JSON.parse('{"version":3,"file":"hello.js","mappings":"ACmBA;ACUA","sources":["a.js","b.js","c.js"],"names":[]}')) assert_equal expected, actual end def test_decode_index_source_map expected = { version: 3, file: "index.js", mappings: [ { source: 'file.js', generated: [1, 0], original: [1, 0] }, { source: 'file.js', generated: [2, 0], original: [1, 0] }, { source: 'file.js', generated: [3, 0], original: [1, 0] }, { source: 'index.js', generated: [4, 0], original: [1, 0] }, { source: 'index.js', generated: [5, 0], original: [2, 0] } ], sources: ["file.js", "index.js"], names: [] } index_map = { "version" => 3, "file" => "index.js", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => { "version" => 3, "file" => "file.js", "mappings" => "AAAA;AAAA;AAAA", "sources" => ["file.js"], "names" => [] } }, { "offset" => { "line" => 3, "column" => 0 }, "map" => { "version" => 3, "file" => "index.js", "mappings" => "AAAA;AACA", "sources" => ["index.js"], "names" => [] } } ] } actual = Sprockets::SourceMapUtils.decode_source_map(index_map) assert_equal expected, actual end def test_encode_source_map map = { version: 3, file: "hello.js", mappings: [ { source: 'a.js', generated: [0, 0], original: [0, 0] }, { source: 'b.js', generated: [1, 0], original: [20, 0] }, { source: 'c.js', generated: [2, 0], original: [30, 0] } ], sources: ["a.js", "b.js", "c.js"], names: [] } assert_equal '{"version":3,"file":"hello.js","mappings":"ACmBA;ACUA","sources":["a.js","b.js","c.js"],"names":[]}', JSON.dump(Sprockets::SourceMapUtils.encode_source_map(map)) end def test_decode_vlq_mappings mappings = "AAEAA,QAASA,MAAK,EAAG,CACfC,OAAAC,IAAA,CAAY,eAAZ,CADe" sources = ["script.js"], names = ["hello", "console", "log"] assert_equal([ { source: ["script.js"], generated: [1, 0], original: [3, 0], name: "hello" }, { source: ["script.js"], generated: [1, 8], original: [3, 9], name: "hello" }, { source: ["script.js"], generated: [1, 14], original: [3, 14] }, { source: ["script.js"], generated: [1, 16], original: [3, 17] }, { source: ["script.js"], generated: [1, 17], original: [4, 2], name: "console" }, { source: ["script.js"], generated: [1, 24], original: [4, 2], name: "log" }, { source: ["script.js"], generated: [1, 28], original: [4, 2] }, { source: ["script.js"], generated: [1, 29], original: [4, 14] }, { source: ["script.js"], generated: [1, 44], original: [4, 2] }, { source: ["script.js"], generated: [1, 45], original: [3, 17] } ], Sprockets::SourceMapUtils.decode_vlq_mappings(mappings, sources: sources, names: names)) end def test_encode_vlq_mappings assert_equal "", Sprockets::SourceMapUtils.encode_vlq_mappings([]) mappings = [ { source: 'a.js', generated: [1, 0], original: [1, 0] }, { source: 'b.js', generated: [2, 0], original: [20, 0] }, { source: 'c.js', generated: [3, 0], original: [30, 0] } ] expected = "AAAA;ACmBA;ACUA" actual = Sprockets::SourceMapUtils.encode_vlq_mappings(mappings) assert_equal expected, actual end TESTS = { 'A' => [0], 'C' => [1], 'D' => [-1], 'E' => [2], 'F' => [-2], 'K' => [5], 'L' => [-5], 'w+B' => [1000], 'x+B' => [-1000], 'gqjG' => [100000], 'hqjG' => [-100000], 'AAgBC' => [0, 0, 16, 1], 'AAgCgBC' => [0, 0, 32, 16, 1], 'DFLx+BhqjG' => [-1, -2, -5, -1000, -100000], 'CEKw+BgqjG' => [1, 2, 5, 1000, 100000] } MAP_TESTS = { 'AA,AA;;AACDE' => [[[0, 0], [0, 0]], [], [[0, 0, 1, -1, 2]]], ';;;;EAEE,EAAE,EAAC,CAAE;ECQY,UACC' => [[], [], [], [], [[2, 0, 2, 2], [2, 0, 0, 2], [2, 0, 0, 1], [1, 0, 0, 2]], [[2, 1, 8, 12], [10, 0, 1, 1]]], 'AAEAA,QAASA,MAAK,EAAG,CACfC,OAAAC,IAAA,CAAY,eAAZ,CADe' => [[[0, 0, 2, 0, 0], [8, 0, 0, 9, 0], [6, 0, 0, 5], [2, 0, 0, 3], [1, 0, 1, -15, 1], [7, 0, 0, 0, 1], [4, 0, 0, 0], [1, 0, 0, 12], [15, 0, 0, -12], [1, 0, -1, 15]]], ';;;;;EACAA;;EACAC;;EAGA;IAAA;;;EAGAC;IAAS;;;EAGTC;;EAGAC;IACE;IACA;IACA;MAAQ;;;;EAGVC;;;IACE;;;EAGF;IAAA;;;EAGAC;;;IAAQ;;MAAA' => [[], [], [], [], [], [[2, 0, 1, 0, 0]], [], [[2, 0, 1, 0, 1]], [], [[2, 0, 3, 0]], [[4, 0, 0, 0]], [], [], [[2, 0, 3, 0, 1]], [[4, 0, 0, 9]], [], [], [[2, 0, 3, -9, 1]], [], [[2, 0, 3, 0, 1]], [[4, 0, 1, 2]], [[4, 0, 1, 0]], [[4, 0, 1, 0]], [[6, 0, 0, 8]], [], [], [], [[2, 0, 3, -10, 1]], [], [], [[4, 0, 1, 2]], [], [], [[2, 0, 3, -2]], [[4, 0, 0, 0]], [], [], [[2, 0, 3, 0, 1]], [], [], [[4, 0, 0, 8]], [], [[6, 0, 0, 0]]], 'AACC,SAAQ,EAAG,CAAA,IACCA,CADD,CACOC,CADP,CACaC,CADb,CAC0CC,CAWpDA,EAAA,CAASA,QAAQ,CAACC,CAAD,CAAI,CACnB,MAAOA,EAAP,CAAWA,CADQ,CAIrBJ,EAAA,CAAO,CAAC,CAAD,CAAI,CAAJ,CAAO,CAAP,CAAU,CAAV,CAAa,CAAb,CAEPC,EAAA,CAAO,MACCI,IAAAC,KADD,QAEGH,CAFH,MAGCI,QAAQ,CAACH,CAAD,CAAI,CAChB,MAAOA,EAAP,CAAWD,CAAA,CAAOC,CAAP,CADK,CAHb,CAcc,YAArB,GAAI,MAAOI,MAAX,EAA8C,IAA9C,GAAoCA,KAApC,EACEC,KAAA,CAAM,YAAN,CAGO,UAAQ,EAAG,CAAA,IACdC,CADc,CACVC,CADU,CACJC,CACdA,EAAA,CAAW,EACNF,EAAA,CAAK,CAAV,KAAaC,CAAb,CAAoBX,CAAAa,OAApB,CAAiCH,CAAjC,CAAsCC,CAAtC,CAA4CD,CAAA,EAA5C,CACER,CACA,CADMF,CAAA,CAAKU,CAAL,CACN,CAAAE,CAAAE,KAAA,CAAcb,CAAAM,KAAA,CAAUL,CAAV,CAAd,CAEF,OAAOU,EAPW,CAAX,CAAA,EApCC,CAAX,CAAAG,KAAA,CA8CO,IA9CP' => [[[0, 0, 1, 1], [9, 0, 0, 8], [2, 0, 0, 3], [1, 0, 0, 0], [4, 0, 1, 1, 0], [1, 0, -1, -1], [1, 0, 1, 7, 1], [1, 0, -1, -7], [1, 0, 1, 13, 1], [1, 0, -1, -13], [1, 0, 1, 42, 1], [1, 0, 11, -52, 0], [2, 0, 0, 0], [1, 0, 0, 9, 0], [8, 0, 0, 8], [1, 0, 0, 1, 1], [1, 0, 0, -1], [1, 0, 0, 4], [1, 0, 1, -19], [6, 0, 0, 7, 0], [2, 0, 0, -7], [1, 0, 0, 11, 0], [1, 0, -1, 8], [1, 0, 4, -21, -4], [2, 0, 0, 0], [1, 0, 0, 7], [1, 0, 0, 1], [1, 0, 0, -1], [1, 0, 0, 4], [1, 0, 0, -4], [1, 0, 0, 7], [1, 0, 0, -7], [1, 0, 0, 10], [1, 0, 0, -10], [1, 0, 0, 13], [1, 0, 0, -13], [1, 0, 2, -7, 1], [2, 0, 0, 0], [1, 0, 0, 7], [6, 0, 1, 1, 4], [4, 0, 0, 0, 1], [5, 0, -1, -1], [8, 0, 2, 3, -3], [1, 0, -2, -3], [6, 0, 3, 1, 4], [8, 0, 0, 8], [1, 0, 0, 1, -3], [1, 0, 0, -1], [1, 0, 0, 4], [1, 0, 1, -16], [6, 0, 0, 7, 0], [2, 0, 0, -7], [1, 0, 0, 11, -1], [1, 0, 0, 0], [1, 0, 0, 7, 1], [1, 0, 0, -7], [1, 0, -1, 5], [1, 0, -3, -13], [1, 0, 14, 14], [12, 0, 0, -21], [3, 0, 0, 4], [6, 0, 0, 7, 4], [6, 0, 0, -11], [2, 0, 0, 46], [4, 0, 0, -46], [3, 0, 0, 36, 0], [5, 0, 0, -36], [2, 0, 1, 2, 1], [5, 0, 0, 0], [1, 0, 0, 6], [12, 0, 0, -6], [1, 0, 3, 7], [10, 0, 0, 8], [2, 0, 0, 3], [1, 0, 0, 0], [4, 0, 1, -14, 1], [1, 0, -1, 14], [1, 0, 1, -10, 1], [1, 0, -1, 10], [1, 0, 1, -4, 1], [1, 0, 1, -14, 0], [2, 0, 0, 0], [1, 0, 0, 11], [2, 0, 1, -6, -2], [2, 0, 0, 0], [1, 0, 0, 5], [1, 0, 0, -10], [5, 0, 0, 13, 1], [1, 0, 0, -13], [1, 0, 0, 20, -11], [1, 0, 0, 0, 13], [7, 0, 0, -20], [1, 0, 0, 33, -3], [1, 0, 0, -33], [1, 0, 0, 38, 1], [1, 0, 0, -38], [1, 0, 0, 44, -1], [1, 0, 0, 0], [2, 0, 0, -44], [1, 0, 1, 2, -8], [1, 0, 1, 0], [1, 0, -1, 6, -2], [1, 0, 0, 0], [1, 0, 0, 5, 10], [1, 0, 0, -5], [1, 0, 1, -6], [1, 0, 0, 0, 2], [1, 0, 0, 0, 2], [5, 0, 0, 0], [1, 0, 0, 14, -13], [1, 0, 0, 0, 6], [5, 0, 0, 0], [1, 0, 0, 10, -5], [1, 0, 0, -10], [1, 0, 0, -14], [1, 0, 2, -2], [7, 0, 0, 7, 10], [2, 0, -7, 11], [1, 0, 0, -11], [1, 0, 0, 0], [2, 0, -36, 1], [1, 0, 0, -11], [1, 0, 0, 0, 3], [5, 0, 0, 0], [1, 0, 46, 7], [4, 0, -46, -7]]] } def test_vlq_encode TESTS.each do |str, int| assert_equal str, Sprockets::SourceMapUtils.vlq_encode(int) end end def test_vlq_decode TESTS.each do |str, int| assert_equal int, Sprockets::SourceMapUtils.vlq_decode(str) end end def test_vlq_encode_decode (-255..255).each do |int| encode = Sprockets::SourceMapUtils.vlq_encode([int]) assert_equal [int], Sprockets::SourceMapUtils.vlq_decode(encode) end end def test_vlq_encode_mappings MAP_TESTS.each do |str, ary| assert_equal str, Sprockets::SourceMapUtils.vlq_encode_mappings(ary) end end def test_vlq_decode_mappings MAP_TESTS.each do |str, ary| assert_equal ary, Sprockets::SourceMapUtils.vlq_decode_mappings(str) end end end sprockets-4.2.1/test/test_source_maps.rb000066400000000000000000000442421447572140400204340ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'sprockets/bundle' require 'sprockets/source_map_utils' silence_warnings do require 'sass' end class TestSourceMaps < Sprockets::TestCase def setup @env = Sprockets::Environment.new(fixture_path('source-maps')) do |env| env.append_path('.') env.cache = {} end end def get_sources(map) map["sections"].reduce([]) { |r, s| r | s["map"]["sources"] } end # Offset should be the line that the asset starts on minus one test "correct offsets" do asset = @env["multi-require.js"] map = asset.metadata[:map] child = @env["child.js"] child_lines = child.to_s.lines.length child_section = map["sections"][0] assert_equal 0, child_section["offset"]["line"] coffee_main = @env["coffee/main.js"] coffee_main_lines = coffee_main.to_s.lines.length coffee_main_section = map["sections"][1] assert_equal child_lines, coffee_main_section["offset"]["line"] sub_a_js = @env["sub/a.js"] sub_a_js_lines = sub_a_js.to_s.lines.length sub_a_js_section = map["sections"][2] assert_equal coffee_main_lines + child_lines, sub_a_js_section["offset"]["line"] plain_js_section = map["sections"][3] assert_equal sub_a_js_lines + coffee_main_lines + child_lines, plain_js_section["offset"]["line"] end test "builds a source map for js files" do asset = @env['child.js'] map = asset.metadata[:map] assert_equal ["child.source.js"], get_sources(map) end test "builds a concatenated source map" do asset = @env['application.js'] map = asset.metadata[:map] assert_equal [ "project.source.coffee", "users.source.coffee", "application.source.coffee" ], get_sources(map) end test "reads js data transcoded to UTF-8" do processor = Proc.new do |input| assert_equal Encoding::UTF_8, input[:data].encoding { data: input[:data] } end @env.register_processor('application/javascript', processor) assert asset = @env.find_asset('plain.js', pipeline: :debug) ensure @env.unregister_preprocessor('application/javascript', processor) end test "reads css data transcoded to UTF-8" do processor = Proc.new do |input| assert_equal Encoding::UTF_8, input[:data].encoding { data: input[:data] } end @env.register_processor('text/css', processor) assert asset = @env.find_asset('sass/precompiled/main.css', pipeline: :debug) ensure @env.unregister_preprocessor('text/css', processor) end test "builds a minified source map" do @env.js_compressor = Sprockets::UglifierCompressor.new asset = @env['application.js'] map = Sprockets::SourceMapUtils.decode_source_map(asset.metadata[:map]) assert map[:mappings].all? { |mapping| mapping[:generated][0] == 1 } assert_equal [ "project.source.coffee", "users.source.coffee", "application.source.coffee" ], map[:sources] end test "builds a source map with js dependency" do asset = @env['parent.js'] map = asset.metadata[:map] assert_equal [ "child.source.js", "users.source.coffee", "parent.source.js" ], get_sources(map) end test "rebuilds a source map when related dependency has changed" do filename = fixture_path('source-maps/dynamic/unstable.js') sandbox filename do write(filename, "var magic_number = 42;", 1421000000) asset1 = @env.find_asset('dynamic/application.js', pipeline: :debug) mapUrl1 = asset1.source.match(/^\/\/# sourceMappingURL=(.*?)$/)[1] write(filename, "var number_of_the_beast = 666;\nmagic_number = 7;", 1422000000) asset2 = @env.find_asset('dynamic/application.js', pipeline: :debug) mapUrl2 = asset2.source.match(/^\/\/# sourceMappingURL=(.*?)$/)[1] refute_equal(asset1.digest_path, asset2.digest_path, "Asset digest didn't update.") refute_equal(mapUrl1, mapUrl2, "`sourceMappingUrl` didn't update.") end end test "compile coffeescript source map" do assert asset = @env.find_asset("coffee/main.js") assert_equal fixture_path('source-maps/coffee/main.coffee'), asset.filename assert_equal "application/javascript", asset.content_type assert_match "(function() {", asset.source assert_match "Math.sqrt", asset.source assert asset = @env.find_asset("coffee/main.js.map") assert_equal fixture_path('source-maps/coffee/main.coffee'), asset.filename assert_equal "coffee/main.js.map", asset.logical_path assert_equal "application/js-sourcemap+json", asset.content_type assert_equal [ "file://#{fixture_path_for_uri('source-maps/coffee/main.coffee')}?type=text/coffeescript&pipeline=source" ], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ "version" => 3, "file" => "coffee/main.coffee", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => { "version" => 3, "file" => "coffee/main.coffee", "mappings" => "AACA;AAAA,MAAA,sDAAA;IAAA;;EAAA,MAAA,GAAW;;EACX,QAAA,GAAW;;EAGX,IAAgB,QAAhB;IAAA,MAAA,GAAS,CAAC,GAAV;;;EAGA,MAAA,GAAS,SAAC,CAAD;WAAO,CAAA,GAAI;EAAX;;EAGT,IAAA,GAAO,CAAC,CAAD,EAAI,CAAJ,EAAO,CAAP,EAAU,CAAV,EAAa,CAAb;;EAGP,IAAA,GACE;IAAA,IAAA,EAAQ,IAAI,CAAC,IAAb;IACA,MAAA,EAAQ,MADR;IAEA,IAAA,EAAQ,SAAC,CAAD;aAAO,CAAA,GAAI,MAAA,CAAO,CAAP;IAAX,CAFR;;;EAKF,IAAA,GAAO,SAAA;AACL,QAAA;IADM,uBAAQ;WACd,KAAA,CAAM,MAAN,EAAc,OAAd;EADK;;EAIP,IAAsB,8CAAtB;IAAA,KAAA,CAAM,YAAN,EAAA;;;EAGA,KAAA;;AAAS;SAAA,sCAAA;;mBAAA,IAAI,CAAC,IAAL,CAAU,GAAV;AAAA;;;AA1BT", "sources" => ["main.source.coffee"], "names" => [], "x_sprockets_linecount"=>47 } } ] }, map) end test "use precompiled coffeescript source map" do assert asset = @env.find_asset("coffee/precompiled/main.js") assert_equal fixture_path('source-maps/coffee/precompiled/main.js'), asset.filename assert_equal "application/javascript", asset.content_type assert_match "(function() {", asset.source assert_match "Math.sqrt", asset.source assert asset = @env.find_asset("coffee/precompiled/main.js.map") assert_equal fixture_path('source-maps/coffee/precompiled/main.js.map'), asset.filename assert_equal "coffee/precompiled/main.js.map", asset.logical_path assert_equal "application/js-sourcemap+json", asset.content_type assert map = JSON.parse(asset.source) assert_equal 3, map['version'] assert_equal "main.js", map['file'] assert_equal 779, map['mappings'].size end test "compile babel source map" do assert asset = @env.find_asset("babel/main.js") assert_equal fixture_path('source-maps/babel/main.es6'), asset.filename assert_equal "application/javascript", asset.content_type assert_match "var SkinnedMesh = (function (_THREE$Mesh)", asset.source assert_match "_defineProperty({}, Symbol.iterator", asset.source assert asset = @env.find_asset("babel/main.js.map") assert_equal fixture_path('source-maps/babel/main.es6'), asset.filename assert_equal "babel/main.js.map", asset.logical_path assert_equal "application/js-sourcemap+json", asset.content_type assert_equal [ "file://#{fixture_path_for_uri('source-maps/babel/main.es6')}?type=application/ecmascript-6&pipeline=source" ], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ "version" => 3, "file" => "babel/main.es6", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => { "version" => 3, "file" => "babel/main.es6", "mappings" => ";;;;;;;;;;AACA,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,UAAA,CAAC;SAAI,CAAC,GAAG,CAAC;CAAA,CAAC,CAAC;AACjC,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,UAAC,CAAC,EAAE,CAAC;SAAK,CAAC,GAAG,CAAC;CAAA,CAAC,CAAC;;IAEhC,WAAW;YAAX,WAAW;;AACJ,WADP,WAAW,CACH,QAAQ,EAAE,SAAS,EAAE;0BAD7B,WAAW;;AAEb,+BAFE,WAAW,6CAEP,QAAQ,EAAE,SAAS,EAAE;GAE5B;;eAJG,WAAW;;WAKT,gBAAC,MAAM,EAAE;AACb,iCANE,WAAW,wCAME;KAChB;;;WACmB,yBAAG;AACrB,aAAO,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;KAC5B;;;SAVG,WAAW;GAAS,KAAK,CAAC,IAAI;;AAapC,IAAI,SAAS,uBACV,MAAM,CAAC,QAAQ,0BAAG;MACb,GAAG,EAAM,GAAG,EAEV,IAAI;;;;AAFN,WAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC;;;AAEd,YAAI,GAAG,GAAG;;AACd,WAAG,GAAG,GAAG,CAAC;AACV,WAAG,IAAI,IAAI,CAAC;;eACN,GAAG;;;;;;;;;;;CAEZ,EACF,CAAA", "sources" => ["main.source.es6"], "names" => [], "x_sprockets_linecount"=>66 } } ] }, map) end test "use precompiled babel source map" do assert asset = @env.find_asset("babel/precompiled/main.js") assert_equal fixture_path('source-maps/babel/precompiled/main.js'), asset.filename assert_equal "application/javascript", asset.content_type assert_match "var SkinnedMesh = (function (_THREE$Mesh)", asset.source assert_match "_defineProperty({}, Symbol.iterator", asset.source assert asset = @env.find_asset("babel/precompiled/main.js.map") assert_equal fixture_path('source-maps/babel/precompiled/main.js.map'), asset.filename assert_equal "babel/precompiled/main.js.map", asset.logical_path assert_equal "application/js-sourcemap+json", asset.content_type assert map = JSON.parse(asset.source) assert_equal 3, map['version'] assert_equal "main.es6", map['file'] assert_equal 694, map['mappings'].size end test "compile scss source map" do asset = silence_warnings do @env.find_asset("sass/main.css") end assert asset assert_equal fixture_path('source-maps/sass/main.scss'), asset.filename assert_equal "text/css", asset.content_type assert_match "nav a {", asset.source asset = silence_warnings do @env.find_asset("sass/main.css.map") end assert asset assert_equal fixture_path('source-maps/sass/main.scss'), asset.filename assert_equal "sass/main.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type assert_equal [ "file://#{fixture_path_for_uri('source-maps/sass/main.scss')}?type=text/scss&pipeline=source" ], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ "version" => 3, "file" => "sass/main.scss", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => { "version" => 3, "file" => "sass/main.scss", "mappings" => "AAAA,AACE,GADC,CACD,EAAE,CAAC;EACD,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,UAAU,EAAE,IAAI,GACjB;;AALH,AAOE,GAPC,CAOD,EAAE,CAAC;EAAE,OAAO,EAAE,YAAY,GAAI;;AAPhC,AASE,GATC,CASD,CAAC,CAAC;EACA,OAAO,EAAE,KAAK;EACd,OAAO,EAAE,QAAQ;EACjB,eAAe,EAAE,IAAI,GACtB", "sources" => ['main.source.scss'], "names" => [], "x_sprockets_linecount"=>12 } } ] }, map) end test "compile scss source map with imported dependencies" do asset = silence_warnings do @env.find_asset("sass/with-import.css") end assert asset assert_equal fixture_path('source-maps/sass/with-import.scss'), asset.filename assert_equal "text/css", asset.content_type assert_match "body {\n color: red; }", asset.source asset = silence_warnings do @env.find_asset("sass/with-import.css.map") end assert asset assert_equal fixture_path('source-maps/sass/with-import.scss'), asset.filename assert_equal "sass/with-import.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type assert_equal [ "file://#{fixture_path_for_uri('source-maps/sass/_imported.scss')}?type=text/scss&pipeline=source", "file://#{fixture_path_for_uri('source-maps/sass/with-import.scss')}?type=text/scss&pipeline=source" ], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ "version" => 3, "file" => "sass/with-import.scss", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => { "version" => 3, "file" => "sass/with-import.scss", "mappings" => "ACAA,AAAA,IAAI,CAAC;EAAE,KAAK,EAAE,GAAG,GAAI;;ADErB,AAAA,GAAG,CAAC;EAAE,KAAK,EAAE,IAAI,GAAI", "sources" => [ "with-import.source.scss", "_imported.source.scss", ], "names" => [], "x_sprockets_linecount"=>5 } } ] }, map) end test "use precompiled scss source map" do asset = silence_warnings do @env.find_asset("sass/precompiled/main.css") end assert asset assert_equal fixture_path('source-maps/sass/precompiled/main.css'), asset.filename assert_equal "text/css", asset.content_type assert_match "nav a {", asset.source asset = silence_warnings do @env.find_asset("sass/precompiled/main.css.map") end assert asset assert_equal fixture_path('source-maps/sass/precompiled/main.css.map'), asset.filename assert_equal "sass/precompiled/main.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type assert map = JSON.parse(asset.source) assert_equal 3, map['version'] assert_equal "main.css", map['file'] assert_equal 172, map['mappings'].size end test "source maps work with index alias" do asset = @env.find_asset("foo.js", pipeline: :debug) mapUrl = asset.source.match(/^\/\/# sourceMappingURL=(.*)$/)[1] assert_equal "foo/index.js-008b5ccb5459dc75d7fd51bf5b1ac79fe54d05157d50586c16e558f33d28e9c4.map", mapUrl map = JSON.parse(@env.find_asset('foo/index.js.map').source) assert_equal [ "file.source.coffee", "index.source.js" ], get_sources(map) end test "relative sources at different depths" do assert @env.find_asset("sub/directory.js", pipeline: :debug) assert map = JSON.parse(@env.find_asset("sub/directory.js.map").source) assert_equal [ "a.source.js", "modules/something.source.js", "directory.source.js" ], get_sources(map) end test "source maps are updated correctly after file change" do filename = fixture_path('source-maps/sub/a.js') sandbox filename do expected = JSON.parse(@env.find_asset('sub/directory.js.map').source).tap do |map| index = map["sections"].find_index { |s| s["map"]["file"].end_with?('sub/a.js') } map["sections"][index]["map"]["mappings"] << ";AACA" map["sections"][(index+1)..-1].each do |s| s["offset"]["line"] += 1 end end File.open(filename, 'a') do |file| file.puts "console.log('newline');" end assert_equal JSON.dump(expected), @env.find_asset('sub/directory.js.map').source end end end class TestSasscSourceMaps < Sprockets::TestCase def setup @env = Sprockets::Environment.new @env = Sprockets::Environment.new(".") do |env| require 'sprockets/sassc_processor' env.register_transformer 'text/sass', 'text/css', Sprockets::SasscProcessor env.register_transformer 'text/scss', 'text/css', Sprockets::ScsscProcessor env.append_path fixture_path('source-maps') end end test "compile scss source map" do asset = silence_warnings do @env.find_asset("sass/main.css") end assert asset assert_equal fixture_path('source-maps/sass/main.scss'), asset.filename assert_equal "text/css", asset.content_type assert_match "nav a {", asset.source asset = silence_warnings do @env.find_asset("sass/main.css.map") end assert asset assert_equal fixture_path('source-maps/sass/main.scss'), asset.filename assert_equal "sass/main.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type assert_equal [ "file:///#{ fixture_path('source-maps/sass/main.scss').delete_prefix('/') }?type=text/scss&pipeline=source" ], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ "version" => 3, "file" => "sass/main.scss", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => { "version" => 3, "file" => "sass/main.scss", "mappings" => "AAAA,AACE,GADC,CACD,EAAE,CAAC;EACD,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,UAAU,EAAE,IAAI,GACjB;;AALH,AAOE,GAPC,CAOD,EAAE,CAAC;EAAE,OAAO,EAAE,YAAY,GAAI;;AAPhC,AASE,GATC,CASD,CAAC,CAAC;EACA,OAAO,EAAE,KAAK;EACd,OAAO,EAAE,QAAQ;EACjB,eAAe,EAAE,IAAI,GACtB", "sources" => ["main.source.scss"], "names" => [], "x_sprockets_linecount"=>12 } } ] }, map) end test "compile scss source map with imported dependencies" do asset = silence_warnings do @env.find_asset("sass/with-import.css") end assert asset assert_equal fixture_path('source-maps/sass/with-import.scss'), asset.filename assert_equal "text/css", asset.content_type assert_match "body {\n color: red; }", asset.source asset = silence_warnings do @env.find_asset("sass/with-import.css.map") end assert asset assert_equal fixture_path('source-maps/sass/with-import.scss'), asset.filename assert_equal "sass/with-import.css.map", asset.logical_path assert_equal "application/css-sourcemap+json", asset.content_type assert_equal [ "file://#{fixture_path_for_uri('source-maps/sass/_imported.scss')}?type=text/scss&pipeline=source", "file://#{fixture_path_for_uri('source-maps/sass/with-import.scss')}?type=text/scss&pipeline=source" ], normalize_uris(asset.links) assert map = JSON.parse(asset.source) assert_equal({ "version" => 3, "file" => "sass/with-import.scss", "sections" => [ { "offset" => { "line" => 0, "column" => 0 }, "map" => { "version" => 3, "file" => "sass/with-import.scss", "mappings" => "ACAA,AAAA,IAAI,CAAC;EAAE,KAAK,EAAE,GAAG,GAAI;;ADErB,AAAA,GAAG,CAAC;EAAE,KAAK,EAAE,IAAI,GAAI", "sources" => [ "with-import.source.scss", "_imported.source.scss" ], "names" => [], "x_sprockets_linecount"=>5 } } ] }, map) end end unless RUBY_PLATFORM.include?('java') sprockets-4.2.1/test/test_sprocketize.rb000066400000000000000000000055571447572140400204640ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'shellwords' require 'tmpdir' class TestSprockets < Sprockets::TestCase parallelize_me! def setup super @env = Sprockets::Environment.new(".") do |env| env.append_path(fixture_path('default')) end @dir = Dir.mktmpdir("sprockets") end def teardown FileUtils.rm_rf @dir super end test "show version for -v flag" do output = sprockets "-v" assert_equal "#{Sprockets::VERSION}\n", output end test "show help for -h flag" do output = sprockets "-h" assert_match "Usage: sprockets", output end test "show help for no flags or inputs" do output = sprockets assert_match "Usage: sprockets", output end test "error if load path is missing" do sprockets fixture_path("default/gallery.js") assert_equal 1, $?.exitstatus end test "compile simple file" do output = sprockets "-I", fixture_path("default"), fixture_path("default/gallery.js") assert_equal "var Gallery = {};\n", output end test "show error if multiple files are given" do sprockets fixture_path("default/gallery.js"), fixture_path("default/application.js") assert_equal 1, $?.exitstatus end test "compile file with dependencies" do output = sprockets "-I", fixture_path("asset"), fixture_path("asset/application.js") assert_equal "var Project = {\n find: function(id) {\n }\n};\nvar Users = {\n find: function(id) {\n }\n};\n\n\n\ndocument.on('dom:loaded', function() {\n $('search').focus();\n});\n", output end test "compile asset to output directory" do digest_path = @env['gallery.js'].digest_path output = sprockets "-I", fixture_path("default"), "-o", @dir, fixture_path("default/gallery.js") assert_equal "", output assert Dir["#{@dir}/.sprockets-manifest-*.json"].first assert File.exist?("#{@dir}/#{digest_path}") end test "compile multiple assets to output directory" do digest_path1, digest_path2 = @env['gallery.js'].digest_path, @env['gallery.css'].digest_path output = sprockets "-I", fixture_path("default"), "-o", @dir, "gallery.js", "gallery.css" assert_equal "", output assert Dir["#{@dir}/.sprockets-manifest-*.json"].first assert File.exist?("#{@dir}/#{digest_path1}") assert File.exist?("#{@dir}/#{digest_path2}") end test "minify js with uglify" do output = sprockets "-I", fixture_path("default"), "--js-compressor", "uglify", "gallery.js" assert_equal "var Gallery={};\n", output end test "compress css with sass" do output = sprockets "-I", fixture_path("default"), "--css-compressor", "sass", "gallery.css" assert_equal ".gallery{color:red}\n", output end def sprockets(*args) script = File.expand_path("../../bin/sprockets", __FILE__) lib = File.expand_path("../../lib", __FILE__) `ruby -I#{lib} #{script} #{Shellwords.join(args)} 2>&1` end end sprockets-4.2.1/test/test_transformers.rb000066400000000000000000000173251447572140400206430ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' class TestTransformers < Sprockets::TestCase def setup @env = Sprockets::Environment.new(".") end test "resolve transform type for svg" do assert_equal 'image/svg+xml', @env.resolve_transform_type('image/svg+xml', 'image/svg+xml') assert_equal 'image/svg+xml', @env.resolve_transform_type('image/svg+xml', '*/*') assert_equal 'image/svg+xml', @env.resolve_transform_type('image/svg+xml', nil) assert_equal 'image/svg+xml', @env.resolve_transform_type('image/svg+xml', 'image/*') assert_equal 'image/png', @env.resolve_transform_type('image/svg+xml', 'image/png') assert_equal 'image/svg+xml', @env.resolve_transform_type('image/svg+xml', 'image/svg+xml, image/png') assert_equal 'image/png', @env.resolve_transform_type('image/svg+xml', 'image/png, image/svg+xml') assert_equal 'image/png', @env.resolve_transform_type('image/svg+xml', 'image/svg+xml; q=0.8, image/png') assert_equal 'image/svg+xml', @env.resolve_transform_type('image/svg+xml', 'text/yaml, image/svg+xml, image/png') assert_equal 'image/png', @env.resolve_transform_type('image/svg+xml', 'text/yaml, image/png, image/svg+xml') refute @env.resolve_transform_type('image/svg+xml', 'text/yaml') refute @env.resolve_transform_type(nil, 'image/svg+xml') refute @env.resolve_transform_type(nil, nil) end test "resolve multistep transform type for svg" do noop = proc {} assert_equal 'test/svg', @env.resolve_transform_type('test/svg', 'test/svg') assert_equal 'test/png', @env.resolve_transform_type('test/png', 'test/png') assert_equal 'test/jpg', @env.resolve_transform_type('test/jpg', 'test/jpg') assert_equal 'test/gif', @env.resolve_transform_type('test/gif', 'test/gif') assert_equal 'test/tif', @env.resolve_transform_type('test/tif', 'test/tif') assert_equal 'test/jif', @env.resolve_transform_type('test/jif', 'test/jif') refute @env.resolve_transform_type('test/svg', 'test/png') refute @env.resolve_transform_type('test/svg', 'test/jpg') refute @env.resolve_transform_type('test/svg', 'test/gif') refute @env.resolve_transform_type('test/svg', 'test/tif') refute @env.resolve_transform_type('test/svg', 'test/jif') @env.register_transformer 'test/svg', 'test/png', noop assert_equal 'test/png', @env.resolve_transform_type('test/svg', 'test/png') refute @env.resolve_transform_type('test/svg', 'test/jpg') refute @env.resolve_transform_type('test/svg', 'test/gif') refute @env.resolve_transform_type('test/svg', 'test/tif') refute @env.resolve_transform_type('test/svg', 'test/jif') @env.register_transformer 'test/svg', 'test/jpg', noop assert_equal 'test/png', @env.resolve_transform_type('test/svg', 'test/png') assert_equal 'test/jpg', @env.resolve_transform_type('test/svg', 'test/jpg') refute @env.resolve_transform_type('test/svg', 'test/gif') refute @env.resolve_transform_type('test/svg', 'test/tif') refute @env.resolve_transform_type('test/svg', 'test/jif') refute @env.resolve_transform_type('test/png', 'test/jpg') @env.register_transformer 'test/jpg', 'test/gif', noop assert_equal 'test/png', @env.resolve_transform_type('test/svg', 'test/png') assert_equal 'test/jpg', @env.resolve_transform_type('test/svg', 'test/jpg') assert_equal 'test/gif', @env.resolve_transform_type('test/jpg', 'test/gif') assert_equal 'test/gif', @env.resolve_transform_type('test/svg', 'test/gif') refute @env.resolve_transform_type('test/svg', 'test/tif') refute @env.resolve_transform_type('test/svg', 'test/jif') refute @env.resolve_transform_type('test/png', 'test/jpg') refute @env.resolve_transform_type('test/png', 'test/gif') @env.register_transformer 'test/gif', 'test/tif', noop assert_equal 'test/png', @env.resolve_transform_type('test/svg', 'test/png') assert_equal 'test/jpg', @env.resolve_transform_type('test/svg', 'test/jpg') assert_equal 'test/gif', @env.resolve_transform_type('test/jpg', 'test/gif') assert_equal 'test/gif', @env.resolve_transform_type('test/svg', 'test/gif') assert_equal 'test/tif', @env.resolve_transform_type('test/gif', 'test/tif') assert_equal 'test/tif', @env.resolve_transform_type('test/jpg', 'test/tif') assert_equal 'test/tif', @env.resolve_transform_type('test/svg', 'test/tif') refute @env.resolve_transform_type('test/svg', 'test/jif') refute @env.resolve_transform_type('test/png', 'test/jpg') refute @env.resolve_transform_type('test/png', 'test/gif') refute @env.resolve_transform_type('test/png', 'test/tif') @env.register_transformer 'test/tif', 'test/jif', noop assert_equal 'test/png', @env.resolve_transform_type('test/svg', 'test/png') assert_equal 'test/jpg', @env.resolve_transform_type('test/svg', 'test/jpg') assert_equal 'test/gif', @env.resolve_transform_type('test/jpg', 'test/gif') assert_equal 'test/gif', @env.resolve_transform_type('test/svg', 'test/gif') assert_equal 'test/tif', @env.resolve_transform_type('test/gif', 'test/tif') assert_equal 'test/tif', @env.resolve_transform_type('test/jpg', 'test/tif') assert_equal 'test/tif', @env.resolve_transform_type('test/svg', 'test/tif') assert_equal 'test/jif', @env.resolve_transform_type('test/jpg', 'test/jif') assert_equal 'test/jif', @env.resolve_transform_type('test/gif', 'test/jif') assert_equal 'test/jif', @env.resolve_transform_type('test/tif', 'test/jif') assert_equal 'test/jif', @env.resolve_transform_type('test/svg', 'test/jif') refute @env.resolve_transform_type('test/png', 'test/jpg') refute @env.resolve_transform_type('test/png', 'test/gif') refute @env.resolve_transform_type('test/png', 'test/tif') refute @env.resolve_transform_type('test/png', 'test/jif') end test "expand transform accepts" do assert_equal [ ['text/plain', 1.0], ['application/plain+ruby', 0.8] ], @env.expand_transform_accepts(@env.parse_q_values('text/plain')) assert_equal [ ['application/javascript', 1.0], ['application/ecmascript-6', 0.8], ['text/coffeescript', 0.8], ['text/eco', 0.8], # TODO: Extra step transform should be weighted down ['text/ejs', 0.8], # TODO: Extra step transform should be weighted down ['application/javascript+function', 0.8], ['application/ecmascript-6+ruby', 0.8], ['application/javascript+ruby', 0.8], ['application/coffeescript+ruby', 0.8], ["application/eco+ruby", 0.8], ["application/ejs+ruby", 0.8], ['text/mustache', 0.8], # TODO: Extra step transform should be weighted down ['text/x-handlebars-template', 0.8], # TODO: Extra step transform should be weighted down ['application/dart', 0.8] ], @env.expand_transform_accepts(@env.parse_q_values('application/javascript')) assert_equal [['image/png', 1.0], ['image/svg+xml', 0.8]], @env.expand_transform_accepts(@env.parse_q_values('image/png')) end test "compose transformers" do @env.register_transformer "image/svg", "image/png", proc { |input| { data: input[:data] + ",svg->png" } } @env.register_transformer "image/png", "image/gif", proc { |input| { data: input[:data] + ",png->gif" } } data = @env.config[:transformers]['image/svg']['image/png'].call(data: '') assert_equal({data: ",svg->png"}, data) data = @env.config[:transformers]['image/svg']['image/gif'].call(data: '') assert_equal({data: ",svg->png,png->gif"}, data) assert_raises(Sprockets::ArgumentError) do @env.compose_transformers(nil, ["image/svg"], nil, nil) end assert_raises(Sprockets::ArgumentError) do @env.compose_transformers(Hash.new { {} }, ["image/svg", "image/jif"], nil, nil) end end end sprockets-4.2.1/test/test_uglifier_compressor.rb000066400000000000000000000020661447572140400221740ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/uglifier_compressor' class TestUglifierCompressor < Minitest::Test def setup @env = Sprockets::Environment.new @env.append_path File.expand_path("../fixtures", __FILE__) end def test_compress_javascript input = { environment: @env, load_path: File.expand_path("../fixtures", __FILE__), filename: File.expand_path("../fixtures/file.js", __FILE__), content_type: 'application/javascript', data: "function foo() {\n return true;\n}", cache: Sprockets::Cache.new, metadata: { map: { "version" => 3, "file" => "test/file.js", "mappings" => "AAAA", "sources" => ["file.js"], "names" => [] } } } output = "function foo(){return!0}" result = Sprockets::UglifierCompressor.call(input) assert_equal output, result[:data] end def test_cache_key assert Sprockets::UglifierCompressor.cache_key end end sprockets-4.2.1/test/test_uri_tar.rb000066400000000000000000000056551447572140400175660ustar00rootroot00000000000000# frozen_string_literal: true require 'sprockets_test' class TestURITar < Sprockets::TestCase def setup @fake_env = Class.new do include Sprockets::PathUtils attr_accessor :root end.new end test "works with nix" do skip "Only runs on nix" if File::ALT_SEPARATOR uri = "/Sites/sprockets/test/fixtures/paths/application.css?type=text/css" @fake_env.root = "/Different/path" tar = Sprockets::URITar.new(uri, @fake_env) assert_equal uri, tar.expand assert_equal uri, tar.compress assert_equal uri, tar.compressed_path uri = "file:///Sites/sprockets/test/fixtures/paths/application.css?type=text/css" @fake_env.root = "/Sites/sprockets" tar = Sprockets::URITar.new(uri, @fake_env) assert_equal uri, tar.expand assert_equal Sprockets::URITar.new(tar.compress, @fake_env).expand, uri assert_equal "test/fixtures/paths/application.css?type=text/css", tar.compressed_path assert_equal "file://test/fixtures/paths/application.css?type=text/css", tar.compress assert_equal Sprockets::URITar.new(tar.compress, @fake_env).compress, tar.compress assert_equal Sprockets::URITar.new(tar.expand, @fake_env).compress, tar.compress uri = "/Sites/sprockets/test/fixtures/paths/application.css?type=text/css" @fake_env.root = "/Sites/sprockets" tar = Sprockets::URITar.new(uri, @fake_env) assert_equal uri, tar.expand assert_equal "test/fixtures/paths/application.css?type=text/css", tar.compressed_path assert_equal "test/fixtures/paths/application.css?type=text/css", tar.compress end test "works with windows" do skip "Only runs on windows" unless File::ALT_SEPARATOR uri = "C:/Sites/sprockets/test/fixtures/paths/application.css?type=text/css" @fake_env.root = "C:/Different/path" tar = Sprockets::URITar.new(uri, @fake_env) assert_equal uri, tar.expand assert_equal uri, tar.compress assert_equal uri, tar.compressed_path uri = "file:///C:/Sites/sprockets/test/fixtures/paths/application.css?type=text/css" @fake_env.root = "C:/Sites/sprockets" tar = Sprockets::URITar.new(uri, @fake_env) assert_equal uri, tar.expand assert_equal Sprockets::URITar.new(tar.compress, @fake_env).expand, uri assert_equal "test/fixtures/paths/application.css?type=text/css", tar.compressed_path assert_equal "file://test/fixtures/paths/application.css?type=text/css", tar.compress assert_equal Sprockets::URITar.new(tar.compress, @fake_env).compress, tar.compress assert_equal Sprockets::URITar.new(tar.expand, @fake_env).compress, tar.compress uri = "C:/Sites/sprockets/test/fixtures/paths/application.css?type=text/css" @fake_env.root = "C:/Sites/sprockets" tar = Sprockets::URITar.new(uri, @fake_env) assert_equal uri, tar.expand assert_equal "test/fixtures/paths/application.css?type=text/css", tar.compressed_path assert_equal "test/fixtures/paths/application.css?type=text/css", tar.compress end end sprockets-4.2.1/test/test_uri_utils.rb000066400000000000000000000216721447572140400201350ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/uri_utils' class TestURIUtils < Minitest::Test include Sprockets::URIUtils DOSISH = File::ALT_SEPARATOR != nil DOSISH_DRIVE_LETTER = File.dirname("A:") == "A:." DOSISH_UNC = File.dirname("//") == "//" def test_split_uri parts = split_uri("https://josh:Passw0rd1@github.com:433/sstephenson/sprockets/issues?author=josh#issue1") assert_equal ["https", "josh:Passw0rd1", "github.com", "433", nil, "/sstephenson/sprockets/issues", nil, "author=josh", "issue1"], parts end def test_join_uri assert_equal "https://josh:Passw0rd1@github.com:433/sstephenson/sprockets/issues?author=josh#issue1", join_uri("https", "josh:Passw0rd1", "github.com", "433", nil, "/sstephenson/sprockets/issues", nil, "author=josh", "issue1") end def test_inverse_uri_functions [ "http://github.com", "http://github.com:8080", "https://github.com/", "https://github.com/home", "https://github.com#logo", "https://josh:Passw0rd1@github.com:433/sstephenson/sprockets/issues?author=josh#issue1", "urn:md5:68b329da9893e34099c7d8ad5cb9c940", ].each do |uri| assert parts = split_uri(uri) assert_equal uri, join_uri(*parts) end end def test_split_file_uri parts = split_file_uri("file://localhost/etc/fstab") assert_equal ['file', 'localhost', '/etc/fstab', nil], parts parts = split_file_uri("file:///etc/fstab") assert_equal ['file', '', '/etc/fstab', nil], parts parts = split_file_uri("file:///usr/local/bin/ruby%20on%20rails") assert_equal ['file', '', '/usr/local/bin/ruby on rails', nil], parts parts = split_file_uri("file:///usr/local/var/github/app/assets/javascripts/application.js") assert_equal ['file', '', '/usr/local/var/github/app/assets/javascripts/application.js', nil], parts if DOSISH parts = split_file_uri("file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc") assert_equal ['file', '', 'C:/Documents and Settings/davris/FileSchemeURIs.doc', nil], parts parts = split_file_uri("file:///D:/Program%20Files/Viewer/startup.htm") assert_equal ['file', '', 'D:/Program Files/Viewer/startup.htm', nil], parts parts = split_file_uri("file:///C:/Program%20Files/Music/Web%20Sys/main.html?REQUEST=RADIO") assert_equal ['file', '', 'C:/Program Files/Music/Web Sys/main.html', 'REQUEST=RADIO'], parts end end def test_join_uri_path assert_equal "file://localhost/etc/fstab", join_file_uri('file', 'localhost', '/etc/fstab', nil) assert_equal "file:///etc/fstab", join_file_uri('file', nil, '/etc/fstab', nil) assert_equal "file:///usr/local/bin/ruby%20on%20rails", join_file_uri('file', nil, '/usr/local/bin/ruby on rails', nil) end def test_inverse_file_uri_functions uris = [ "file://localhost/etc/fstab", "file:///etc/fstab", "file:///usr/local/bin/ruby%20on%20rails", "file:///usr/local/var/github/app/assets/javascripts/application.js", "file:///usr/local/var/github/app/assets/javascripts/application.coffee?type=application/javascript" ] if DOSISH uris.concat([ "file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc", "file:///D:/Program%20Files/Viewer/startup.htm" ]) end uris.each do |uri| assert parts = split_file_uri(uri) assert_equal uri, join_file_uri(*parts) end end def test_validate assert valid_asset_uri?("file:///usr/local/var/github/app/assets/javascripts/application.js") if DOSISH assert valid_asset_uri?("file:///C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js") end refute valid_asset_uri?("http:///usr/local/var/github/app/assets/javascripts/application.js") refute valid_asset_uri?("/usr/local/var/github/app/assets/javascripts/application.js") end def test_validate_with_invalid_uri_error refute valid_asset_uri?("file:///[]") end def test_parse_file_paths assert_equal ["/usr/local/var/github/app/assets/javascripts/application.js", {}], parse_asset_uri("file:///usr/local/var/github/app/assets/javascripts/application.js") assert_equal ["/usr/local/var/github/app/assets/javascripts/foo bar.js", {}], parse_asset_uri("file:///usr/local/var/github/app/assets/javascripts/foo%20bar.js") if DOSISH assert_equal ["C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js", {}], parse_asset_uri("file:///C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js") end end def test_parse_query_params assert_equal ["/usr/local/var/github/app/assets/javascripts/application.coffee", {type: 'application/javascript'}], parse_asset_uri("file:///usr/local/var/github/app/assets/javascripts/application.coffee?type=application/javascript") assert_equal ["/usr/local/var/github/app/assets/stylesheets/users.css", {type: 'text/css', flag: true}], parse_asset_uri("file:///usr/local/var/github/app/assets/stylesheets/users.css?type=text/css&flag") assert_equal ["/usr/local/var/github/app/assets/views/users.html", {type: 'text/html; charset=utf-8'}], parse_asset_uri("file:///usr/local/var/github/app/assets/views/users.html?type=text/html;%20charset=utf-8") end def test_asset_uri_raise_erorr_when_invalid_uri_scheme assert_raises URI::InvalidURIError do parse_asset_uri("http:///usr/local/var/github/app/assets/javascripts/application.js") end end def test_build_file_path assert_equal "file:///usr/local/var/github/app/assets/javascripts/application.js", build_asset_uri("/usr/local/var/github/app/assets/javascripts/application.js") assert_equal "file:///usr/local/var/github/app/assets/javascripts/foo%20bar.js", build_asset_uri("/usr/local/var/github/app/assets/javascripts/foo bar.js") if DOSISH assert_equal "file:///C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js", build_asset_uri("C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js") end end def test_build_query_params assert_equal "file:///usr/local/var/github/app/assets/javascripts/application.coffee?type=application/javascript", build_asset_uri("/usr/local/var/github/app/assets/javascripts/application.coffee", type: 'application/javascript') assert_equal "file:///usr/local/var/github/app/assets/images/logo.svg?type=image/svg+xml", build_asset_uri("/usr/local/var/github/app/assets/images/logo.svg", type: 'image/svg+xml') assert_equal "file:///usr/local/var/github/app/assets/stylesheets/users.css?type=text/css&flag", build_asset_uri("/usr/local/var/github/app/assets/stylesheets/users.css", type: 'text/css', flag: true) assert_equal "file:///usr/local/var/github/app/assets/stylesheets/users.css?type=text/css", build_asset_uri("/usr/local/var/github/app/assets/stylesheets/users.css", type: 'text/css', flag: false) assert_equal "file:///usr/local/var/github/app/assets/stylesheets/users.css?type=css", build_asset_uri("/usr/local/var/github/app/assets/stylesheets/users.css", type: :css) assert_equal "file:///usr/local/var/github/app/assets/views/users.html?type=text/html;%20charset=utf-8", build_asset_uri("/usr/local/var/github/app/assets/views/users.html", type: 'text/html; charset=utf-8') end def test_raise_error_when_invalid_param_value assert_raises TypeError do build_asset_uri("/usr/local/var/github/app/assets/images/logo.png", encodings: ['gzip', 'deflate']) end end def test_parse_file_digest_uri assert_equal "/usr/local/var/github/app/assets/javascripts/application.js", parse_file_digest_uri("file-digest:///usr/local/var/github/app/assets/javascripts/application.js") assert_equal "/usr/local/var/github/app/assets/javascripts/foo bar.js", parse_file_digest_uri("file-digest:///usr/local/var/github/app/assets/javascripts/foo%20bar.js") if DOSISH assert_equal "C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js", parse_file_digest_uri("file-digest:///C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js") end end def test_build_file_digest_uri assert_equal "file-digest:///usr/local/var/github/app/assets/javascripts/application.js", build_file_digest_uri("/usr/local/var/github/app/assets/javascripts/application.js") assert_equal "file-digest:///usr/local/var/github/app/assets/javascripts/foo%20bar.js", build_file_digest_uri("/usr/local/var/github/app/assets/javascripts/foo bar.js") if DOSISH assert_equal "file-digest:///C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js", build_file_digest_uri("C:/Users/IEUser/Documents/github/app/assets/javascripts/application.js") end end def test_file_digest_raise_erorr_when_invalid_uri_scheme assert_raises URI::InvalidURIError do parse_file_digest_uri("http:///usr/local/var/github/app/assets/javascripts/application.js") end end end sprockets-4.2.1/test/test_utils.rb000066400000000000000000000134251447572140400172530ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/utils' class TestUtils < Minitest::Test include Sprockets::Utils def test_duplicable skip unless 0.class != Integer # Ruby < 2.4 objs = [nil, true, false, 1, "foo", :foo, [], {}] objs.each do |obj| begin obj.dup rescue TypeError refute duplicable?(obj), "can't dup: #{obj.inspect}" else assert duplicable?(obj), "can dup: #{obj.inspect}" end end end def test_hash_reassoc h = hash_reassoc({}.freeze, :foo) do |value| refute value nil end assert_equal({foo: nil}, h) assert h.frozen? h = hash_reassoc({}, :foo) do |value| refute value true end assert_equal({foo: true}, h) assert h.frozen? h = hash_reassoc({foo: 1}.freeze, :foo) do |value| assert_equal 1, value 2 end assert_equal({foo: 2}, h) assert h.frozen? h = hash_reassoc({foo: "bar".freeze}.freeze, :foo) do |value| assert_equal "bar", value refute value.frozen? "baz" end assert_equal({foo: "baz"}, h) assert h.frozen? assert h[:foo].frozen? h = hash_reassoc({foo: "bar".freeze}.freeze, :foo) do |value| assert_equal "bar", value refute value.frozen? value.sub!("r", "z") end assert_equal({foo: "baz"}, h) assert h.frozen? assert h[:foo].frozen? h = hash_reassoc({foo: {bar: "baz".freeze}.freeze}.freeze, :foo, :bar) do |value| assert_equal "baz", value refute value.frozen? "biz" end assert_equal({foo: {bar: "biz"}}, h) assert h.frozen? assert h[:foo].frozen? assert h[:foo][:bar].frozen? end def test_string_ends_with_semicolon assert string_end_with_semicolon?("var foo;") refute string_end_with_semicolon?("var foo") assert string_end_with_semicolon?("var foo;\n") refute string_end_with_semicolon?("var foo\n") assert string_end_with_semicolon?("var foo;\n\n") refute string_end_with_semicolon?("var foo\n\n") assert string_end_with_semicolon?(" var foo;\n \n") refute string_end_with_semicolon?(" var foo\n \n") assert string_end_with_semicolon?("\tvar foo;\n\t\n") refute string_end_with_semicolon?("\tvar foo\n\t\n") assert string_end_with_semicolon?("var foo\n;\n") refute string_end_with_semicolon?("var foo\n\n") end def test_concat_javascript_sources assert_equal "var foo;\n", apply_concat_javascript_sources("".freeze, "var foo;\n".freeze) assert_equal "\nvar foo;\n", apply_concat_javascript_sources("\n".freeze, "var foo;\n".freeze) assert_equal "var foo;\nvar bar;\n", apply_concat_javascript_sources("var foo;\n".freeze, "var bar;\n".freeze) assert_equal " var foo;\n", apply_concat_javascript_sources(" ".freeze, "var foo;\n".freeze) assert_equal "var foo;var bar;", apply_concat_javascript_sources("var foo".freeze, "var bar".freeze) assert_equal "var foo;var bar;", apply_concat_javascript_sources("var foo;".freeze, "var bar;".freeze) assert_equal "var foo;var bar;", apply_concat_javascript_sources("var foo".freeze, "var bar;".freeze) assert_equal "var foo;\nvar bar;", apply_concat_javascript_sources("var foo\n".freeze, "var bar;".freeze) end def apply_concat_javascript_sources(*args) args.reduce(+"", &method(:concat_javascript_sources)) end def test_post_order_depth_first_search m = [] m[11] = [4, 5, 10] m[4] = [2, 3] m[10] = [8, 9] m[2] = [0, 1] m[8] = [6, 7] assert_equal Set.new(0..11), dfs(11) { |n| m[n] } m = [] m[11] = [4, 5, 10] m[4] = [2, 3] m[3] = [1] m[5] = [1, 2] m[10] = [8, 9] m[2] = [0, 1] m[8] = [6, 7] m[6] = [5] assert_equal Set.new(0..11), dfs(11) { |n| m[n] } end def test_post_order_depth_first_find_all_paths m = [] m[0] = [1] m[1] = [2] m[2] = [3] m[3] = [4, 5] m[4] = [1] assert_equal [ [0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 5] ], dfs_paths([0]) { |n| m[n] || [] } assert_equal [ [1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 5] ], dfs_paths([1]) { |n| m[n] || [] } assert_equal [[5]], dfs_paths([5]) { |n| m[n] || [] } end module Functions module InstanceMethods def bar 2 end end include InstanceMethods def foo 1 end end module ScopedFunctions def foo 7 end def bar 8 end def baz 9 end end module OtherScopedFunctions def bar 3 end def baz 4 end end class Context include Functions end def test_module_include context = Context.new assert context.respond_to?(:foo) assert context.respond_to?(:bar) refute context.respond_to?(:baz) assert_equal 1, context.foo assert_equal 2, context.bar module_include(Functions, ScopedFunctions) do assert context.respond_to?(:foo) assert context.respond_to?(:bar) assert context.respond_to?(:baz) assert_equal 7, context.foo assert_equal 8, context.bar assert_equal 9, context.baz end assert context.respond_to?(:foo) assert context.respond_to?(:bar) refute context.respond_to?(:baz) assert_equal 1, context.foo assert_equal 2, context.bar module_include(Functions, OtherScopedFunctions) do assert context.respond_to?(:foo) assert context.respond_to?(:bar) assert context.respond_to?(:baz) assert_equal 1, context.foo assert_equal 3, context.bar assert_equal 4, context.baz end assert context.respond_to?(:foo) assert context.respond_to?(:bar) refute context.respond_to?(:baz) assert_equal 1, context.foo assert_equal 2, context.bar end end sprockets-4.2.1/test/test_yui_compressor.rb000066400000000000000000000017221447572140400211720ustar00rootroot00000000000000# frozen_string_literal: true require 'minitest/autorun' require 'sprockets/cache' require 'sprockets/yui_compressor' class TestYUICompressor < Minitest::Test def test_compress_javascript input = { content_type: 'application/javascript', data: "function foo() {\n return true;\n}", cache: Sprockets::Cache.new } output = "function foo(){return true};" begin assert_equal output, Sprockets::YUICompressor.call(input) rescue YUI::Compressor::RuntimeError skip "No Java runtime present" end end def test_compress_css input = { content_type: 'text/css', data: "h1 {\n color: red;\n}\n", cache: Sprockets::Cache.new } output = "h1{color:red}" begin assert_equal output, Sprockets::YUICompressor.call(input) rescue YUI::Compressor::RuntimeError skip "No Java runtime present" end end def test_cache_key assert Sprockets::YUICompressor.cache_key end end