lasso
Version:
Lasso.js is a build tool and runtime library for building and bundling all of the resources needed by a web application
275 lines (220 loc) • 9.97 kB
JavaScript
var Bundle = require('./Bundle');
var InlinePos = require('./InlinePos');
var EventEmitter = require('events').EventEmitter;
var ok = require('assert').ok;
function safeRelativePath(path) {
return path.replace(/[^A-Za-z0-9_.\-\/\\$@]/g, '_');
}
var BundleMappings = function(config, pageName) {
BundleMappings.$super.call(this);
ok(pageName == null || typeof pageName === 'string', 'pageName should be a String');
this.config = config;
this.dependencyToBundleMapping = {};
this.bundlesByKey = {};
this.inPlaceDeploymentEnabled = config.isInPlaceDeploymentEnabled();
this.bundlingEnabled = config.bundlingEnabled !== false;
this.pageName = pageName;
};
BundleMappings.prototype = {
__BundleMappings: true,
setParentBundleMappings: function(parentBundleMappings) {
if (parentBundleMappings && !parentBundleMappings.__BundleMappings) {
throw new Error('Invalid parent bundle mappings');
}
if (parentBundleMappings === this) {
throw new Error('parent bundle mappings cannot be self');
}
// The parent bundle mappings are those defined at configuration time
// and we may not have actually
this.parentBundleMappings = parentBundleMappings;
},
getBundleMappingForDependency: function(dependency) {
var key = dependency.getKey();
var bundleMapping = this.dependencyToBundleMapping[key];
if (bundleMapping) {
return bundleMapping;
} else if (this.parentBundleMappings) {
return this.parentBundleMappings.getBundleMappingForDependency(dependency);
} else {
return undefined;
}
},
getBundleForDependency: function(dependency) {
var key = dependency.getKey();
var bundleMapping = this.dependencyToBundleMapping[key];
if (bundleMapping) {
return bundleMapping.bundle;
} else if (this.parentBundleMappings) {
return this.parentBundleMappings.getBundleForDependency(dependency);
} else {
return undefined;
}
},
removeBundleMapping: function(bundleMapping) {
var dependency = bundleMapping.dependency;
delete bundleMapping.bundleMappings.dependencyToBundleMapping[dependency.getKey()];
bundleMapping.bundle.removeDependencyByIndex(bundleMapping.index);
},
addDependencyToBundle: function(dependency, targetBundleName, dependencySlot, bundleConfig, lassoContext) {
ok(lassoContext, 'lassoContext expected');
var targetBundle;
if (dependency.isPackageDependency()) {
throw new Error('Illegal argument. Dependency cannot be a package dependency. Dependency: ' + dependency.toString());
}
var inlinePos = dependency.inline;
if (inlinePos != null) {
if (inlinePos === 'true' || inlinePos === true || inlinePos === 'end') {
inlinePos = InlinePos.END;
} else if (inlinePos === 'beginning') {
inlinePos = InlinePos.BEGINNING;
} else if (inlinePos === 'in-place') {
inlinePos = InlinePos.IN_PLACE;
} else if (inlinePos === 'false' || inlinePos === false) {
// normalize false to undefined (really no need to set inline to false since it is the default)
inlinePos = undefined;
} else {
throw new Error('Invalid value for "inline": ' + inlinePos);
}
}
var bundleKey = Bundle.getKey(dependencySlot, dependency.getContentType(), inlinePos, targetBundleName);
targetBundle = this.bundlesByKey[bundleKey];
if (!targetBundle) {
targetBundle = new Bundle(targetBundleName);
targetBundle.key = bundleKey;
targetBundle.setInlinePos(inlinePos);
targetBundle.setSlot(dependencySlot);
targetBundle.setContentType(dependency.getContentType());
targetBundle.setUrl(dependency.getUrl ? dependency.getUrl() : dependency.url);
if (bundleConfig) {
targetBundle.setConfig(bundleConfig);
}
this.bundlesByKey[bundleKey] = targetBundle;
}
var index = targetBundle.addDependency(dependency);
var bundleMapping = {
// store the index of the dependency within the bundle
index: index,
// store the bundle associated with the mapping
bundle: targetBundle,
// store the bundle mapping
bundleMappings: this,
// store the dependency associated with the mapping
dependency: dependency
};
this.dependencyToBundleMapping[dependency.getKey()] = bundleMapping;
dependency.emit('addedToBundle', {
bundle: targetBundle,
lassoContext: lassoContext
});
return targetBundle;
},
addDependencyToPageBundle: function(dependency, pageBundleName, dependencySlot, bundleConfig, lassoContext) {
ok(lassoContext, 'lassoContext expected');
if (dependency.isPackageDependency()) {
throw new Error('Illegal argument. Dependency cannot be a package dependency. Dependency: ' + dependency.toString());
}
var bundle;
var defaultBundleName = dependency.getDefaultBundleName(pageBundleName, lassoContext);
var flags = lassoContext.flags;
if (this.inPlaceDeploymentEnabled && dependency.isInPlaceDeploymentAllowed()) {
// Create a bundle with a single dependency for each dependency
// that allows in-place deployment
if (!dependency.getSourceFile) {
throw new Error('getSourceFile() is required when in-place deployment is allowed. Dependency: ' + dependency);
}
bundle = this.addDependencyToBundle(
dependency,
dependency.getSourceFile(),
dependencySlot,
bundleConfig,
lassoContext);
bundle.dependency = dependency;
bundle.inPlaceDeployment = true;
} else if (dependency.isExternalResource()) {
bundle = this.addDependencyToBundle(
dependency,
dependency.getUrl(),
dependencySlot,
bundleConfig,
lassoContext);
bundle.dependency = dependency;
bundle.isExternalResource = true;
} else if (this.bundlingEnabled === false) {
// Bundling is NOT enabled
// We will try to find a relative path that will be used for
// the output file of the bundle.
// This relative path might be different from the bundle name
// if a defaultBundleName is provided
//
// NOTE: If we don't have a defaultBundleName for this dependency
// and if we don't find a relative path then we will use
// `${dependencyType}-${pageBundleName}` as the
// bundle name.
var targetBundle;
if (dependency.getUnbundledTarget) {
targetBundle = dependency.getUnbundledTarget(lassoContext);
}
if (!targetBundle && dependency.getSourceFile) {
let sourceFile = dependency.getSourceFile();
if (sourceFile) {
targetBundle = lassoContext.getClientPath(sourceFile);
}
}
if (targetBundle) {
targetBundle = safeRelativePath(targetBundle);
var prefix = pageBundleName.replace(/[\\\/]/g, '-');
if (flags && !flags.isEmpty()) {
prefix += '-' + flags.getKey();
}
if (dependency.getUnbundledTargetPrefix) {
let unbundledTargetPrefix = dependency.getUnbundledTargetPrefix(lassoContext);
if (unbundledTargetPrefix) {
prefix += '/' + unbundledTargetPrefix;
}
}
targetBundle = prefix + '/' + targetBundle;
}
var finalBundleName = defaultBundleName || targetBundle;
if (!finalBundleName) {
finalBundleName = dependency.type + '-' + pageBundleName;
if (flags && !flags.isEmpty()) {
finalBundleName += '-' + flags.getKey();
}
}
bundle = this.addDependencyToBundle(
dependency,
finalBundleName,
dependencySlot,
null,
lassoContext);
// bundle.dependency = dependency;
if (targetBundle) {
// We associate this bundle with a relative path which will
// be used as the output file for the bundle
bundle.relativeOutputPath = targetBundle;
}
} else {
// Bundling is enabled
// Make sure the dependency is part of a bundle. If it not part of a preconfigured bundle then put it in a page-specific bundle
bundle = this.addDependencyToBundle(
dependency,
defaultBundleName || pageBundleName,
dependencySlot,
bundleConfig,
lassoContext);
}
return bundle;
},
toString: function() {
var lines = [];
for (var k in this.dependencyToBundleMapping) {
if (this.dependencyToBundleMapping.hasOwnProperty(k)) {
var targetBundle = this.dependencyToBundleMapping[k].bundle;
lines.push(k + ' --> ' + targetBundle.toString());
}
}
return lines.join('\n');
}
};
require('raptor-util').inherit(BundleMappings, EventEmitter);
module.exports = BundleMappings;