Rack-based asset packaging system
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.
Install Sprockets from RubyGems:
$ gem install sprocketsOr include it in your project's Gemfile with Bundler:
gem 'sprockets', '~> 4.0'These are the major features in Sprockets 4.x
Read more about them by referencing Upgrading document
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.
If you are a library developer who is extending the functionality of Sprockets, see Extending Sprockets.
If you want to work on Sprockets or better understand how it works read How Sprockets Works
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 Rails asset pipeline), 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.
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.coffeeNote: 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.jseven if the file on disk is a coffeescript file, since the asset it will generate is javascript.
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:
//= require jquery
$().ready({
// my custom code here
})The directive processor understands comment blocks in three formats:
/* Multi-line comment blocks (CSS, SCSS, JavaScript)
*= require foo
*/// Single-line comment blocks (SCSS, JavaScript)
//= require foo# Single-line comment blocks (CoffeeScript)
#= require fooNote: 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 - Add the contents of a file to currentrequire_self - Change order of where current contents are concatenated to currentrequire_directory - Add contents of each file in a folder to currentrequire_tree - Add contents of all files in all directories in a path to currentlink - Make target file compile and be publicly available without adding contents to currentlink_directory - Make target directory compile and be publicly available without adding contents to currentlink_tree - Make target tree compile and be publicly available without adding contents to currentdepend_on - Recompile current file if target has changeddepend_on_directory - Recompile current file if any files in target directory have changedstub - Ignore target fileYou can see what each of these does below.
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.scssThen 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 to embed content in your doc before running the sass processor. To accomplish this you would need to name your file
application.scss.erbProcessors 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 an 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).
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
//= require_directory my_javascriptThe files in that directory will be loaded in alphabetical order. If the directory looks like this:
$ ls -1 my_javascript/
alpha.js
beta.js
jquery.jsThen 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 recommended to use require_directory with files that are ordering dependent. You can either require individual files manually:
//= require jquery
//= require alpha
//= require betaOr you can use index files to proxy your 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. 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:
Instead you would need to use:
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 .
//= 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:
//= require foo.jsThen create a file foo/index.js that requires all the files in that folder in any order you want using relative references:
//= require ./foo.min.js
//= require ./foo-ui.jsNow 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.
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
//= require a.js
//= require b.js
# ...
//= require z.jsThe 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.
Directives take a path or a path to a file. Paths for directive can be relative to the current file, for example:
//= require ../foo.jsThis would load a file named foo.js from one directory up. However this isn't required if foo.js is on one of Sprocket's load paths. You can simply use
//= require foo.jsWithout 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:
//= require sub/path/foo.jsYou 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 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:
var a = "A";and a b.js;
var b = "B";Then you could require both of these in an application.js
//= require a.js
//= require b.jsWhich would generate one concatenated file:
var a = "A";
var b = "B";`r
No open issues yet, or sync has not completed.