Git Product home page Git Product logo

assetgraph-builder's Introduction

AssetGraph

NPM version Build Status Coverage Status Dependencies Status

AssetGraph is an extensible, node.js-based framework for manipulating and optimizing web pages and web 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 discover assets based on your declarative code, reducing the configuration needs to a minimum.

If you just want to get started with the basics, read Peter Müller - Getting started with Assetgraph.

If you are looking for a prepackaged build system take a look at Assetgraph-builder.

Tools built with AssetGraph

  • assetgraph-builder - A static web page build system that post-processes your website with extremely little configuration
  • subfont - A tool that supercharges your webfont loading by automatically applying all best practice loading techniques and generating optimal font subsets
  • hyperlink - A link checker tool that will ensure all your internal and external links are intact and up to date
  • seespee - A Content-Security Policy generator. Point it at a webpage and it will tell you what policy you need as a minimum
  • trackingdog - cli for finding the original source location of a line+column in a generated file, utilizing the source map
  • bringhome - cli for downloading a web page and its assets to a local directory.

Assets and relations

All web build tools, even those that target very specific problems, have to get a bunch of boring stuff right just to get started, such as loading files from disc, parsing and serializing them, charsets, inlining, finding references to other files, resolution of and updating urls, etc.

The observation that inspired the project is that most of these tasks can be viewed as graph problems, where the nodes are the assets (HTML, CSS, images, JavaScript...) and the edges are the relations between them, e.g. anchor tags, image tags, favorite icons, css background-image properties and so on.

An example illustration of an asset graph representing a web page

An AssetGraph object is a collection of assets (nodes) and the relations (edges) between them. It's a basic data model that allows you to populate, query, and manipulate the graph at a high level of abstraction. For instance, if you change the url of an asset, all relations pointing at it are automatically updated.

Additionally, each individual asset can be inspected and massaged using a relevant API: jsdom for HTML, PostCSS for CSS, and an Esprima AST for Javascript.

AssetGraph represents inline assets the same way as non-inline ones, so eg. inline scripts, stylesheets, and images specified as data: urls are also first-class nodes in the graph. This means that you don't need to dig into the HTML of the containing asset to manipulate them. An extreme example would be an Html asset with a conditional comment with an inline stylesheet with an inline image, which are modelled as 4 separate assets:

<!DOCTYPE html>
<html>
  <head>
    <!--[if !IE]> -->
    <style type="text/css">
      body {
        background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);
      }
    </style>
    <!-- <![endif]-->
  </head>
  <body></body>
</html>

These are some of the supported assets and associated relation types:

HTML

<a>, <link rel="stylesheet|shortcut icon|fluid-icon|alternate|serviceworker">, <script>, <style>, <html manifest="..."> <img>, <video>, <audio>, <applet>, <embed>, <esi:include>, <iframe>, <svg>, <meta property="og:...">

SVG

<style>, inline style=... attributes, event handlers, <?xml-stylesheet href=...>, <font-face-src>

CSS

//# sourceMappingURL=..., background-image: url(...), @import url(...), behavior: url(...), filter: AlphaImageLoader(src='...'), @font-face { src: url(...) }

JavaScript

//# sourceMappingURL=..., homegrown 'foo/bar.png'.toString('url') syntax for referencing external files

Web manifest

Icon urls, related_applications, start_url, etc.

Cache manifest (appcache)

Entries in the CACHE, NETWORK and FALLBACK sections

JSON, XML, PNG, GIF, JPEG, ICO

(none)

Features

  • Build an AssetGraph programmatically or load it from disk or a remote server via http.
  • Find explicit dependencies between JavaScript and CSS and roll them out as <script> and <link rel='stylesheet'> tags in your HTML.
  • Bundle and inline CSS and JavaScript.
  • Create a cache manifest with references to all the assets your web app needs to be usable offline.
  • Move all CSS, JavaScript, image assets etc. to a static dir and rename them to md5.extension so the web server can be configured to set a far-future Cache-Control.
  • Help getting your static assets on a CDN by allowing you to easily rewrite all references to them.
  • Use Graphviz to visualize your dependencies at any step.
  • Using the separate assetgraph-sprite transform: Optimize CSS background images by creating sprite images. The spriting is guided by a set of custom CSS properties with a -ag-sprite prefix.

Installation

Make sure you have node.js and npm installed, then run:

$ npm install assetgraph

Querying the graph

AssetGraph supports a flexible syntax for finding assets and relations in a populated graph using the findAssets and findRelations methods. Both methods take a query object as the first argument. The query engine uses MongoDB-like queries via the sift module. Please consult that to learn about the advanced querying features. Below are some basic examples.

Get an array containing all assets in the graph:

var allAssets = assetGraph.findAssets();

Find assets by type:

var htmlAssets = assetGraph.findAssets({ type: 'Html' });

Find assets of different named types:

