Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 1fb37824c5d3b4f00dc7b56d745b209464912a07 @Munter committed Jan 23, 2013
Showing with 347 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +2 −0 .npmignore
  3. +86 −0 bin/assetviz
  4. +123 −0 index.js
  5. +27 −0 package.json
  6. 0 tpl/data.js
  7. +33 −0 tpl/graph.css
  8. +57 −0 tpl/graph.js
  9. +14 −0 tpl/index.html
  10. +4 −0 tpl/vendor/d3.v3.min.js
@@ -0,0 +1 @@
+node_modules
@@ -0,0 +1,2 @@
+node_modules
+.npmignore
@@ -0,0 +1,86 @@
+#!/usr/bin/env node
+
+var optimist = require('optimist'),
+ commandLineOptions = optimist
+ .usage('Output a graph visualization html of your web application to stdout\n$0 [-r <inputRootDirectory>] <htmlFile(s)>')
+ .options('h', {
+ alias: 'help',
+ describe: 'Show this help',
+ type: 'boolean',
+ default: false
+ })
+ .options('r', {
+ alias: 'root',
+ describe: 'Path to your web root. Omission will make $0 take a best guess',
+ type: 'string',
+ demand: false
+ })
+ .wrap(72)
+ .argv;
+
+if (commandLineOptions.h || commandLineOptions._.length === 0) {
+ optimist.showHelp();
+ process.exit(1);
+}
+
+var urlTools = require('assetgraph/lib/util/urlTools'),
+ rootUrl = commandLineOptions.root && urlTools.urlOrFsPathToUrl(commandLineOptions.root, true),
+ inputUrls;
+
+if (commandLineOptions._.length > 0) {
+ inputUrls = commandLineOptions._.map(function (urlOrFsPath) {
+ return urlTools.urlOrFsPathToUrl(urlOrFsPath, false);
+ });
+ if (!rootUrl) {
+ rootUrl = urlTools.findCommonUrlPrefix(inputUrls.filter(function (inputUrl) {
+ return (/^file:/).test(inputUrl);
+ }));
+ }
+} else if (rootUrl && /^file:/.test(rootUrl)) {
+ inputUrls = [rootUrl + '**/*.html'];
+ console.warn('No input files specified, defaulting to ' + inputUrls[0]);
+} else {
+ throw new Error("No input files and no --root specified (or it isn't file:), cannot proceed");
+}
+
+var AssetGraph = require('assetgraph'),
+ query = AssetGraph.query,
+ followRelationsQueryObj = query.or({to: {type: 'I18n'}}, {
+ type: query.not([
+ 'JavaScriptInclude',
+ 'JavaScriptExtJsRequire',
+ 'JavaScriptCommonJsRequire',
+ 'HtmlAnchor'
+ ]),
+ to: {
+ url: query.not(/^https?:/)
+ }
+ }),
+ assetviz = require('../index.js');
+
+AssetGraph.registerTransform(assetviz, 'assetviz');
+
+
+new AssetGraph({root: rootUrl})
+ .on('error', function (err) {
+ console.error((err.asset ? err.asset.urlOrDescription + ': ' : '') + err.stack);
+ process.exit(1);
+ })
+ .registerRequireJsConfig()
+ .loadAssets(inputUrls)
+ .populate({followRelations: followRelationsQueryObj})
+ .queue(function fixBaseAssetsOfUnresolvedOutgoingRelationsFromHtmlFragments(assetGraph) {
+ assetGraph.findRelations({from: {type: 'Html', isFragment: true, isInitial: true}}, true).forEach(function (relation) {
+ if (relation._baseAssetPath === null) {
+ delete relation._baseAssetPath;
+ }
+ });
+ })
+ .populate({followRelations: followRelationsQueryObj, startAssets: {type: 'Html', isFragment: true, isInitial: true}})
+ .assetviz()
+ //.writeStatsToStderr()
+ .run(function (err) {
+ if (err) {
+ throw err;
+ }
+ });
123 index.js
@@ -0,0 +1,123 @@
+var Path = require('path'),
+ fs = require('fs'),
+ relationLabelByType = {
+ HtmlScript: '<script>',
+ HtmlStyle: '<style>',
+ HtmlCacheManifest: '<html manifest>',
+ HtmlIFrame: '<iframe>',
+ HtmlFrame: '<frame>',
+ HtmlAlternateLink: '<link rel=alternate>',
+ HtmlConditionalComment: function (htmlConditionalComment) {return '<!--[if ' + htmlConditionalComment.condition + ']>';},
+ HtmlImage: '<img>',
+ HtmlAudio: '<audio>',
+ HtmlShortcutIcon: 'icon',
+ HtmlVideo: '<video>',
+ HtmlVideoPoster: '<video poster>',
+ HtmlEmbed: '<embed>',
+ HtmlApplet: '<applet>',
+ HtmlObject: '<object>',
+ HtmlEdgeSideInclude: '<esi:include>',
+ HtmlAnchor: '<a>',
+ HtmlRequireJsMain: 'data-main',
+ JavaScriptInclude: 'INCLUDE',
+ JavaScriptGetText: 'GETTEXT',
+ JavaScriptGetStaticUrl: 'GETSTATICURL',
+ JavaScriptAmdDefine: 'define',
+ JavaScriptAmdRequire: 'require',
+ CssImage: 'background-image',
+ CssImport: '@import',
+ CssBehavior: 'behavior',
+ CssFontFaceSrc: '@font-face src',
+ CssAlphaImageLoader: 'AlphaImageLoader'
+ };
+
+module.exports = function (name) {
+ name = (name || 'assetviz');
+ var targetFileName = name + '.html',
+ data = {
+ assets: [],
+ relations: []
+ };
+
+ return function drawGraph(assetGraph, cb) {
+ var idx = 0,
+ vizGraph = new assetGraph.constructor({ root: './tpl' }),
+ query = assetGraph.constructor.query;
+
+ assetGraph.findAssets().forEach(function (asset) {
+ if (asset.url || asset.outgoingRelations.length) {
+ asset.idx = idx;
+ data.assets.push({
+ path: asset.url ? Path.relative(assetGraph.root, asset.url) : '',
+ fileName: (asset.url ? Path.basename(asset.url) : 'i:' + asset).replace(/"/g, '\\"'),
+ type: asset.type.toLowerCase()
+ });
+ idx += 1;
+ }
+ });
+
+ assetGraph.findRelations({}, true).forEach(function (relation) {
+ if ('idx' in relation.from && 'idx' in relation.to) {
+ var typeString = relationLabelByType[relation.type] || '';
+ if (typeof typeString === 'function') {
+ typeString = typeString(relation);
+ }
+ data.relations.push({
+ source: relation.from.idx,
+ target: relation.to.idx,
+ type: typeString
+ });
+ }
+ });
+
+ var dataString = 'var assetgraph = ' + JSON.stringify(data, undefined, 4);
+
+ vizGraph.on('error', function (err) {
+ console.error((err.asset ? err.asset.urlOrDescription + ': ' : '') + err.stack);
+ process.exit(1);
+ })
+ .loadAssets([
+ 'index.html'
+ ])
+ .populate({
+ followRelations: {
+ type: query.not(['HtmlAnchor'])
+ }
+ })
+ .queue(function injectAssetData(assetGraph) {
+ assetGraph.findAssets({
+ type: 'JavaScript',
+ fileName: 'data.js'
+ })[0].text = dataString;
+ })
+ .inlineRelations({
+ type: ['CssImage', 'HtmlImage', 'HtmlShortcutIcon', 'HtmlAppleTouchStartupImage']
+ })
+ .inlineRelations({
+ type: ['CssFontFaceSrc']
+ })
+ .inlineRelations({
+ type: ['CssBehavior']
+ })
+ /*
+ .compressJavaScript({type: 'JavaScript'}, 'uglifyJs')
+ .minifyAssets({
+ type: ['Html', 'JavaScript', 'Css']
+ })
+ */
+ // inline style and script
+ .inlineRelations({ type: ['HtmlStyle', 'HtmlScript'] })
+ .moveAssets({type: 'Html', isInline: false}, function (asset) {
+ return "file://" + Path.normalize(targetFileName);
+ })
+ .writeAssetsToDisc({url: /^file:/})
+ .writeStatsToStderr()
+ .run(function (err) {
+ if (err) {
+ console.error(err);
+ }
+ });
+
+ cb();
+ };
+};
@@ -0,0 +1,27 @@
+{
+ "name": "assetviz",
+ "version": "0.0.1",
+ "description": "A graph visualization of the assets and relations in yout web app",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/munter/assetviz.git"
+ },
+ "keywords": [
+ "dependency",
+ "graph",
+ "asset",
+ "assetgraph",
+ "visualization"
+ ],
+ "author": "Peter Müller <munter@fumle.dk>",
+ "license": "BSD",
+ "dependencies": {
+ "assetgraph": "=1.3.2",
+ "optimist": "*"
+ },
+ "bin": "./bin/assetviz"
+}
No changes.
@@ -0,0 +1,33 @@
+body {
+ background: #F6F6F6;
+ padding:0;
+ margin:0;
+ font-family:helvetica,arial;
+ font-size: 10px;
+ overflow: hidden;
+}
+
+.graph {
+ width:100%;
+ height: 100%;
+}
+
+path {
+ stroke-width: 1;
+ stroke: #ccc;
+}
+
+text {
+ pointer-events: none;
+}
+
+circle {
+ fill: #ccc;
+ stroke: #666;
+ stroke-width: 1px;
+}
+
+circle.gif {
+ fill: #cfc;
+ stroke: #696;
+}
@@ -0,0 +1,57 @@
+/*global d3, assetgraph*/
+
+var svg = d3.select('.graph'),
+ force = d3.layout.force()
+ .nodes(d3.values(assetgraph.assets))
+ .links(assetgraph.relations)
+ .size([window.innerWidth, window.innerHeight]) // Some browsers have trouble reading dimensions of svg elements
+ .linkDistance(180)
+ .charge(-100);
+
+
+var edges = svg.append('g')
+ .attr('class', 'relations')
+ .selectAll('path')
+ .data(force.links())
+ .enter()
+ .append('path')
+ .attr('id', function (d, i) { return 'p' + i; });
+
+var edgeLabels = svg.append('g')
+ .attr('class', 'relationLabels')
+ .selectAll('text')
+ .data(force.links()).enter()
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .append('textPath')
+ .attr('startOffset', '50%')
+ .attr('xlink:href', function (d, i) { return '#p' + i; })
+ .text(function (d) { return d.type; });
+
+var nodes = svg.append('g')
+ .attr('class', 'assets')
+ .selectAll('g')
+ .data(force.nodes()).enter()
+ .append('g');
+
+nodes.append('circle')
+ .attr('r', 6)
+ .attr('class', function (d) { return d.type; })
+ .call(force.drag);
+
+nodes.append('text')
+ .attr('text-anchor', 'middle')
+ .attr('dy', function () { return -10; })
+ .text(function (d) { return d.fileName; });
+
+
+force.on('tick', function () {
+ edges.attr('d', function (d) {
+ return "M" + d.source.x + "," + d.source.y +
+ " " + d.target.x + "," + d.target.y;
+ });
+
+ nodes.attr("transform", function (d) {
+ return "translate(" + d.x + "," + d.y + ")";
+ });
+}).start();
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <title>assetGraph</title>
+ <link rel="stylesheet" type="text/css" href="graph.css">
+ </head>
+
+ <body>
+ <svg class="graph"></svg>
+
+ <script src="vendor/d3.v3.min.js"></script>
+ <script src="data.js"></script>
+ <script src="graph.js"></script>
+ </body>
+</html>

Large diffs are not rendered by default.

Oops, something went wrong.

0 comments on commit 1fb3782

Please sign in to comment.