lib/transforms/bundleRelations.js

lib/ AssetGraph.js errors.js index.js query.js
assets/ Asset.js Atom.js CacheManifest.js CoffeeScript.js Css.js Flash.js Gif.js Htc.js Html.js I18n.js Ico.js Image.js JavaScript.js Jpeg.js Json.js KnockoutJsTemplate.js Less.js Png.js Rss.js StaticUrlMap.js Stylus.js Text.js Xml.js index.js
relations/ CacheManifestEntry.js CssAlphaImageLoader.js CssBehavior.js CssFontFaceSrc.js CssImage.js CssImport.js CssUrlTokenRelation.js HtmlAlternateLink.js HtmlAnchor.js HtmlAppleTouchStartupImage.js HtmlApplet.js HtmlAudio.js HtmlCacheManifest.js HtmlConditionalComment.js HtmlDataBindAttribute.js HtmlEdgeSideInclude.js HtmlEmbed.js HtmlFrame.js HtmlIFrame.js HtmlIFrameSrcDoc.js HtmlImage.js HtmlInlineScriptTemplate.js HtmlKnockoutContainerless.js HtmlObject.js HtmlRelation.js HtmlRequireJsMain.js HtmlScript.js HtmlShortcutIcon.js HtmlStyle.js HtmlStyleAttribute.js HtmlVideo.js HtmlVideoPoster.js JavaScriptAmdDefine.js JavaScriptAmdRequire.js JavaScriptCommonJsRequire.js JavaScriptExtJsRequire.js JavaScriptGetStaticUrl.js JavaScriptGetText.js JavaScriptInclude.js JavaScriptShimRequire.js JavaScriptTrHtml.js Relation.js StaticUrlMapEntry.js index.js
resolvers/ data.js extJs4Dir.js file.js fixedDirectory.js http.js index.js javascript.js
transforms/ addCacheManifest.js bundleRelations.js bundleRequireJs.js compileCoffeeScriptToJavaScript.js compileLessToCss.js compileStylusToCss.js compressJavaScript.js convertCssImportsToHtmlStyles.js convertHtmlStylesToInlineCssImports.js convertStylesheetsToInlineStyles.js drawGraph.js executeJavaScriptInOrder.js externalizeRelations.js flattenStaticIncludes.js inlineCssImagesWithLegacyFallback.js inlineRelations.js loadAssets.js mergeIdenticalAssets.js minifyAssets.js moveAssets.js moveAssetsInOrder.js populate.js prettyPrintAssets.js pullGlobalsIntoVariables.js registerRequireJsConfig.js removeAssets.js removeRelations.js setAssetContentType.js setAssetEncoding.js setAssetExtension.js setHtmlImageDimensions.js startOverIfAssetSourceFilesChange.js writeAssetsToDisc.js writeAssetsToStdout.js writeStatsToStderr.js
util/ deepCopy.js extendWithGettersAndSetters.js fsTools.js getImageInfoFromBuffers.js memoizeAsyncAccessor.js uniqueId.js urlTools.js
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