var jsAndCss = assetGraph.findAssets({ type: { $in: ['Css', 'JavaScript' ] });

Find assets by matching a regular expression against the url:

var localImageAssets = assetGraph.findAssets({
  url: { $regex: /^file:.*\.(?:png|gif|jpg)$/ },
});

Find assets by predicate function:

var orphanedJavaScriptAssets = assetGraph.findAssets(function (asset) {
  return (
    asset.type === 'JavaScript' &&
    assetGraph.findRelations({ to: asset }).length === 0
  );
});

Find all HtmlScript (<script src=...> and inline <script>) relations:

var allHtmlScriptRelations = assetGraph.findRelations({ type: 'HtmlScript' });

Query objects have "and" semantics, so all conditions must be met for a multi-criteria query to match:

var textBasedAssetsOnGoogleCom = assetGraph.findAssets({
  isText: true,
  url: { $regex: /^https?:\/\/(?:www\.)google\.com\// },
});

Find assets by existence of incoming relations:

var importedCssAssets = assetGraph.findAssets({
  type: 'Css',
  incomingRelations: { $elemMatch: { type: 'CssImport' } },
});

Relation queries can contain nested asset queries when querying the to and from properties.

Find all HtmlAnchor (<a href=...>) relations pointing at local images:

assetGraph.findRelations({
  type: 'HtmlAnchor',
  to: { isImage: true, url: { $regex: /^file:/ } },
});

Transforms and workflows

AssetGraph comes with a collection of premade "transforms" that you can use as high level building blocks when putting together your build procedure. Most transforms work on a set of assets or relations and usually accept a query object so they can be scoped to work on only a specific subset of the graph.

Usually you'll start by loading some initial assets from disc or via http using the loadAssets transform, then get the related assets added using the populate transform, then do the actual processing. Eventually you'll probably write the resulting assets back to disc.

Thus the skeleton looks something like this:

var AssetGraph = require('assetgraph');

const assetGraph = new AssetGraph({ root: '/the/root/directory/' });

await assetGraph.loadAssets('*.html'); // Load all Html assets in the root dir
await assetGraph.populate({ followRelations: { type: 'HtmlAnchor' } }); // Follow <a href=...>
// More work...
await assetGraph.writeAssetsToDisc({ type: 'Html' }); // Overwrite existing files

// Done!

In the following sections the built-in transforms are documented individually:

assetGraph.addCacheManifest([queryObj])

Add a CacheManifest asset to each Html asset in the graph (or to all Html assets matched by queryObj if provided). The cache manifests will contain relations to all assets reachable by traversing the graph through relations other than HtmlAnchor.

assetGraph.bundleRelations(queryObj[, strategyName])

Bundle the Css and JavaScript assets pointed to by the relations matched by queryObj.

The strategyName (string) parameter can be either:

oneBundlePerIncludingAsset (the default)

Each unique asset pointing to one or more of the assets being bundled will get its own bundle. This can lead to duplication if eg. several Html assets point to the same sets of assets, but guarantees that the number of http requests is kept low.

sharedBundles

Create as many bundles as needed, optimizing for combined byte size of the bundles rather than http requests. Warning: Not as well tested as oneBundlePerIncludingAsset.

Note that a conditional comment within an Html asset conveniently counts as a separate including asset, so in the below example ie.css and all.css won't be bundled together:

<!--[if IE]><link rel="stylesheet" href="ie.css" /><![endif]-->
<link rel="stylesheet" href="all.css" />

The created bundles will be placed at the root of the asset graph with names derived from their unique id (for example file://root/of/graph/124.css) and will replace the original assets.

assetGraph.compressJavaScript([queryObj[, compressorName[, compressorOptions]]])

Compresses all JavaScript assets in the graph (or those specified by queryObj).

The compressorName (string) parameter can be either:

uglifyJs (the default and the fastest)

The excellent UglifyJS compressor. If provided, the compressorOptions object will be passed to UglifyJS' ast_squeeze command.

yuicompressor

Yahoo's YUICompressor though Tim-Smart's node-yuicompressor module. If provided, the compressorOptions object will be passed as the second argument to require('yui-compressor').compile.

closurecompiler

Google's Closure Compiler through Tim-Smart's node-closure module. If provided, the compressorOptions object will be passed as the second argument to require('closure-compiler').compile.

assetGraph.convertCssImportsToHtmlStyles([queryObj])

Finds all Html assets in the graph (or those specified by queryObj), finds all CssImport relations (@import url(...)) in inline and external CSS and converts them to HtmlStyle relations directly from the Html document.

Effectively the inverse of assetGraph.convertHtmlStylesToInlineCssImports.

Example:

<style type="text/css">
  @import url(print.css) print;
  @import url(foo.css);
  body {
    color: red;
  }
</style>

is turned into:

<link rel="stylesheet" href="print.css" media="print" />
<link rel="stylesheet" href="foo.css" />
<style type="text/css">
  body {
    color: red;
  }
</style>

assetGraph.convertHtmlStylesToInlineCssImports([queryObj])

Finds all Html assets in the graph (or those specified by queryObj), finds all outgoing, non-inline HtmlStyle relations (<link rel='stylesheet' href='...'>) and turns them into groups of CssImport relations (@import url(...)) in inline stylesheets. A maximum of 31 CssImports will be created per inline stylesheet.

Example:

<link rel="stylesheet" href="foo.css" />
<link rel="stylesheet" href="bar.css" />

is turned into:

<style type="text/css">
  @import url(foo.css);
  @import url(bar.css);
</style>

This is a workaround for the limit of 31 stylesheets in Internet Explorer <= 8. This transform allows you to have up to 31*31 stylesheets in the development version of your HTML and still have it work in older Internet Explorer versions.

assetGraph.drawGraph(fileName)

Uses the Graphviz dot command to render the current contents of the graph and writes the result to fileName. The image format is automatically derived from the extension and can be any of these. Using .svg is recommended.

Requires Graphviz to be installed, sudo apt-get install graphviz on Debian/Ubuntu.

assetGraph.executeJavaScriptInOrder(queryObj[, context])

Experimental: For each JavaScript asset in the graph (or those matched by queryObj), find all reachable JavaScript assets and execute them in order.

If the context parameter is specified, it will be used as the execution context. Otherwise a new context will be created using vm.createContext.

assetGraph.externalizeRelations([queryObj])

Finds all inline relations in the graph (or those matched by queryObj) and makes them external. The file names will be derived from the unique ids of the assets.

For example:

<script>
  foo = 'bar';
</script>
<style type="text/css">
  body {
    color: maroon;
  }
</style>

could be turned into:

<script src="4.js"></script>
<link rel="stylesheet" href="5.css" />

assetGraph.inlineCssImagesWithLegacyFallback([queryObj[, options]])

Finds all Html assets in the graph (or those matched by queryObj), finds all directly reachable Css assets, and converts the outgoing CssImage relations (background-image etc.) to data: urls, subject to these criteria:

  1. If options.sizeThreshold is specified, images with a greater byte size won't be inlined.

  2. To avoid duplication, images referenced by more than one CssImage relation won't be inlined.

  3. A CssImage relation pointing at an image with an inline GET parameter will always be inlined (eg. background-image: url(foo.png?inline);). This takes precedence over the first two criteria.

  4. If options.minimumIeVersion is specified, the data: url length limitations of that version of Internet Explorer will be honored.

If any image is inlined an Internet Explorer-only version of the stylesheet will be created and referenced from the Html asset in a conditional comment.

For example:

await assetGraph.inlineCssImagesWithLegacyFallback(
  { type: 'Html' },
  { minimumIeVersion: 7, sizeThreshold: 4096 }
);

where assetGraph contains an Html asset with this fragment:

<link rel="stylesheet" href="foo.css" />

and foo.css contains:

body {
  background-image: url(small.gif);
}

will be turned into:

<!--[if IE]><link rel="stylesheet" href="foo.css" /><![endif]-->
<!--[if !IE]>--><link rel="stylesheet" href="1234.css" /><!--<![endif]-->

where 1234.css is a copy of the original foo.css with the images inlined as data: urls:

body {
  background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);
}

The file name 1234.css is just an example. The actual asset file name will be derived from the unique id of the copy and be placed at the root of the assetgraph.

assetGraph.inlineRelations([queryObj])

Inlines all relations in the graph (or those matched by queryObj). Only works on relation types that support inlining, for example HtmlScript, HtmlStyle, and CssImage.

Example:

await assetGraph.inlineRelations({ type: { $in: ['HtmlStyle', 'CssImage'] } });

where assetGraph contains an Html asset with this fragment:

<link rel="stylesheet" href="foo.css" />

and foo.css contains:

body {
  background-image: url(small.gif);
}

will be turned into:

<style type="text/css">
  body {
    background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);
  }
</style>

Note that foo.css and the CssImage will still be modelled as separate assets after being inlined, so they can be manipulated the same way as when they were external.

assetGraph.loadAssets(fileName|wildcard|url|Asset[, ...])

Add new assets to the graph and make sure they are loaded, returning a promise that fulfills with an array of the assets that were added. Several syntaxes are supported, for example:

const [aHtml, bCss] = await assetGraph.loadAssets('a.html', 'b.css'); // Relative to assetGraph.root
await assetGraph.loadAssets({
  url: 'http://example.com/index.html',
  text: 'var foo = bar;', // The source is specified, won't be loaded
});

file:// urls support wildcard expansion:

await assetGraph.loadAssets('file:///foo/bar/*.html'); // Wildcard expansion
await assetGraph.loadAssets('*.html'); // assetGraph.root must be file://...

assetGraph.mergeIdenticalAssets([queryObj])

Compute the MD5 sum of every asset in the graph (or those specified by queryObj) and remove duplicates. The relations pointing at the removed assets are updated to point at the copy that is kept.

For example:

await assetGraph.mergeIdenticalAssets({ type: { $in: ['Png', 'Css'] } });

where assetGraph contains an Html asset with this fragment:

<head>
  <style type="text/css">
    body {
      background-image: url(foo.png);
    }
  </style>
</head>
<body>
  <img src="bar.png" />
</body>

will be turned into the following if foo.png and bar.png are identical:

<head>
  <style type="text/css">
    body {
      background-image: url(foo.png);
    }
  </style>
</head>
<body>
  <img src="foo.png" />
</body>

and the bar.png asset will be removed from the graph.

assetGraph.minifyAssets([queryObj])

Minify all assets in the graph, or those specified by queryObj. Only has an effect for asset types that support minification, and what actually happens also varies:

Html and Xml

Pure-whitespace text nodes are removed immediately.

Json, JavaScript, and Css

The asset gets marked as minified (isPretty is set to false), which doesn't affect the in-memory representation (asset.parseTree), but is honored when the asset is serialized. For JavaScript this only governs the amount of whitespace (escodegen's compact parameter); for how to apply variable renaming and other compression techniques see assetGraph.compressJavaScript.

