UNPKG

14.1 kBMarkdownView Raw
1# Asset Smasher
2
3Asset pre-processor, merger, and compressor for Node.js
4
5- [Structuring Your Assets](#structure-assets)
6 - [Manifest Files](#manifest-files)
7- [Using via Command Line](#command-line)
8 - [Helpers](#cli-helpers)
9 - [Plugins](#cli-plugins)
10- [Using via Express Middleware](#express-middleware)
11- [Using via Programmatic Interface](#programmatic-interface)
12- [Transformer Notes](#transformer-notes)
13 - [LESS](#tn-less)
14 - [ejs](#tn-ejs)
15 - [dust and Handlebars](#tn-dust-hbs)
16
17## Overview
18
19Asset Smasher is a command-line tool, express middleware, and programmatic interface for:
20
21- Pre-processing and transforming files down to plain JavaScript and CSS.
22 - `.coffee` - Compile CoffeeScript into JavaScript
23 - `.ejs` - Run a file through EJS (e.g. to populate configuration parameters into a JavaScript file)
24 - `.less` - Compile Less into CSS
25 - `.hbs` - Precompile Handlebars templates into JavaScript files that register them with `Handlebars.templates`.
26 - `.dust` - Precompile Dust templates into JavaScript files that register them for use with `dust.render`.
27 - Processors can be chained together. E.g `test.js.hbs.ejs` (run Handlebars template through EJS, then compile it)
28 - Additional processors can be plugged in.
29- Merging files together using Manifest files (`.mf`) with dependency management directives similar to Sprockets.
30 - `require` - Require a single file
31 - `require_dir` - Require all the files in a specific directory
32 - `require_tree` - Require all the files in a specific directory (and subdirectories)
33- Compressing, gzipping, and generating hashed file names.
34 - Compress JavaScript files with `uglify-js`
35 - Compress LESS during LESS preprocessing
36 - Generate Gzipped versions of files
37 - Include a MD5 hash of the file's contents in the file name. `myAsset.js` -> `myAsset-c89cba7b7df028e65cb01d86f4d27077.js`
38 - `asset_path` helper that can be used to reference the hashed name.
39
40It's released under the [MIT](http://en.wikipedia.org/wiki/MIT_License) license.
41
42## <a name="structure-assets"></a> Structuring Your Assets
43
44Asset Smasher has the concept of "asset paths". These are locations in which your asset files will be located, and from which any relative asset paths will be rooted to.
45
46The simplest structure has one asset path.
47
48E.g.
49
50 Asset Paths
51 -----------
52 - app
53
54 File Structure
55 --------------
56 app/
57 js/
58 css/
59 images/
60
61A more complicated structure might be
62
63 Asset Paths
64 -----------
65 - app
66 - lib
67 - vendor
68
69 File Structure
70 --------------
71 app/
72 js/
73 css/
74 images/
75 lib/
76 js/
77 css/
78 images/
79 vendor/
80 js/
81 css/
82 images/
83
84Both of these examples will result in a compiled structure of
85
86 js/
87 css/
88 images/
89
90### <a name="manifest-files"></a> Manifest Files
91
92Manifest (`.mf`) files are used to merge many assets into a single resulting file. The file should be named with the resulting file type before the `.mf` extension (e.g. `manifest.css.mf` or `manifest.js.mf`. *Manifest files can `require` other manifest files*
93
94A simple manifest file might look like
95
96 # A comment here
97 require "./one.js"
98 require_dir "./subdir1"
99 #
100 # Another comment
101 require_tree "./subdir2"
102
103**Directives:**
104
105<table border="1" cellpadding="5" cellspacing="0" width="100%">
106 <thead>
107 <tr><th width="15%">Directive</th><th width="85%">Description</th></tr>
108 </thead>
109 <tbody>
110 <tr>
111 <td><code>require "[path]"</code></td>
112 <td>
113 <strong>Include a single file</strong>
114 <ul>
115 <li>
116 If the path starts with <code>"/"</code>, <code>"../"</code>, or <code>"./"</code>, process and include the specified file. The file <em>must</em> be
117 inside one of the configured asset paths.
118 </li>
119 <li>
120 If the path does not start with <code>"/"</code>, <code>"../"</code>, or <code>"./"</code>, the file will be searched for in all of the configured
121 asset paths. E.g. if there are asset paths <code>one</code> and <code>two</code> defined, <code>require "js/test.js"</code>
122 will look for <code>one/js/test.js</code> and then <code>two/js/test.js</code> stopping when it finds a matching file.
123 </li>
124 <li>
125 The filename part of the path does not have to include the whole extension. E.g <code>require "test"</code>
126 finds the first file that matches the name in the asset paths (for example <code>test.js.ejs</code>)
127 </li>
128 </ul>
129 </td>
130 </tr>
131 <tr>
132 <td><code>require_dir "[path]"</code></td>
133 <td>
134 <strong>Include all the files in a directory</strong>
135 <ul>
136 <li>
137 The path must be absolute, or relative to the current directory. E.g. you can do <code>require_dir "../some/other/dir"</code>
138 but not <code>require_dir "somedir"</code>
139 </li>
140 <li>
141 If using absolute paths, or <code>".."</code> in your paths, the resulting directory needs to be inside one of the configured asset paths.
142 </li>
143 <li>
144 Make sure the directory only contains assets of the type you want. E.g. for <code>myManifest.js.mf</code>, the dir required had better
145 only contain javascript files, or else bad things will happen.
146 </li>
147 </ul>
148 </td>
149 </tr>
150 <tr>
151 <td><code>require_tree "[path]"</code></td>
152 <td>
153 <strong>Include the files in a directory recursively</strong>
154 <ul>
155 <li>The rules for <code>require_tree</code> are the same as the rules for <code>require_dir</code></li>
156 </ul>
157 </td>
158 </tr>
159 </tbody>
160</table>
161
162## <a name="command-line"></a> Using via Command-Line
163
164Use `npm install -g asset-smasher` to install the `asset-smasher` command-line tool globally.
165
166 asset-smasher --help
167
168 Usage: asset-smasher [options] <output dir>
169
170 Options:
171
172 -h, --help output usage information
173 -V, --version output the version number
174 --compress compress/minify the generated files
175 --hash generate versions of the files with md5 hashes in the name
176 --gzip generate gzipped versions of the compiled files
177 --hashVersion <version> invalidate all assets without changing file contents [1.0]
178 --only <pattern,...> only process the files matching these glob patterns (relative to any of the paths) [**/*]
179 --paths <path,...> list of paths to look for assets [.]
180 --prefix <prefix> prefix to append to logical paths when constructing urls. use if output dir is not served from the root of your web app []
181 --helpers <js_file> a .js module of helper functions require()s to expose to transforms []
182 --plugins <js_file> a .js plugin module []
183
184 If --only is not specified, *all* files in the --paths will be processed.
185
186 Examples:
187
188 Compile all assets in the current directory to /home/me/compiledAssets
189
190 $ asset-smasher /home/me/compiledAssets
191
192 Something similar to what the Rails asset pipeline does by default
193
194 $ asset-smasher --compress --hash --gzip --prefix=/assets \
195 --paths=./js,./css,./images \
196 --only **/*.{jpg,gif,png},application.js.mf,application.css.mf ./public/assets
197
198 Compile assets, providing some custom helpers to the transformation
199
200 $ asset-smasher --helpers helpers.js output
201
202### <a name="cli-helpers"></a> Helpers
203
204There is a built-in `asset_path` helper that can be used to get the "real" (i.e. with hashed file name) path of an asset. E.g. `asset_path('css/myFile.css')` might return `'/assets/css/myFile-c89cba7b7df028e65cb01d86f4d27077.css`.
205
206Some transformers (e.g. the `.ejs` one) take in a set of local variables that they can use during transformation. You can pass in the path to a JavaScript module whose exports will be included in this set of variables.
207
208You can use this, for example, to set configuration parameters in your JS files:
209
210**helper.js**
211
212 exports.serviceUrl = 'http://my.service/';
213
214**config.js.ejs**
215
216 //...
217 var serviceUrl = '<%= serviceUrl %>';
218 var cssLocation = '<%= asset_path('css/myFile.css') %>';
219 //...
220
221**Execution**
222
223 $ asset-smasher --helpers helper.js --only config.js.ejs,css/myFile.css .
224 $ cat config.js
225 var serviceUrl = 'http://my.service/';
226 var cssLocation = '/assets/css/myFile-c89cba7b7df028e65cb01d86f4d27077.css';
227
228
229### <a name="cli-plugins"></a> Plugins
230
231If there's a type of file you want to pre-process that is not natively supported by Asset Smasher, you can add it using a plugin file.
232
233*TODO: How to add additional transformers via a plugin file.*
234
235## <a name="express-middleware"></a> Using via Express Middleware
236
237Asset smasher exposes an `express` middleware that can:
238
239- Serve your assets un-merged/mangled in development mode.
240- Serve precompiled assets (with hashed file names) in production mode.
241
242The middleware takes in the same arguments as the `Smasher` constructor, with a few extras:
243
244- `serve` - boolean whether the middleware should serve the asset files. Usualy set this to `true` in development, `false` in production
245- `assetMapLocation` - path to the `map.json` generated by the command-line `asset-smasher` util. This allows the helper methods to determine what the hashed file names were
246
247The middleware exposes two helpers to your views:
248
249- `js_asset(logicalPath)` - Render a `<script>` tag for the specified JS asset. When `serve` is true, this will "explode" manifests and write out a separate `<script>` for each required file. This makes debugging much easier.
250- `css_asset(logicalPath)` - Render a `<link>` tag for the specified CSS asset. Same thing happens when `serve` is true.
251
252### Example
253
254 var assetSmasher = require('asset-smasher');
255
256**Middleware config (Dev)**
257
258 app.use(assetSmasher.middleware({
259 serve: true,
260 paths: [path.join(__dirname, 'assetDir1'), path.join(__dirname, 'assetDir2')],
261 prefix: '/assets',
262 outputTo: path.join(__dirname, 'tmp')
263 }));
264
265**Middleware config (Prod)**
266
267 app.use(assetSmasher.middleware({
268 serve: false,
269 prefix: '/assets',
270 assetMapLocation: path.join(__dirname, 'public/assets/map.json')
271 }));
272
273**View (ejs here, but could be others)**
274
275 <!DOCTYPE html>
276 <html>
277 <head>
278 <title>Test</title>
279 <%- css_asset('application.css') %>
280 <%- js_asset('application.js') %>
281 </head>
282 <body>
283 This is a test
284 </body>
285 </html>
286
287## <a name="programmatic-interface"></a> Using via Programmatic Interface
288
289You can invoke Asset Smasher programmatically by `require`ing it. You can also plug in additional transformers this way.
290
291The `Smasher` object has the following methods:
292
293 - `compileAssets(cb)` - Find and compile all the assets.
294 - `compileSingleAsset(assetFilePath, cb)` - Compile a single asset (assetFilePath is the actual path to the file, not a logical path)
295 - `findAssets(cb)` - Find, but don't compile the assets. Good for determining dependency graph without compiling.
296 - `getAssetByLogicalPath(logicalPath)` - Get information about an asset by its logical path. Only call this after finding/compiling assets.
297 - `getHashedFileMapping()` - When `hash` is true, this returns a mapping of logical path to "hashed" logical path. This object is what the command-line tool outputs to `map.json`. Only call this after finding/compiling assets.
298 - `getRequiredLogicalPathsFor(asset)` - Get the logical paths of the assets that should be merged into the specified asset (populated for `.mf` files). Only call this after finding/compiling assets.
299 - `getProcessingOrderLogicalPaths()` - Get a list of the order in which assets should be processed in order to satisfy all dependencies. Only call this after finding/compiling assets.
300
301The `Asset` object returned by `getAssetByLogicalPath` has the following properties:
302 - `logicalPath` - The logical path
303 - `hashedPath` - If `hash` is true, the hashed filename path, otherwise the same as `logicalPath`
304 - `assetFilePath` - The full path to the actual source asset
305 - `compiled` - Whether the asset has been compiled
306 - `compiledAssetFilePath` - The full path to the compiled asset file
307
308**Example**
309
310 var assetSmasher = require('asset-smasher');
311 var Smasher = assetSmasher.Smasher;
312
313 // Plug in a custom transformer
314 assetSmasher.transforms['MyAwesomeFormat'] = require('myAwesomeFormatTransformer');
315
316 var sm = new Smasher({
317 paths:['/path/one', '/path/two'],
318 only:['**/*.{jpg,gif,png}', 'application.js.mf', 'application.css.mf'],
319 prefix:'/assets',
320 compress:true,
321 hash:true,
322 hashVersion:'1.0',
323 gzip:true,
324 outputTo:__dirname + '/public/assets',
325 helpers:{
326 my: 'helper',
327 another: 'helper'
328 }
329 });
330 sm.compileAssets(function(err) {
331 if(err) {
332 console.log('An error occurred', err);
333 } else {
334 console.log('Compilation done!');
335 }
336 });
337
338## <a name="transformer-notes"></a> Transformer Notes
339
340### <a name="tn-less"></a> LESS
341
342- When the `compress` option is true, the compression is done directly via the `less` compiler
343- Any `@include` paths are *relative to the path that the file is in*.
344- Any `@include`d files will *not* be processed individually by Asset Smasher (i.e. you can't `@include` a LESS file that is preprocessed by ejs)
345
346### <a name="tn-ejs"></a> ejs
347
348- Any registered helpers will be exposed as global variables to the `ejs` transform.
349- The built-in `asset_paths` helper can be used here.
350
351### <a name="tn-dust-hbs"></a> dust and Handlebars
352
353- The name of the template will be the template's "logical path" (minus the asset path it is in), minus the `.js.dust` or `.js.hbs` file extension.
354 - E.g. `/my/templates/test.js.dust`'s template name will be `test` (assuming `/my/templates` is the asset path)