1 | # AssetGraph
|
2 |
|
3 | [![NPM version](https://badge.fury.io/js/assetgraph.svg)](http://badge.fury.io/js/assetgraph)
|
4 | [![Build Status](https://travis-ci.org/assetgraph/assetgraph.svg?branch=master)](https://travis-ci.org/assetgraph/assetgraph)
|
5 | [![Coverage Status](https://img.shields.io/coveralls/assetgraph/assetgraph.svg)](https://coveralls.io/r/assetgraph/assetgraph?branch=master)
|
6 | [![Dependency Status](https://david-dm.org/assetgraph/assetgraph.svg)](https://david-dm.org/assetgraph/assetgraph)
|
7 |
|
8 | AssetGraph is an extensible, <a href="http://nodejs.org/">node.js</a>-based
|
9 | framework for manipulating and optimizing web pages and web
|
10 | applications. The main core is a dependency graph model of your entire website, where all assets are treated as first class citizens. It can automatically dicsover assets based on your declarative code, reducing the configuration needs to a minimum.
|
11 |
|
12 | If you just want to get started with the basics, read [Peter Müller - Getting started with Assetgraph](http://mntr.dk/2014/getting-started-with-assetgraph/).
|
13 |
|
14 | If you are looking for a prepackaged build system take a look at <a
|
15 | href="https://github.com/assetgraph/assetgraph-builder">Assetgraph-builder</a>.
|
16 |
|
17 | ## Tools built with AssetGraph
|
18 |
|
19 | - [assetgraph-builder](https://www.npmjs.com/package/assetgraph-builder) - A static web page build system that post-processes your website with extremely little configuration
|
20 | - [subfont](https://www.npmjs.com/package/subfont) - A tool that supercharges your webfont loading by automatically applying all best practice loading techniques and generating optimal font subsets
|
21 | - [hyperlink](https://www.npmjs.com/package/hyperlink) - A link checker tool that will ensure all your internal and external links are intact and up to date
|
22 | - [seespee](https://www.npmjs.com/package/seespee) - A Content-Security Policy generator. Point it at a webpage and it will tell you what policy you need as a minimum
|
23 | - [trackingdog](https://github.com/papandreou/trackingdog) - cli for finding the original source location of a line+column in a generated file, utilizing the source map
|
24 |
|
25 | # Assets and relations
|
26 |
|
27 | All web build tools, even those that target very specific problems,
|
28 | have to get a bunch of boring stuff right just to get started, such
|
29 | as loading files from disc, parsing and serializing them, charsets,
|
30 | inlining, finding references to other files, resolution of and
|
31 | updating urls, etc.
|
32 |
|
33 | The observation that inspired the project is that most of these
|
34 | tasks can be viewed as graph problems, where the nodes are the
|
35 | assets (HTML, CSS, images, JavaScript...) and the edges are the
|
36 | relations between them, e.g. anchor tags, image tags, favorite
|
37 | icons, css background-image properties and so on.
|
38 |
|
39 | ![An example illustration of an asset graph representing a web page](illustration.png)
|
40 |
|
41 | An AssetGraph object is a collection of assets (nodes) and the
|
42 | relations (edges) between them. It's a basic data model that allows
|
43 | you to populate, query, and manipulate the graph at a high level of
|
44 | abstraction. For instance, if you change the url of an asset, all
|
45 | relations pointing at it are automatically updated.
|
46 |
|
47 | Additionally, each individual asset can be inspected and massaged
|
48 | using a relevant API: <a
|
49 | href="https://github.com/tmpvar/jsdom">jsdom</a> for HTML, <a href="https://github.com/postcss/postcss">PostCSS</a> for CSS, and an <a
|
50 | href="http://esprima.org/">Esprima</a> AST for Javascript.
|
51 |
|
52 | AssetGraph represents inline assets the same way as non-inline ones,
|
53 | so eg. inline scripts, stylesheets, and images specified as `data:`
|
54 | urls are also first-class nodes in the graph. This means that you
|
55 | don't need to dig into the HTML of the containing asset to manipulate
|
56 | them. An extreme example would be an Html asset with a conditional
|
57 | comment with an inline stylesheet with an inline image, which are
|
58 | modelled as 4 separate assets:
|
59 |
|
60 | ```html
|
61 | <!DOCTYPE html>
|
62 | <html>
|
63 | <head>
|
64 | <!--[if !IE]> -->
|
65 | <style type="text/css">
|
66 | body {
|
67 | background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);
|
68 | }
|
69 | </style>
|
70 | <!-- <![endif]-->
|
71 | </head>
|
72 | <body></body>
|
73 | </html>
|
74 | ```
|
75 |
|
76 | These are some of the supported assets and associated relation types:
|
77 |
|
78 | #### HTML
|
79 |
|
80 | `<a>`, `<link rel="stylesheet|shortcut icon|fluid-icon|alternate|serviceworker">`, `<script>`, `<style>`,
|
81 | `<html manifest="...">` `<img>`, `<video>`, `<audio>`, `<applet>`,
|
82 | `<embed>`, `<esi:include>`, `<iframe>`, `<svg>`, `<meta property="og:...">`
|
83 |
|
84 | #### SVG
|
85 |
|
86 | `<style>`, inline `style=...` attributes, event handlers, `<?xml-stylesheet href=...>`, `<font-face-src>`
|
87 |
|
88 | #### CSS
|
89 |
|
90 | `//# sourceMappingURL=...`, `background-image: url(...)`, `@import url(...)`, `behavior: url(...)`,
|
91 | `filter: AlphaImageLoader(src='...')`, `@font-face { src: url(...) }`
|
92 |
|
93 | #### JavaScript
|
94 |
|
95 | `//# sourceMappingURL=...`, homegrown `'foo/bar.png'.toString('url')` syntax for referencing external files
|
96 |
|
97 | #### Web manifest
|
98 |
|
99 | Icon urls, `related_applications`, `start_url`, etc.
|
100 |
|
101 | #### Cache manifest (appcache)
|
102 |
|
103 | Entries in the `CACHE`, `NETWORK` and `FALLBACK` sections
|
104 |
|
105 | #### JSON, XML, PNG, GIF, JPEG, ICO
|
106 |
|
107 | (none)
|
108 |
|
109 | # Features
|
110 |
|
111 | - Build an AssetGraph programmatically or load it from disk or a
|
112 | remote server via http.
|
113 | - Find explicit dependencies between JavaScript and CSS and roll them
|
114 | out as `<script>` and `<link rel='stylesheet'>` tags in your
|
115 | HTML.
|
116 | - Bundle and inline CSS and JavaScript.
|
117 | - Create a cache manifest with references to all the assets your web
|
118 | app needs to be usable offline.
|
119 | - Move all CSS, JavaScript, image assets etc. to a static dir and
|
120 | rename them to md5.extension so the web server can be configured to
|
121 | set a far-future Cache-Control.
|
122 | - Help getting your static assets on a CDN by allowing you to easily
|
123 | rewrite all references to them.
|
124 | - Use Graphviz to visualize your dependencies at any step.
|
125 | - Using the separate <a
|
126 | href="https://github.com/assetgraph/assetgraph-sprite">assetgraph-sprite
|
127 | transform</a>: Optimize CSS background images by creating sprite
|
128 | images. The spriting is guided by a set of custom CSS properties
|
129 | with a `-ag-sprite` prefix.
|
130 |
|
131 | # Installation
|
132 |
|
133 | Make sure you have <a href="http://nodejs.org/">node.js</a> and <a
|
134 | href="http://npmjs.org/">npm</a> installed, then run:
|
135 |
|
136 | ```
|
137 | $ npm install assetgraph
|
138 | ```
|
139 |
|
140 | ## Querying the graph
|
141 |
|
142 | AssetGraph supports a flexible syntax for finding assets and relations
|
143 | in a populated graph using the `findAssets` and `findRelations`
|
144 | methods. Both methods take a query object as the first argument.
|
145 | The query engine uses MongoDB-like queries via the
|
146 | [sift module](https://github.com/crcn/sift.js). Please consult that
|
147 | to learn about the advanced querying features. Below are some basic examples.
|
148 |
|
149 | Get an array containing all assets in the graph:
|
150 |
|
151 | ```javascript
|
152 | var allAssets = assetGraph.findAssets();
|
153 | ```
|
154 |
|
155 | Find assets by type:
|
156 |
|
157 | ```javascript
|
158 | var htmlAssets = assetGraph.findAssets({ type: 'Html' });
|
159 | ```
|
160 |
|
161 | Find assets of different named types:
|
162 |
|
163 | ```javascript
|
164 | var jsAndCss = assetGraph.findAssets({ type: { $in: ['Css', 'JavaScript' ] });
|
165 | ```
|
166 |
|
167 | Find assets by matching a regular expression against the url:
|
168 |
|
169 | ```javascript
|
170 | var localImageAssets = assetGraph.findAssets({
|
171 | url: { $regex: /^file:.*\.(?:png|gif|jpg)$/ }
|
172 | });
|
173 | ```
|
174 |
|
175 | Find assets by predicate function:
|
176 |
|
177 | ```javascript
|
178 | var orphanedJavaScriptAssets = assetGraph.findAssets(function(asset) {
|
179 | return (
|
180 | asset.type === 'JavaScript' &&
|
181 | assetGraph.findRelations({ to: asset }).length === 0
|
182 | );
|
183 | });
|
184 | ```
|
185 |
|
186 | Find all HtmlScript (`<script src=...>` and inline `<script>`) relations:
|
187 |
|
188 | ```javascript
|
189 | var allHtmlScriptRelations = assetGraph.findRelations({ type: 'HtmlScript' });
|
190 | ```
|
191 |
|
192 | Query objects have "and" semantics, so all conditions must be met for
|
193 | a multi-criteria query to match:
|
194 |
|
195 | ```javascript
|
196 | var textBasedAssetsOnGoogleCom = assetGraph.findAssets({
|
197 | isText: true,
|
198 | url: { $regex: /^https?:\/\/(?:www\.)google\.com\// }
|
199 | });
|
200 | ```
|
201 |
|
202 | Find assets by existence of incoming relations:
|
203 |
|
204 | ```javascript
|
205 | var importedCssAssets = assetGraph.findAssets({
|
206 | type: 'Css',
|
207 | incomingRelations: { $elemMatch: { type: 'CssImport' } }
|
208 | });
|
209 | ```
|
210 |
|
211 | Relation queries can contain nested asset queries when querying the
|
212 | `to` and `from` properties.
|
213 |
|
214 | Find all HtmlAnchor (`<a href=...>`) relations pointing at local images:
|
215 |
|
216 | ```javascript
|
217 | assetGraph.findRelations({
|
218 | type: 'HtmlAnchor',
|
219 | to: { isImage: true, url: { $regex: /^file:/ } }
|
220 | });
|
221 | ```
|
222 |
|
223 | # Transforms and workflows
|
224 |
|
225 | AssetGraph comes with a collection of premade "transforms" that you
|
226 | can use as high level building blocks when putting together your build
|
227 | procedure. Most transforms work on a set of assets or relations and
|
228 | usually accept a query object so they can be scoped to work on only a
|
229 | specific subset of the graph.
|
230 |
|
231 | Usually you'll start by loading some initial assets from disc or via
|
232 | http using the `loadAssets` transform, then get the related assets
|
233 | added using the `populate` transform, then do the actual
|
234 | processing. Eventually you'll probably write the resulting assets back
|
235 | to disc.
|
236 |
|
237 | Thus the skeleton looks something like this:
|
238 |
|
239 | ```javascript
|
240 | var AssetGraph = require('assetgraph');
|
241 |
|
242 | const assetGraph = new AssetGraph({ root: '/the/root/directory/' });
|
243 |
|
244 | await assetGraph.loadAssets('*.html'); // Load all Html assets in the root dir
|
245 | await assetGraph.populate({ followRelations: { type: 'HtmlAnchor' } }); // Follow <a href=...>
|
246 | // More work...
|
247 | await assetGraph.writeAssetsToDisc({ type: 'Html' }); // Overwrite existing files
|
248 |
|
249 | // Done!
|
250 | ```
|
251 |
|
252 | In the following sections the built-in transforms are documented
|
253 | individually:
|
254 |
|
255 | ## assetGraph.addCacheManifest([queryObj])
|
256 |
|
257 | Add a `CacheManifest` asset to each `Html` asset in the graph (or
|
258 | to all `Html` assets matched by `queryObj` if provided). The cache
|
259 | manifests will contain relations to all assets reachable by traversing
|
260 | the graph through relations other than `HtmlAnchor`.
|
261 |
|
262 | ## assetGraph.bundleRelations(queryObj[, strategyName])
|
263 |
|
264 | Bundle the `Css` and `JavaScript` assets pointed to by the
|
265 | relations matched by `queryObj`.
|
266 |
|
267 | The `strategyName` (string) parameter can be either:
|
268 |
|
269 | #### `oneBundlePerIncludingAsset` (the default)
|
270 |
|
271 | Each unique asset pointing to one or more of the assets being
|
272 | bundled will get its own bundle. This can lead to duplication if
|
273 | eg. several `Html` assets point to the same sets of assets, but
|
274 | guarantees that the number of http requests is kept low.
|
275 |
|
276 | #### `sharedBundles`
|
277 |
|
278 | Create as many bundles as needed, optimizing for combined byte size
|
279 | of the bundles rather than http requests. Warning: Not as well
|
280 | tested as `oneBundlePerIncludingAsset`.
|
281 |
|
282 | Note that a conditional comment within an `Html` asset conveniently
|
283 | counts as a separate including asset, so in the below example
|
284 | `ie.css` and `all.css` won't be bundled together:
|
285 |
|
286 | ```html
|
287 | <!--[if IE]><link rel="stylesheet" href="ie.css"/><![endif]-->
|
288 | <link rel="stylesheet" href="all.css" />
|
289 | ```
|
290 |
|
291 | The created bundles will be placed at the root of the asset graph with
|
292 | names derived from their unique id (for example
|
293 | `file://root/of/graph/124.css`) and will replace the original
|
294 | assets.
|
295 |
|
296 | ## assetGraph.compressJavaScript([queryObj[, compressorName[, compressorOptions]]])
|
297 |
|
298 | Compresses all `JavaScript` assets in the graph (or those specified by
|
299 | `queryObj`).
|
300 |
|
301 | The `compressorName` (string) parameter can be either:
|
302 |
|
303 | #### `uglifyJs` (the default and the fastest)
|
304 |
|
305 | The excellent <a href="https://github.com/mishoo/UglifyJS">UglifyJS</a>
|
306 | compressor. If provided, the `compressorOptions` object will be
|
307 | passed to UglifyJS' `ast_squeeze` command.
|
308 |
|
309 | #### `yuicompressor`
|
310 |
|
311 | Yahoo's YUICompressor though Tim-Smart's <a
|
312 | href="https://github.com/Tim-Smart/node-yui-compressor">node-yuicompressor
|
313 | module</a>. If provided, the `compressorOptions` object will be
|
314 | passed as the second argument to `require('yui-compressor').compile`.
|
315 |
|
316 | #### `closurecompiler`
|
317 |
|
318 | Google's Closure Compiler through Tim-Smart's <a
|
319 | href="https://github.com/Tim-Smart/node-closure">node-closure
|
320 | module</a>. If provided, the `compressorOptions` object will be
|
321 | passed as the second argument to
|
322 | `require('closure-compiler').compile`.
|
323 |
|
324 | ## assetGraph.convertCssImportsToHtmlStyles([queryObj])
|
325 |
|
326 | Finds all `Html` assets in the graph (or those specified by
|
327 | `queryObj`), finds all `CssImport` relations (`@import url(...)`) in inline and external CSS and converts them to
|
328 | `HtmlStyle` relations directly from the Html document.
|
329 |
|
330 | Effectively the inverse of `assetGraph.convertHtmlStylesToInlineCssImports`.
|
331 |
|
332 | Example:
|
333 |
|
334 | ```html
|
335 | <style type="text/css">
|
336 | @import url(print.css) print;
|
337 | @import url(foo.css);
|
338 | body {
|
339 | color: red;
|
340 | }
|
341 | </style>
|
342 | ```
|
343 |
|
344 | is turned into:
|
345 |
|
346 | ```html
|
347 | <link rel="stylesheet" href="print.css" media="print" />
|
348 | <link rel="stylesheet" href="foo.css" />
|
349 | <style type="text/css">
|
350 | body {
|
351 | color: red;
|
352 | }
|
353 | </style>
|
354 | ```
|
355 |
|
356 | ## assetGraph.convertHtmlStylesToInlineCssImports([queryObj])
|
357 |
|
358 | Finds all `Html` assets in the graph (or those specified by
|
359 | `queryObj`), finds all outgoing, non-inline `HtmlStyle` relations
|
360 | (`<link rel='stylesheet' href='...'>`) and turns them into groups of
|
361 | `CssImport` relations (`@import url(...)`) in inline
|
362 | stylesheets. A maximum of 31 `CssImports` will be created per inline
|
363 | stylesheet.
|
364 |
|
365 | Example:
|
366 |
|
367 | ```html
|
368 | <link rel="stylesheet" href="foo.css" />
|
369 | <link rel="stylesheet" href="bar.css" />
|
370 | ```
|
371 |
|
372 | is turned into:
|
373 |
|
374 | ```html
|
375 | <style type="text/css">
|
376 | @import url(foo.css);
|
377 | @import url(bar.css);
|
378 | </style>
|
379 | ```
|
380 |
|
381 | This is a workaround for <a
|
382 | href="http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/ad1b6e88-bbfa-4cc4-9e95-3889b82a7c1d/">the
|
383 | limit of 31 stylesheets in Internet Explorer <= 8</a>. This transform
|
384 | allows you to have up to 31\*31 stylesheets in the development version
|
385 | of your HTML and still have it work in older Internet Explorer
|
386 | versions.
|
387 |
|
388 | ## assetGraph.drawGraph(fileName)
|
389 |
|
390 | Uses the Graphviz `dot` command to render the current contents of the
|
391 | graph and writes the result to `fileName`. The image format is
|
392 | automatically derived from the extension and can be any of <a
|
393 | href="http://www.graphviz.org/doc/info/output.html">these</a>. Using
|
394 | `.svg` is recommended.
|
395 |
|
396 | Requires Graphviz to be installed, `sudo apt-get install graphviz` on
|
397 | Debian/Ubuntu.
|
398 |
|
399 | ## assetGraph.executeJavaScriptInOrder(queryObj[, context])
|
400 |
|
401 | Experimental: For each JavaScript asset in the graph (or those matched by
|
402 | queryObj), find all reachable `JavaScript` assets and execute them
|
403 | in order.
|
404 |
|
405 | If the `context` parameter is specified, it will be used as <a
|
406 | href="http://nodejs.org/docs/latest/api/vm.html#vm.runInContext">the
|
407 | execution context</a>. Otherwise a new context will be created using
|
408 | <a
|
409 | href="http://nodejs.org/docs/latest/api/vm.html#vm.createContext">vm.createContext</a>.
|
410 |
|
411 | ## assetGraph.externalizeRelations([queryObj])
|
412 |
|
413 | Finds all inline relations in the graph (or those matched by
|
414 | `queryObj`) and makes them external. The file names will be derived
|
415 | from the unique ids of the assets.
|
416 |
|
417 | For example:
|
418 |
|
419 | ```html
|
420 | <script>
|
421 | foo = 'bar';
|
422 | </script>
|
423 | <style type="text/css">
|
424 | body {
|
425 | color: maroon;
|
426 | }
|
427 | </style>
|
428 | ```
|
429 |
|
430 | could be turned into:
|
431 |
|
432 | ```html
|
433 | <script src="4.js"></script>
|
434 | <link rel="stylesheet" href="5.css" />
|
435 | ```
|
436 |
|
437 | ## assetGraph.inlineCssImagesWithLegacyFallback([queryObj[, options]])
|
438 |
|
439 | Finds all `Html` assets in the graph (or those matched by
|
440 | `queryObj`), finds all directly reachable `Css` assets, and
|
441 | converts the outgoing `CssImage` relations (`background-image`
|
442 | etc.) to `data:` urls, subject to these criteria:
|
443 |
|
444 | 1. If `options.sizeThreshold` is specified, images with a greater byte size
|
445 | won't be inlined.
|
446 |
|
447 | 2. To avoid duplication, images referenced by more than one
|
448 | `CssImage` relation won't be inlined.
|
449 |
|
450 | 3. A `CssImage` relation pointing at an image with an `inline` GET
|
451 | parameter will always be inlined (eg. `background-image: url(foo.png?inline);`). This takes precedence over the first two
|
452 | criteria.
|
453 |
|
454 | 4. If `options.minimumIeVersion` is specified, the `data:` url length
|
455 | limitations of that version of Internet Explorer will be honored.
|
456 |
|
457 | If any image is inlined an Internet Explorer-only version of the
|
458 | stylesheet will be created and referenced from the `Html` asset in a
|
459 | conditional comment.
|
460 |
|
461 | For example:
|
462 |
|
463 | ```javascript
|
464 | await assetGraph.inlineCssImagesWithLegacyFallback(
|
465 | { type: 'Html' },
|
466 | { minimumIeVersion: 7, sizeThreshold: 4096 }
|
467 | );
|
468 | ```
|
469 |
|
470 | where `assetGraph` contains an Html asset with this fragment:
|
471 |
|
472 | ```html
|
473 | <link rel="stylesheet" href="foo.css" />
|
474 | ```
|
475 |
|
476 | and `foo.css` contains:
|
477 |
|
478 | ```css
|
479 | body {
|
480 | background-image: url(small.png);
|
481 | }
|
482 | ```
|
483 |
|
484 | will be turned into:
|
485 |
|
486 | ```html
|
487 | <!--[if IE]><link rel="stylesheet" href="foo.css"/><![endif]-->
|
488 | <!--[if !IE]>--><link rel="stylesheet" href="1234.css" /><!--<![endif]-->
|
489 | ```
|
490 |
|
491 | where `1234.css` is a copy of the original `foo.css` with the
|
492 | images inlined as `data:` urls:
|
493 |
|
494 | ```css
|
495 | body {
|
496 | background-image: url(data;image/png;base64, iVBORw0KGgoAAAANSUhE...);
|
497 | }
|
498 | ```
|
499 |
|
500 | The file name `1234.css` is just an example. The actual asset file
|
501 | name will be derived from the unique id of the copy and be placed at
|
502 | the root of the assetgraph.
|
503 |
|
504 | ## assetGraph.inlineRelations([queryObj])
|
505 |
|
506 | Inlines all relations in the graph (or those matched by
|
507 | `queryObj`). Only works on relation types that support inlining, for
|
508 | example `HtmlScript`, `HtmlStyle`, and `CssImage`.
|
509 |
|
510 | Example:
|
511 |
|
512 | ```javascript
|
513 | await assetGraph.inlineRelations({ type: { $in: ['HtmlStyle', 'CssImage'] } });
|
514 | ```
|
515 |
|
516 | where `assetGraph` contains an Html asset with this fragment:
|
517 |
|
518 | ```html
|
519 | <link rel="stylesheet" href="foo.css" />
|
520 | ```
|
521 |
|
522 | and `foo.css` contains:
|
523 |
|
524 | ```css
|
525 | body {
|
526 | background-image: url(small.png);
|
527 | }
|
528 | ```
|
529 |
|
530 | will be turned into:
|
531 |
|
532 | ```html
|
533 | <style type="text/css">
|
534 | body {
|
535 | background-image: url(data;image/png;base64, iVBORw0KGgoAAAANSUhE...);
|
536 | }
|
537 | </style>
|
538 | ```
|
539 |
|
540 | Note that `foo.css` and the `CssImage` will still be modelled as
|
541 | separate assets after being inlined, so they can be manipulated the
|
542 | same way as when they were external.
|
543 |
|
544 | ## assetGraph.loadAssets(fileName|wildcard|url|Asset[, ...])
|
545 |
|
546 | Add new assets to the graph and make sure they are loaded, returning a promise
|
547 | that fulfills with an array of the assets that were added. Several
|
548 | syntaxes are supported, for example:
|
549 |
|
550 | ```javascript
|
551 | const [aHtml, bCss] = await assetGraph.loadAssets('a.html', 'b.css'); // Relative to assetGraph.root
|
552 | await assetGraph.loadAssets({
|
553 | url: 'http://example.com/index.html',
|
554 | text: 'var foo = bar;' // The source is specified, won't be loaded
|
555 | });
|
556 | ```
|
557 |
|
558 | `file://` urls support wildcard expansion:
|
559 |
|
560 | ```javascript
|
561 | await assetGraph.loadAssets('file:///foo/bar/*.html'); // Wildcard expansion
|
562 | await assetGraph.loadAssets('*.html'); // assetGraph.root must be file://...
|
563 | ```
|
564 |
|
565 | ## assetGraph.mergeIdenticalAssets([queryObj])
|
566 |
|
567 | Compute the MD5 sum of every asset in the graph (or those specified by
|
568 | `queryObj`) and remove duplicates. The relations pointing at the
|
569 | removed assets are updated to point at the copy that is kept.
|
570 |
|
571 | For example:
|
572 |
|
573 | ```javascript
|
574 | await assetGraph.mergeIdenticalAssets({ type: { $in: ['Png', 'Css'] } });
|
575 | ```
|
576 |
|
577 | where `assetGraph` contains an `Html` asset with this fragment:
|
578 |
|
579 | ```html
|
580 | <head>
|
581 | <style type="text/css">
|
582 | body {
|
583 | background-image: url(foo.png);
|
584 | }
|
585 | </style>
|
586 | </head>
|
587 | <body>
|
588 | <img src="bar.png" />
|
589 | </body>
|
590 | ```
|
591 |
|
592 | will be turned into the following if `foo.png` and `bar.png` are identical:
|
593 |
|
594 | ```html
|
595 | <head>
|
596 | <style type="text/css">
|
597 | body {
|
598 | background-image: url(foo.png);
|
599 | }
|
600 | </style>
|
601 | </head>
|
602 | <body>
|
603 | <img src="foo.png" />
|
604 | </body>
|
605 | ```
|
606 |
|
607 | and the `bar.png` asset will be removed from the graph.
|
608 |
|
609 | ## assetGraph.minifyAssets([queryObj])
|
610 |
|
611 | Minify all assets in the graph, or those specified by
|
612 | `queryObj`. Only has an effect for asset types that support
|
613 | minification, and what actually happens also varies:
|
614 |
|
615 | #### `Html` and `Xml`
|
616 |
|
617 | Pure-whitespace text nodes are removed immediately.
|
618 |
|
619 | #### `Json`, `JavaScript`, and `Css`
|
620 |
|
621 | The asset gets marked as minified (`isPretty` is set to
|
622 | `false`), which doesn't affect the in-memory representation
|
623 | (`asset.parseTree`), but is honored when the asset is serialized.
|
624 | For `JavaScript` this only governs the amount of whitespace
|
625 | (<a href="https://github.com/estools/escodegen">escodegen</a>'s
|
626 | `compact` parameter); for how to apply variable renaming and
|
627 | other compression techniques see `assetGraph.compressJavaScript`.
|
628 |
|
629 | Compare to `assetGraph.prettyPrintAssets`.
|
630 |
|
631 | ## assetGraph.moveAssets(queryObj, newUrlFunctionOrString)
|
632 |
|
633 | Change the url of all assets matching `queryObj`. If the second
|
634 | argument is a function, it will be called with each asset as the first
|
635 | argument and the assetGraph instance as the second and the url of the
|
636 | asset will be changed according to the return value:
|
637 |
|
638 | - If a falsy value is returned, nothing happens; the asset keeps its
|
639 | current url.
|
640 | - If a non-absolute url is returned, it is resolved from
|
641 | `assetGraph.root`.
|
642 | - If the url ends in a slash, the file name part of the old url is
|
643 | appended.
|
644 |
|
645 | Move all `Css` and `Png` assets to a root-relative url:
|
646 |
|
647 | ```javascript
|
648 | await assetGraph.moveAssets({ type: 'Css' }, '/images/');
|
649 | ```
|
650 |
|
651 | If the graph contains `http://example.com/foo/bar.css` and
|
652 | `assetGraph.root` is `file:///my/local/dir/`, the resulting url will
|
653 | be `file:///my/local/dir/images/bar.css`.
|
654 |
|
655 | Move all non-inline `JavaScript` and `Css` assets to either
|
656 | `http://example.com/js/` or `http://example.com/css/`, preserving
|
657 | the current file name part of their url:
|
658 |
|
659 | ```javascript
|
660 | await assetGraph.moveAssets(
|
661 | { type: { $in: ['JavaScript', 'Css'] }, isInline: false },
|
662 | (asset, assetGraph) =>
|
663 | `http://example.com/${asset.type.toLowerCase()}/${asset.fileName}`
|
664 | );
|
665 | ```
|
666 |
|
667 | The assets are moved in no particular order. Compare with
|
668 | `assetGraph.moveAssetsInOrder`.
|
669 |
|
670 | ## assetGraph.moveAssetsInOrder(queryObj, newUrlFunctionOrString)
|
671 |
|
672 | Does the same as `assetGraph.moveAssets`, but makes sure that the
|
673 | "leaf assets" are moved before the assets that have outgoing relations
|
674 | to them.
|
675 |
|
676 | The typical use case for this is when you want to rename assets to
|
677 | `<hashOfContents>.<extension>` while making sure that the hashes of
|
678 | the assets that have already been moved don't change as a result of
|
679 | updating the urls of the related assets after the fact.
|
680 |
|
681 | Here's a simplified example taken from `buildProduction` in
|
682 | <a href="http://github.com/assetgraph/assetgraph-builder">AssetGraph-builder</a>.
|
683 |
|
684 | ```javascript
|
685 | await assetGraph.moveAssetsInOrder(
|
686 | { type: { $in: ['JavaScript', 'Css', 'Jpeg', 'Gif', 'Png'] } },
|
687 | asset => `/static/${asset.md5Hex.substr(0, 10)}${asset.extension}`
|
688 | );
|
689 | ```
|
690 |
|
691 | If a graph contains an `Html` asset with a relation to a `Css` asset
|
692 | that again has a relation to a `Png` asset, the above snippet will
|
693 | always move the `Png` asset before the `Css` asset, thus making it
|
694 | safe to compute the md5 of the respective assets when the function is
|
695 | invoked.
|
696 |
|
697 | Obviously this only works for graphs (or subsets of graphs)
|
698 | that don't contain cycles, and if that's not the case, an error will
|
699 | be thrown.
|
700 |
|
701 | ## transforms.populate(options)
|
702 |
|
703 | Add assets to the graph by recursively following "dangling
|
704 | relations". This is the preferred way to load a complete web site or
|
705 | web application into an `AssetGraph` instance after using
|
706 | `assetGraph.loadAssets` to add one or more assets to serve as the
|
707 | starting point for the population. The loading of the assets happens
|
708 | in parallel.
|
709 |
|
710 | The `options` object can contain these properties:
|
711 |
|
712 | #### `from`: queryObj
|
713 |
|
714 | Specifies the set assets of assets to start populating from
|
715 | (defaults to all assets in the graph).
|
716 |
|
717 | #### `followRelations`: queryObj
|
718 |
|
719 | Limits the set of relations that are followed. The default is to
|
720 | follow all relations.
|
721 |
|
722 | #### `onError`: function (err, assetGraph, asset)
|
723 |
|
724 | If there's an error loading an asset and an `onError` function is
|
725 | specified, it will be called, and the population will continue. If
|
726 | not specified, the population will stop and pass on the error to its
|
727 | callback. (This is poorly thought out and should be removed or
|
728 | redesigned).
|
729 |
|
730 | #### `concurrency`: Number
|
731 |
|
732 | The maximum number of assets that can be loading at once (defaults to 100).
|
733 |
|
734 | Example:
|
735 |
|
736 | ```javascript
|
737 | const assetGraph = new AssetGraph();
|
738 | await assetGraph.loadAssets('a.html');
|
739 | await assetGraph.populate({
|
740 | followRelations: {
|
741 | type: 'HtmlAnchor',
|
742 | to: { url: { $regex: /\/[bc]\.html$/ } }
|
743 | }
|
744 | });
|
745 | ```
|
746 |
|
747 | If `a.html` links to `b.html`, and `b.html` links to `c.html`
|
748 | (using `<a href="...">`), all three assets will be in the graph
|
749 | after `assetGraph.populate` is done. If `c.html` happens to link
|
750 | to `d.html`, `d.html` won't be added.
|
751 |
|
752 | ## assetGraph.prettyPrintAssets([queryObj])
|
753 |
|
754 | Pretty-print all assets in the graph, or those specified by
|
755 | `queryObj`. Only has an effect for asset types that support pretty
|
756 | printing (`JavaScript`, `Css`, `Html`, `Xml`, and `Json`).
|
757 |
|
758 | The asset gets marked as pretty printed (`isPretty` is set to
|
759 | `true`), which doesn't affect the in-memory representation
|
760 | (`asset.parseTree`), but is honored when the asset is
|
761 | serialized. For `Xml`, and `Html`, however, the existing
|
762 | whitespace-only text nodes in the document are removed immediately.
|
763 |
|
764 | Compare to `assetGraph.minifyAssets`.
|
765 |
|
766 | Example:
|
767 |
|
768 | ```javascript
|
769 | // Pretty-print all Html and Css assets:
|
770 | await assetGraph.prettyPrintAssets({ type: { $in: ['Html', 'Css'] } });
|
771 | ```
|
772 |
|
773 | ## assetGraph.removeRelations([queryObj, [options]])
|
774 |
|
775 | Remove all relations in the graph, or those specified by `queryObj`.
|
776 |
|
777 | The `options` object can contain these properties:
|
778 |
|
779 | #### `detach`: Boolean
|
780 |
|
781 | Whether to also detach the relations (remove their nodes from the
|
782 | parse tree of the source asset). Only supported for some relation
|
783 | types. Defaults to `false`.
|
784 |
|
785 | #### `removeOrphan`: Boolean
|
786 |
|
787 | Whether to also remove assets that become "orphans" as a result of
|
788 | removing their last incoming relation.
|
789 |
|
790 | ## assetGraph.setHtmlImageDimensions([queryObj])
|
791 |
|
792 | Sets the `width` and `height` attributes of the `img` elements
|
793 | underlying all `HtmlImage` relations, or those matching
|
794 | `queryObj`. Only works when the image pointed to by the relation is
|
795 | in the graph.
|
796 |
|
797 | Example:
|
798 |
|
799 | ```javascript
|
800 | const AssetGraph = require('assetgraph');
|
801 |
|
802 | const assetGraph = new AssetGraph();
|
803 | await assetGraph.loadAssets('hasanimage.html');
|
804 | await assetGraph.populate();
|
805 |
|
806 | // assetGraph.findAssets({type: 'Html'})[0].text === '<body><img src="foo.png"></body>'
|
807 |
|
808 | await assetGraph.setHtmlImageDimensions();
|
809 |
|
810 | // assetGraph.findAssets({type: 'Html'})[0].text === '<body><img src="foo.png" width="29" height="32"></body>'
|
811 | ```
|
812 |
|
813 | ## assetGraph.writeStatsToStderr([queryObj])
|
814 |
|
815 | Dumps an ASCII table with some basic stats about all the assets in the
|
816 | graph (or those matching `queryObj`) in their current state.
|
817 |
|
818 | Example:
|
819 |
|
820 | ```
|
821 | Ico 1 1.1 KB
|
822 | Png 28 196.8 KB
|
823 | Gif 145 129.4 KB
|
824 | Json 2 60.1 KB
|
825 | Css 2 412.6 KB
|
826 | JavaScript 34 1.5 MB
|
827 | Html 1 1.3 KB
|
828 | Total: 213 2.2 MB
|
829 | ```
|
830 |
|
831 | ## assetGraph.writeAssetsToDisc(queryObj, outRoot[, root])
|
832 |
|
833 | Writes the assets matching `queryObj` to disc. The `outRoot`
|
834 | parameter must be a `file://` url specifying the directory where the
|
835 | files should be output. The optional `root` parameter specifies the
|
836 | url that you want to correspond to the `outRoot` directory (defaults
|
837 | to the `root` property of the AssetGraph instance).
|
838 |
|
839 | Directories will be created as needed.
|
840 |
|
841 | Example:
|
842 |
|
843 | ```javascript
|
844 | const AssetGraph = require('assetgraph');
|
845 |
|
846 | const assetGraph = new AssetGraph({root: 'http://example.com/'});
|
847 | await assetGraph.loadAssets(
|
848 | 'http://example.com/bar/quux/foo.html',
|
849 | 'http://example.com/bar/baz.html'
|
850 | );
|
851 |
|
852 | // Write the two assets to /my/output/dir/quux/foo.html and /my/output/dir/baz.html:
|
853 | await assetGraph.writeAssetsToDisc({type: 'Html'} 'file:///my/output/dir/', 'http://example.com/bar/');
|
854 | ```
|
855 |
|
856 | ## assetGraph.writeAssetsToStdout([queryObj])
|
857 |
|
858 | Writes all assets in the graph (or those specified by `queryObj`) to
|
859 | stdout. Mostly useful for piping out a single asset.
|
860 |
|
861 | ## License
|
862 |
|
863 | AssetGraph is licensed under a standard 3-clause BSD license -- see the
|
864 | `LICENSE`-file for details.
|