Compare to assetGraph.prettyPrintAssets.

transforms.populate(options)

Add assets to the graph by recursively following "dangling relations". This is the preferred way to load a complete web site or web application into an AssetGraph instance after using assetGraph.loadAssets to add one or more assets to serve as the starting point for the population. The loading of the assets happens in parallel.

The options object can contain these properties:

from: queryObj

Specifies the set assets of assets to start populating from (defaults to all assets in the graph).

followRelations: queryObj

Limits the set of relations that are followed. The default is to follow all relations.

onError: function (err, assetGraph, asset)

If there's an error loading an asset and an onError function is specified, it will be called, and the population will continue. If not specified, the population will stop and pass on the error to its callback. (This is poorly thought out and should be removed or redesigned).

concurrency: Number

The maximum number of assets that can be loading at once (defaults to 100).

Example:

const assetGraph = new AssetGraph();
await assetGraph.loadAssets('a.html');
await assetGraph.populate({
  followRelations: {
    type: 'HtmlAnchor',
    to: { url: { $regex: /\/[bc]\.html$/ } },
  },
});

If a.html links to b.html, and b.html links to c.html (using <a href="...">), all three assets will be in the graph after assetGraph.populate is done. If c.html happens to link to d.html, d.html won't be added.

assetGraph.prettyPrintAssets([queryObj])

