|
var _ = require('underscore'),
urlTools = require('../util/urlTools'),
assets = require('../assets'),
relations = require('../relations'),
cssom = require('cssom-papandreou'),
bundleStrategyByName = {};
// Internal helper function. Reuses the parse trees of existing assets, so be careful!
function makeBundle(assetGraph, assetsToBundle) {
if (assetsToBundle.length === 0) {
throw new Error('makeBundle: Bundle must contain at least one asset');
} else if (assetsToBundle.length === 1) {
// Shortcut
return [assetsToBundle[0]];
}
var type = assetsToBundle[0].type,
incomingType = {Css: 'HtmlStyle', 'JavaScript': 'HtmlScript'}[type],
parseTrees = _.pluck(assetsToBundle, 'parseTree'),
constructorOptions = {};
if (type === 'JavaScript') {
var topLevelStatements = [];
assetsToBundle.forEach(function (asset) {
assetGraph.findRelations({from: asset, type: 'JavaScriptInclude'}, true).forEach(function (relation) {
if (relation.parentNode === asset.parseTree[1]) {
// This INCLUDE relation is a top level statement, update its .parentNode property to point at
// the top level statements array of the bundle:
relation.parentNode = topLevelStatements;
}
});
Array.prototype.push.apply(topLevelStatements, asset.parseTree[1]);
});
constructorOptions.parseTree = ['toplevel', topLevelStatements];
constructorOptions.copyrightNoticeComments = [];
assetsToBundle.forEach(function (javaScript) {
if (javaScript.copyrightNoticeComments) {
Array.prototype.push.apply(constructorOptions.copyrightNoticeComments, javaScript.copyrightNoticeComments);
}
});
} else {
// type === 'Css'
constructorOptions.parseTree = new cssom.CSSStyleSheet();
assetsToBundle.forEach(function (asset) {
asset.parseTree.parentStyleSheet = constructorOptions.parseTree;
Array.prototype.push.apply(constructorOptions.parseTree.cssRules, asset.parseTree.cssRules);
});
}
var bundleAsset = new assets[type](constructorOptions);
bundleAsset.url = urlTools.resolveUrl(assetGraph.root, bundleAsset.id + bundleAsset.extension);
bundleAsset._outgoingRelations = assetGraph.findRelations({from: assetsToBundle});
bundleAsset._outgoingRelations.forEach(function (outgoingRelation) {
outgoingRelation.remove();
outgoingRelation.from = bundleAsset;
});
var seenReferringAssets = {},
incomingRelations = assetGraph.findRelations({type: incomingType, to: assetsToBundle});
incomingRelations.forEach(function (incomingRelation) {
if (!(incomingRelation.from.id in seenReferringAssets)) {
var bundleRelation = new relations[incomingType]({
to: bundleAsset
});
bundleRelation.attach(incomingRelation.from, 'before', incomingRelation);
if (incomingType === 'HtmlStyle') {
var media = incomingRelation.node.getAttribute('media');
if (media && media !== 'all') {
bundleRelation.node.setAttribute('media', media);
bundleRelation.from.markDirty();
}
} else if (incomingType === 'HtmlScript') {
for (var i = 0 ; i < incomingRelations.length ; i += 1) {
var otherIncomingRelation = incomingRelations[i];
if (otherIncomingRelation.node.hasAttribute('data-main')) {
var existingHtmlRequireJsMainRelations = assetGraph.findRelations({
from: otherIncomingRelation.from,
type: 'HtmlRequireJsMain'
}).filter(function (relation) {
return relation.node === otherIncomingRelation.node;
});
if (existingHtmlRequireJsMainRelations.length === 1) {
var existingHtmlRequireJsMainRelation = existingHtmlRequireJsMainRelations[0];
bundleRelation.node.setAttribute('data-main', otherIncomingRelation.node.getAttribute('data-main'));
existingHtmlRequireJsMainRelation.node = bundleRelation.node;
break;
} else {
throw new Error("transforms.bundleRelations: Unexpected number of existing HtmlRequireJsMain relations found");
}
}
}
}
seenReferringAssets[incomingRelation.from.id] = true;
}
incomingRelation.detach();
});
assetGraph.addAsset(bundleAsset);
assetsToBundle.forEach(function (asset) {
if (assetGraph.findRelations({to: asset}).length === 0) {
assetGraph.removeAsset(asset);
}
});
return bundleAsset;
}
// Quick and dirty bundling strategy that gets you down to one
|