Pretty-print all assets in the graph, or those specified by queryObj. Only has an effect for asset types that support pretty printing (JavaScript, Css, Html, Xml, and Json).

The asset gets marked as pretty printed (isPretty is set to true), which doesn't affect the in-memory representation (asset.parseTree), but is honored when the asset is serialized. For Xml, and Html, however, the existing whitespace-only text nodes in the document are removed immediately.

Compare to assetGraph.minifyAssets.

Example:

// Pretty-print all Html and Css assets:
await assetGraph.prettyPrintAssets({ type: { $in: ['Html', 'Css'] } });

assetGraph.removeRelations([queryObj, [options]])

Remove all relations in the graph, or those specified by queryObj.

The options object can contain these properties:

detach: Boolean

Whether to also detach the relations (remove their nodes from the parse tree of the source asset). Only supported for some relation types. Defaults to false.

removeOrphan: Boolean

Whether to also remove assets that become "orphans" as a result of removing their last incoming relation.

assetGraph.setHtmlImageDimensions([queryObj])

Sets the width and height attributes of the img elements underlying all HtmlImage relations, or those matching queryObj. Only works when the image pointed to by the relation is in the graph.

Example:

const AssetGraph = require('assetgraph');

const assetGraph = new AssetGraph();
await assetGraph.loadAssets('hasanimage.html');
await assetGraph.populate();

// assetGraph.findAssets({type: 'Html'})[0].text === '<body><img src="foo.png"></body>'

await assetGraph.setHtmlImageDimensions();

// assetGraph.findAssets({type: 'Html'})[0].text === '<body><img src="foo.png" width="29" height="32"></body>'

assetGraph.writeStatsToStderr([queryObj])

Dumps an ASCII table with some basic stats about all the assets in the graph (or those matching queryObj) in their current state.

Example:

       Ico   1   1.1 KB
       Png  28 196.8 KB
       Gif 145 129.4 KB
      Json   2  60.1 KB
       Css   2 412.6 KB
JavaScript  34   1.5 MB
      Html   1   1.3 KB
    Total: 213   2.2 MB

assetGraph.writeAssetsToDisc(queryObj, outRoot[, root])

Writes the assets matching queryObj to disc. The outRoot parameter must be a file:// url specifying the directory where the files should be output. The optional root parameter specifies the url that you want to correspond to the outRoot directory (defaults to the root property of the AssetGraph instance).

Directories will be created as needed.

Example:

const AssetGraph = require('assetgraph');

const assetGraph = new AssetGraph({root: 'http://example.com/'});
await assetGraph.loadAssets(
  'http://example.com/bar/quux/foo.html',
  'http://example.com/bar/baz.html'
);

// Write the two assets to /my/output/dir/quux/foo.html and /my/output/dir/baz.html:
await assetGraph.writeAssetsToDisc({type: 'Html'} 'file:///my/output/dir/', 'http://example.com/bar/');

assetGraph.writeAssetsToStdout([queryObj])

Writes all assets in the graph (or those specified by queryObj) to stdout. Mostly useful for piping out a single asset.

License

AssetGraph is licensed under a standard 3-clause BSD license -- see the LICENSE-file for details.

assetgraph-builder's People

Contributors

alexjeffburke avatar cybear avatar danielruf avatar depfu[bot] avatar greenkeeperio-bot avatar gustavnikolaj avatar guybedford avatar munawwar avatar munter avatar mwoc avatar noahlaux avatar papandreou avatar salomvary avatar saulshanabrook avatar silvenon avatar sunesimonsen avatar tenzer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

assetgraph-builder's Issues

Move image postrprocessing instructions to image url

Currently image post processing instructions are defined in -one-image-postprocess.

This has several drawbacks:

  • Only CSS images can be post processed
  • Elements may contain multiple backgrounds that shouldn't have identical postprocessing

We should move the post processing instructions into the URL.

Requirements

  • CGI parametres (dynamic images are 404 on file systems anyway, and this would enable middleware to show the post processed image in developmetn mode)
  • buildProduction should remove all the cgi parametres it understands when post processing
  • The same image referenced several time with identical post processing should be treated as one image
  • The same image with different postprocessing should be split into two different images and throw a command line warning
  • -one-image-postprocess should be deprecated
  • Order of cgi parametres should be significant

Discussion about cgi parametre syntax is appreciated

buildProduction doesn't complain about no input files

In the help text to buildProduction (eg. run without parameters), htmlFile(s) are considered mandatory:

node ./node_modules/.bin/buildProduction --root <inputRootDirectory> --outroot <dir> [options] <htmlFile(s)>

But the program doesn't complain when no files are given. It just work through an empty graph.

I believe adding a .demand(1) in the optimist-chain will suffice.

Image identity lost on image postprocessing

If I have two selectors using tha same background image and I set postproccessing instructions on both of them, assetgraph will make two copies of the image.

Possible solutions:

  • Keep identity and run all transforms in order
  • Warn about multiple transforms on the same image

any advice on handling dynamic html fragments?

first of all, this tool is freaking sweet. thanks for releasing it!

I'm wondering how I handle the sections of html that are loaded dynamically. For example, in my single page app when a user views a new page the front end requests an html template from the server, renders it, and fetches the resources (mostly images) used in the template. But because this happens at run time assetgraph-builder never picks up these linkages and so all of my sub pages are still llinking to resources served from my web server. Any ideas on how to fix this?

It's not a huge problem because most of the resources load on the first page anyway. It would just be super nice if 100% of my static content was hitting the cdn.

thanks again for awesome software!

[email protected] is throwing a fatal error

0.001 secs: registerRequireJsConfig
0.005 secs: registerLabelsAsCustomProtocols

/usr/lib/node_modules/assetgraph-builder/bin/buildProduction:280
            throw err;
                  ^
TypeError: loadAssets transform: Parameter 'url' must be a string, not undefined
    at Url.parse (url.js:118:11)
    at urlParse (url.js:112:5)
    at Object.urlResolve [as resolve] (url.js:402:10)
    at Object.urlTools.resolveUrl (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/util/urlTools.js:178:24)
    at Object.requireJsConfig.resolveUrl (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/registerRequireJsConfig.js:37:29)
    at Html.extendWithGettersAndSetters.findOutgoingRelationsInParseTree (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/assets/Html.js:244:71)
    at Html.outgoingRelations (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/assets/Asset.js:538:44)
    at Html.extendWithGettersAndSetters.populate (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/assets/Asset.js:583:17)
    at AssetGraph._.extend.addAsset (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/AssetGraph.js:161:15)
    at /usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/loadAssets.js:37:44
assetgraph-builder optimization failed. code: 8

Strange errors when missing dependencies

When forgetting to install all the dependencies to run assetgraph (say, pngquant), it results in a Error: EPIPE, Broken Pipe:

node.js:134
        throw e; // process.nextTick error, or 'error' event on first tick
        ^
Error: EPIPE, Broken pipe
    at Socket._writeImpl (net.js:159:14)
    at Socket._writeOut (net.js:444:25)
    at Socket.write (net.js:377:17)
    at pipeThroughChildProcessAndBuffer (.../node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/postProcessBackgroundImages.js:22:24)
    at .../node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/postProcessBackgroundImages.js:42:13
    at proceed (.../node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/postProcessBackgroundImages.js:103:13)
    at applyFilters (.../node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/postProcessBackgroundImages.js:111:5)
    at Function.<anonymous> (.../node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/postProcessBackgroundImages.js:147:17)
    at Function.<anonymous> (.../node_modules/assetgraph-builder/node_modules/seq/index.js:238:28)
    at action (.../node_modules/assetgraph-builder/node_modules/seq/index.js:76:11)
make: *** [http-pub-production] Error 1

Ideally, this should be converted into a friendly reminder that forgetful developers should install all the dependencies...

relations pointing to non-existing assets should not be added to the manifest file and shouldn't break the build process

I have a special case in my index.html file; there is one javascript that is specified as:

<script src="user"></script>

this points at a purposely non-existent static file, which forces it to be loaded from the API. so there is no file named 'user' in my assets directory. This causes buildProduction to break.

What I need is a way to provide some hinting and tell assetgraph builder "leave this one alone in the index.html file. just ignore it."

any ideas?

How to not combine inline script tags?

<head>
  <script>
    // 1
  </script>
<head>

<body>
  <script>
    // 2
  </script>
</body>

becomes:

<head>
  <script>
    // 1
    // 2
  </script>
<head>

<body>
</body>

I think there should be an option to cancel this behaviour.

Is there a workaround?

Any pointers on where to look to implement a fix.

Thanks loads

assetgraph-builder breaks absolute paths for css and js resources

My site has a deep hierarchy of paths, so I use absolute paths In my index.html:

<link href="/css/style.css" rel="stylesheet">
<script src="/js/vendor/modernizr/modernizr.js"></script>

I run buildProduction --outroot dist/ --root public/ public/index.html
which generates an index.html file that contains this:

<link rel="stylesheet" href="static/f01f97329c.css" /
<script src="static/8b4984bcd4.js">

Notice how my absolute paths have been turned into relative paths? This breaks my site when I'm viewing a page that isn't at the root level. For example, http://foo.com works, but http://foo.com/bar breaks because it expects the resources to be in /bar/static

However, the image references are still preserved as being absolute:

<img src="/static/3c7df4a3bc.png" alt="HelloSprout" title="HelloSprout" style="background:#fff" />

buildProduction doesn't understand UMD

When defining a module in a cross module compatible way using https://github.com/umdjs/umd buildProduction builds requirejs modules wrong.

This module:

(function (root, factory) {
    if (typeof module !== "undefined") {
        module.exports = factory();
    } else if (typeof root.define === 'function' && define.amd) {
        define(factory);
    } else {
        root.one = root.one || {};
        root.one.validation = factory();
    }
}(this, function () {
    return true;
}));

is built into:

(function(root, factory) {
    if (typeof module !== "undefined") {
        module.exports = factory();
    } else if (typeof root.define === "function" && define.amd) {
        define(factory);
    } else {
        root.one = root.one || {};
        root.one.validation = factory();
    }
})(this, function() {
    return true;
});

define("vendor/test", function() {});

Since there are more end more modules out there targeting multiple module definitions and falling back to vanilla js using the factory pattern, this is a showstopper when using vendor modules.

ping @papandreou

Guides for getting started

I would be nice with some guides for getting stared with the various parts of assetgraph. Eg.

  • Getting started with a basic static site.
  • Using LESS in development/production.
  • ...

buildProduction --version without previous content-version element

When running buildProduction on html that hasn't been generated by buildDevelopment, the meta tag describing the current version ends up being <meta http-equiv="Content-Version" content="/production" />

Where there should have been a version number there is nothing.

Missing less dependency

/home/munter/git/falcon-client-html/node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/compileLessToCss.js:12
        throw new Error('transforms.compileLessToCss: The "less" module is req
              ^
Error: transforms.compileLessToCss: The "less" module is required. Please run "npm install less" and try again (tested with version 1.2.1).

--locale kills 'nobundle' attribute

<!doctype html>

<html>
    <head>
        <script nobundle="nobundle">
            // navigation timing script
            var foo = new Date();
        </script>
    </head>

    <body>
        <script src="application.js"></script>

        <script nobundle="nobundle">
            // navigation timing script
            var bar = new Date();
        </script>
    </body>
</body>

Building the above with buildProduction --locale en_US bundles the script with the nobundle attribute

assetgraph-builder installation fails on windows7 due to canvas dependency

I'm trying to install assetgraph-builder on windows7 but the installation fails due to failure to install canvas (error text below). I understand canvas is required for assetgraph-sprite and it can't be installed on windows as it needs native compilation. Can the assetgraph-sprite dependency be made optional so that the rest of the build system can be used on windows?

[email protected] preinstall C:\Users\omkar.patil\AppData\Roaming\npm\node_modules\assetgraph-sprite\n
ode_modules\canvas
node-waf configure build

'node-waf' is not recognized as an internal or external command,
operable program or batch file.
npm ERR! error installing [email protected]
npm ERR! error installing [email protected]

npm ERR! [email protected] preinstall: node-waf configure build
npm ERR! cmd "/c" "node-waf configure build" failed with 1
npm ERR!
npm ERR! Failed at the [email protected] preinstall script.
npm ERR! This is most likely a problem with the canvas package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-waf configure build
npm ERR! You can get their info via:
npm ERR! npm owner ls canvas
npm ERR! There is likely additional logging output above.
npm ERR!
npm ERR! System Windows_NT 6.1.7601
npm ERR! command "C:\Program Files (x86)\nodejs\node.exe" "C:\Program Files (x86)\nodejs\nod
e_modules\npm\bin\npm-cli.js" "install" "-g" "assetgraph-sprite"
npm ERR! cwd C:\work\workspaces\sublime\coffeescript\gnie
npm ERR! node -v v0.6.12
npm ERR! npm -v 1.1.4
npm ERR! code ELIFECYCLE
npm ERR! message [email protected] preinstall: node-waf configure build
npm ERR! message cmd "/c" "node-waf configure build" failed with 1
npm ERR! errno {}

--cdnroot can't contain protocol relative urls

passing --cdnroot //app.falconsocial.comto buildProductionfails

From my Makefile:

CDN:=//app.falconsocial.com/static

production/%.html: http-pub/%.html.template $(DEPS) node_modules/.bin/buildProduction
    rm -rf $(@D)
    buildProduction \
        --root $(<D) \
        --outroot $(@D) \
        --optimizepngs \
        --version $(VERSION) \
        --asyncscripts \
        --locale $(LOCALE) \
        --cdnroot $(CDN) \
        --cdnoutroot $(@D)/static \
        $<

Assetgraph fails with: Error: EACCES, mkdir '/app.falconsocial.com'

why is upgrading from 1.4.0 to 1.4.1 breaking my deployment?

When I run assetgraph-builder with 1.4.1 or 1.4.2, I see this error in my output:

3.544 secs: populate
0.001 secs: fixBaseAssetsOfUnresolvedOutgoingRelationsFromHtmlFragments
0.064 secs: populate
0.001 secs: removeRelations
0.000 secs: removeRelations
0.004 secs: convertCssImportsToHtmlStyles

/usr/lib/node_modules/assetgraph-builder/bin/buildProduction:274
            throw err;
                  ^
Error: buildProduction transform: externalizeRelations transform: recomputeBaseAssets: Couldn't find base asset for [HtmlImage/98: [Html/2 file:///root/bytesamurai/web/public/templates/landing.html] => /img/preview/tab-slide1.png]
    at AssetGraph._.extend.recomputeBaseAssets (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/AssetGraph.js:424:27)
    at Array.forEach (native)
    at AssetGraph._.extend.recomputeBaseAssets (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/AssetGraph.js:421:55)
    at JavaScript.url (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/assets/Asset.js:453:37)
    at module.exports (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/externalizeRelations.js:8:33)
    at Array.forEach (native)
    at externalizeRelations (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/transforms/externalizeRelations.js:6:44)
    at _.extend._runTransform.callbackCalled (/usr/lib/node_modules/assetgraph-builder/node_modules/assetgraph/lib/AssetGraph.js:648:21)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

and here is my build command:

shell.exec 'buildProduction --optimizeimages --outroot '+tmp_dir+' --root web/public web/public/index.html web/public/te
mplates/landing.html web/public/templates/features.html web/public/templates/support.html --cdnroot '+cdn_host

What gives? This is really, really bad. I'm assuming that the API has changed between 1.4.0 and 1.4.1 in a breaking way?

You guys really need to follow semantic versioning carefully. http://semver.org
patch numbers are intended to be patches. You can't destabilize your API in a patch release.

Adapt spriting syntax/features to match transforms.processImages

  • Turn -ag-sprite-group into a GET parameter so that different images mentioned in the same CSS rule can be added to different sprites
  • Explore whether this syntax change makes the spriting applicable to sprite images referred to from eg. HTML. Eg. <img src="foo.png?sprite=abc"><img src="blank.png" style="background-image:url(sprite.png);background-position: ...">
  • Find out whether to reintroduce the possibility of processing sprited images after they have been generated

Localize CSS

Clone the asset for each locale and remove or keep CSS rules whose selectors match on <html lang="...">.

For American English that would turn

html[lang="en"] .myThing { width: 123px; }

into

.myThing { width: 123px; }

... but for non-English locales the rule would be removed.

Maybe also add a syntax for localizing the content property.

Any other ideas?

calling buildProduction on front end using requirejs fails

I've tried running assetgraph-builder against my requirejs backed code. I've run into some build problems:

Here is my the line from my index.html which includes require.js:

<script src="/js/vendor/requirejs/require.js" data-main="/js/app"></script>

According to requirejs documentation, the basePath should now be set to /js. Instead it seems like it's being set to the index.html directory (I'm assuming it's just using what I set for --outroot.) Here's some parts of my requirejs config (from /js/app.js):

# sets the require.js configuration for the application
require.config {
    paths:
        backbone         : 'vendor/backbone-amd/backbone' 
        deepmodel        : 'vendor/deep-model'
        ...
}

My project directory structure:

|- dist/
|- public/
   |- index.html
   `- js/
      |- app.js
      `- vendor/
         |- backbone-amd/
         |  `- backbone.js
         |- deep-model.js
         `- requirejs/
            `- require.js

My build command:

cd projectdir
buildProduction --outroot dist/ --root public/ public/index.html

Here are some of the errors I'm seeing:

file:///root/hellosprout/public/vendor/backbone-amd/backbone.js: ENOENT, open '/root/hellosprout/public/vendor/backbone-amd/backbone.js'
Including assets:
    file:///root/hellosprout/public/js/app.js

file:///root/hellosprout/public/vendor/deep-model.js: ENOENT, open '/root/hellosprout/public/vendor/deep-model.js'
Including assets:
    file:///root/hellosprout/public/js/models/cart.js

Make buildProduction independent og buildDevelopment

Currently buildDevelopment and buildProduction populate the graph in different ways.

buildProduction assumes that buildDevelopment has already added script and css includes from one.include into the html, and is told to ignore these inclusions.

It would be nice if buildProduction wasn't chained to buildDevelopment

If buildProduction followed JavaScriptOneInclude, JavaScriptExtJsRequire and JavaScriptCommonJsRequire relations during population and the build was based on the output of buildDevelopment, wouldn't assetgraph notice that the assets are identical and merge them?

https://github.com/One-com/assetgraph-builder/blob/master/bin/buildDevelopment#L36-39
https://github.com/One-com/assetgraph-builder/blob/master/bin/buildProduction#L145-146

file not found errors for a couple js scripts when calling buildProduction

Here is my command:

buildProduction --optimizeimages --outroot '+tmp_dir+' --root web/public web/public/index.html  --cdnroot '+cdn_host

And here's the output:

0.000 secs: registerRequireJsConfig
0.002 secs: registerLabelsAsCustomProtocols
0.127 secs: loadAssets
file:///root/whim/web/public/user.js: ENOENT, open '/root/whim/web/public/user.js'
Including assets:
    file:///root/whim/web/public/index.html

file:///root/whim/web/public/underscore.js: ENOENT, open '/root/whim/web/public/underscore.js'
Including assets:
    file:///root/whim/web/public/js/vendor/backbone-deep-model-0.8.0.js

file:///root/whim/web/public/backbone.js: ENOENT, open '/root/whim/web/public/backbone.js'
Including assets:
    file:///root/whim/web/public/js/vendor/backbone-deep-model-0.8.0.js

1.974 secs: populate
0.001 secs: removeRelations
0.000 secs: removeRelations
0.001 secs: convertCssImportsToHtmlStyles
0.006 secs: externalizeRelations
0.003 secs: mergeIdenticalAssets
0.093 secs: processImages
assetgraph-sprite: Canvas not found, skipping
0.000 secs: spriteBackgroundImages
0.679 secs: processImages
0.010 secs: inlineKnockoutJsTemplates
0.011 secs: bundleRequireJs
0.000 secs: removeDuplicateHtmlStyles
0.009 secs: bundleRelations
0.021 secs: bundleRelations
0.000 secs: removeNobundleAttribute
0.146 secs: inlineCssImagesWithLegacyFallback
0.168 secs: minifyAssets
0.000 secs: removeAssets
0.001 secs: removeAssets
2.351 secs: compressJavaScript
0.164 secs: inlineRelations
0.004 secs: inlineAngularJsTemplates
0.002 secs: inlineKnockoutJsTemplates
0.000 secs: setAsyncOrDeferOnHtmlScripts
0.000 secs: omitGetStaticUrlFunctionCall
0.000 secs: inlineRelations
0.051 secs: moveAssetsInOrder
5.811 secs: buildProduction
0.004 secs: writeAssetsToDisc
0.030 secs: writeAssetsToDisc
      Html  1   3.1 KB
       Png  5   8.8 KB
       Gif  1  12.2 KB
       Xml 19  91.0 KB
       Css  2  48.0 KB
JavaScript  2 136.3 KB
    Total: 30 299.5 KB
0.001 secs: writeStatsToStderr

I see 3 errors. The first one is expected because web/public/user.js is intentionally missing from my static resources (forces it to be pulled from API at run time)

However the two other errors seem strange. (backbone.js and underscore.js)

I have these 2 files in my public/ directory:

public/
|- js/
    `- vendor/
        |- backbone-0.9.2.min.js
        `- underscore-1.3.3.min.js

and here are my script declarations in index.html:

<script src="js/vendor/underscore-1.3.3.min.js"></script> 
<script src="js/vendor/backbone-0.9.2.min.js"></script>

any idea why this is happening?

is there a way to force assetgraph-builder to process script tags as html templates?

I'm using backbonejs in one of my apps, and have several script tags that contain html templates. Here's an example of one of the template scripts, embedded in my index.html:

<script type="text/template" id="about-template">
        <h1>welcome back, <%- username %>!
        <img src="img/icn-logo.png" class="whim-logo" alt="logo" />
</script>

in my backbone view, I basically load this as you might expect:

data = $('#about-template').html()
template = _.template data
$('#about').html @template( { username: 'mike' } )

Is there some way I can use the GETSTATICURL to cause these references to be satisfied?

I'm sorry to keep flooding you folks with github issues. : (

Missing copyright header in minified javascript

Thanks for a great tool!

When i run

./node_modules/assetgraph-builder/bin/buildProduction --outroot dir \
  --root anotherdir --version `git log --pretty=format:'%h' -n 1` -less true 

Everything works as expected except for that I don't the copyright headers is stript from the minified js file. Is this by design (are there a way to get the copyright headers?) If not I would regard this as an bug.

Knockout templates included twice

buildProduction gives me this error:

Error: bundleRequireJs: More than one of the loaded Knockout.js templates have the file name samples.ko, so they will overwrite each other in the ko.externalTemplateEngine.templates hash. Please disambiguate by changing at least one of the file names.

The reason is that I'm depending on knockout templates using the requirejs tpl!-notation.
Two different views depend on the same template. So it's not a name clash. Assetgraph just doesn't seem to realize that the two templates are the same file.

Create a saveWebpage binary

It would be nice to have a binary that could scrape a given remote web page so you could work on optimization locally.

Translation inserts whitespaces

I have the following html

                                <label for="login-email"><span data-i18n="email">Email</span>:</label>

This builds into

<label for="login-email">
                  Email
:</label>

Somewhere along the way in the build process two newlines and an indentation got added to the html.

I am assuming this is caused by the html being prettyprinted along the way, then translated, then minified, but since the span is replaced with a textnode the minifier can't make assumtions on collapsing white spaces.

Ideally these whitespaces should not be there. And I can't make head or tails of the number of them. I indented with 32 spaces and the reult is indented by 18

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.