384 lines
12 KiB
JavaScript
384 lines
12 KiB
JavaScript
define([
|
|
"../buildControl",
|
|
"../fileUtils",
|
|
"../fs",
|
|
"dojo/_base/lang",
|
|
"dojo/json"
|
|
], function(bc, fileUtils, fs, lang, json){
|
|
var
|
|
computingLayers
|
|
// the set of layers being computed; use this to detect circular layer dependencies
|
|
= {},
|
|
|
|
computeLayerContents = function(
|
|
layerModule,
|
|
include,
|
|
exclude
|
|
){
|
|
// add property layerSet (a set of mid) to layerModule that...
|
|
//
|
|
// * includes the layerModule itself
|
|
// * includes dependency tree of layerModule
|
|
// * includes all modules in layerInclude and their dependency trees
|
|
// * excludes all modules in layerExclude and their dependency trees
|
|
//
|
|
// note: layerSet is built exactly as given above, so included modules that are later excluded
|
|
// are *not* in result layerSet
|
|
|
|
if(computingLayers[layerModule.mid]){
|
|
bc.log("amdCircularDependency", ["module", layerModule.mid]);
|
|
return {};
|
|
}
|
|
computingLayers[layerModule.mid] = 1;
|
|
|
|
var
|
|
includeSet = {},
|
|
visited,
|
|
includePhase,
|
|
traverse = function(module){
|
|
var mid = module.mid;
|
|
|
|
if(visited[mid]){
|
|
return;
|
|
}
|
|
visited[mid] = 1;
|
|
if(includePhase){
|
|
includeSet[mid] = module;
|
|
}else{
|
|
delete includeSet[mid];
|
|
}
|
|
if(module!==layerModule && module.layer){
|
|
var layerModuleSet = module.moduleSet || computeLayerContents(module, module.layer.include, module.layer.exclude);
|
|
for(var p in layerModuleSet){
|
|
if(includePhase){
|
|
includeSet[p] = layerModuleSet[p];
|
|
}else{
|
|
delete includeSet[p];
|
|
}
|
|
}
|
|
}else{
|
|
for(var deps = module.deps, i = 0; deps && i<deps.length; traverse(deps[i++])){
|
|
}
|
|
}
|
|
};
|
|
|
|
visited = {};
|
|
includePhase = true;
|
|
traverse(layerModule);
|
|
include.forEach(function(mid){
|
|
var module = bc.amdResources[bc.getSrcModuleInfo(mid, layerModule).mid];
|
|
if(!module){
|
|
bc.log("amdMissingLayerIncludeModule", ["missing", mid, "layer", layerModule.mid]);
|
|
}else{
|
|
traverse(module);
|
|
}
|
|
});
|
|
|
|
visited = {};
|
|
includePhase = false;
|
|
exclude.forEach(function(mid){
|
|
var module = bc.amdResources[bc.getSrcModuleInfo(mid, layerModule).mid];
|
|
if(!module){
|
|
bc.log("amdMissingLayerExcludeModule", ["missing", mid, "layer", layerModule.mid]);
|
|
}else{
|
|
traverse(module);
|
|
}
|
|
});
|
|
|
|
layerModule.moduleSet = includeSet;
|
|
delete computingLayers[layerModule.mid];
|
|
|
|
// return a copy so that clients can not mutate layerModule.moduleSet
|
|
var result = {};
|
|
for(var p in includeSet){
|
|
result[p] = includeSet[p];
|
|
}
|
|
return result;
|
|
},
|
|
|
|
insertAbsMid = function(
|
|
text,
|
|
resource
|
|
){
|
|
return (!resource.mid || resource.tag.hasAbsMid || !bc.insertAbsMids) ?
|
|
text : text.replace(/(define\s*\(\s*)(.*)/, "$1\"" + resource.mid + "\", $2");
|
|
},
|
|
|
|
pushString = function(
|
|
strings,
|
|
pair
|
|
){
|
|
strings[pair[0]] = pair[1];
|
|
},
|
|
|
|
appendStringsToCache = function(
|
|
strings,
|
|
cache
|
|
) {
|
|
for(var p in strings){
|
|
cache.push("'" + p + "':" + strings[p])
|
|
}
|
|
},
|
|
|
|
getPreloadL10nRootPath = function(
|
|
dest
|
|
){
|
|
var match = dest.match(/(.+)\/([^\/]+)$/);
|
|
return match[1] + "/nls/" + match[2];
|
|
},
|
|
|
|
flattenRootBundle = function(resource){
|
|
if(resource.flattenedBundles){
|
|
return;
|
|
}
|
|
resource.flattenedBundles = {};
|
|
var scheduledToFlatten = {};
|
|
bc.localeList.forEach(function(locale){
|
|
scheduledToFlatten[locale] = 1;
|
|
});
|
|
bc.localeList.forEach(function(locale){
|
|
var accumulator = lang.mixin({}, resource.bundleValue.root);
|
|
if(!bc.localeList.discreteLocales[locale]){
|
|
console.log(resource.mid, locale);
|
|
}else{
|
|
bc.localeList.discreteLocales[locale].forEach(function(discreteLocale){
|
|
var localizedBundle = resource.localizedSet[discreteLocale];
|
|
if(localizedBundle && localizedBundle.bundleValue){
|
|
lang.mixin(accumulator, localizedBundle.bundleValue);
|
|
}
|
|
});
|
|
|
|
var set = {};
|
|
if(locale === "ROOT"){
|
|
for(var p in resource.localizedSet) set[p] = 1;
|
|
}else{
|
|
for(var p in resource.localizedSet) if(!scheduledToFlatten[p] && p.indexOf(locale) == 0 && p.length > locale.length){
|
|
// p is a more-specific version of locale and it is not scheduled to be flattened itself
|
|
set[p] = 1;
|
|
}
|
|
}
|
|
accumulator._localized = set;
|
|
}
|
|
resource.flattenedBundles[locale] = accumulator;
|
|
}
|
|
)
|
|
;
|
|
},
|
|
|
|
getFlattenedBundles = function(
|
|
resource,
|
|
rootBundles
|
|
){
|
|
rootBundles.forEach(flattenRootBundle);
|
|
|
|
var newline = bc.newline,
|
|
rootPath = getPreloadL10nRootPath(resource.dest.match(/(.+)(\.js)$/)[1]),
|
|
mid, cache;
|
|
bc.localeList.forEach(function(locale){
|
|
cache = [];
|
|
rootBundles.forEach(function(rootResource){
|
|
cache.push("'" + rootResource.prefix + rootResource.bundle + "':" + json.stringify(rootResource.flattenedBundles[locale]) + newline);
|
|
});
|
|
mid = getPreloadL10nRootPath(resource.mid) + "_" + locale;
|
|
var flattenedResource = {
|
|
src:"*synthetic*",
|
|
dest:rootPath + "_" + locale + ".js",
|
|
pid:resource.pid,
|
|
mid:mid,
|
|
pack:resource.pack,
|
|
deps:[],
|
|
tag:{flattenedNlsBundle:1},
|
|
encoding:'utf8',
|
|
text:"define(" + (bc.insertAbsMids ? "'" + mid + "',{" : "{") + newline + cache.join("," + newline) + "});",
|
|
getText:function(){ return this.text; }
|
|
};
|
|
if(bc.insertAbsMids){
|
|
flattenedResource.tag.hasAbsMid = 1;
|
|
}
|
|
bc.start(flattenedResource);
|
|
});
|
|
},
|
|
|
|
getLayerText = function(
|
|
resource,
|
|
resourceText // ===undefined (normal case) => AMD define() the resource at the end of the layer
|
|
// ===false => put the resource in the cache
|
|
// otherwise write the value of resourceText at the end of the layer (so far only used in the writeDojo transform)
|
|
){
|
|
var newline = bc.newline,
|
|
rootBundles = [],
|
|
strings = {},
|
|
cache = [],
|
|
layer = resource.layer,
|
|
moduleSet = computeLayerContents(resource, layer.include, layer.exclude),
|
|
includeLocales = "includeLocales" in layer ? layer.includeLocales : bc.includeLocales;
|
|
for(var p in moduleSet){
|
|
// always put modules!=resource in the cache; put resource in the cache if it's a boot layer and an explicit resourceText wasn't given
|
|
if(p!=resource.mid || resourceText===false){
|
|
var module = moduleSet[p];
|
|
if(module.localizedSet && bc.localeList){
|
|
// this is a root NLS bundle and the profile is building flattened layer bundles;
|
|
// therefore, add this bundle to the set to be flattened, but don't write the root bundle
|
|
// to the cache since the loader will explicitly load the flattened bundle
|
|
rootBundles.push(module);
|
|
if(includeLocales){
|
|
// include the ROOT always
|
|
cache.push("'" + p + "':function(){" + newline + module.getText() + newline + "}");
|
|
// now include each locale in the layer
|
|
includeLocales.forEach(function(locale){
|
|
var parts = locale.split("-");
|
|
for(var i = parts.length; i > 0; i--){
|
|
var localizedSet = module.localizedSet[parts.slice(0, i).join("-")];
|
|
// see if the localized set is there
|
|
if(localizedSet){
|
|
// put the bundle in the cache
|
|
cache.push("'" + localizedSet.mid + "':function(){" + newline + localizedSet.getText() + newline + "}");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}else if(module.internStrings){
|
|
pushString(strings, module.internStrings());
|
|
}else if(module.getText){
|
|
cache.push("'" + p + "':function(){" + newline + module.getText() + newline + "}");
|
|
}else{
|
|
bc.log("amdMissingLayerModuleText", ["module", module.mid, "layer", resource.mid]);
|
|
}
|
|
}
|
|
}
|
|
appendStringsToCache(strings, cache);
|
|
|
|
// compute the flattened layer bundles (if any)
|
|
if(rootBundles.length){
|
|
getFlattenedBundles(resource, rootBundles);
|
|
// push an *now into the cache that causes the flattened layer bundles to be loaded immediately
|
|
cache.push("'*now':function(r){r(['dojo/i18n!*preload*" + getPreloadL10nRootPath(resource.mid) + "*" +
|
|
json.stringify(bc.localeList.filter(function(locale){
|
|
return !includeLocales || (includeLocales.indexOf(locale) == -1 && locale != "ROOT");
|
|
})) + "']);}" + newline);
|
|
}
|
|
|
|
// construct the cache text
|
|
if(cache.length && resource.layer.noref){
|
|
cache.push("'*noref':1");
|
|
}
|
|
return (cache.length ? "require({cache:{" + newline + cache.join("," + newline) + "}});" + newline : "") +
|
|
(resourceText===undefined ? insertAbsMid(resource.getText(), resource) : (resourceText==false ? "" : resourceText)) +
|
|
(resource.layer.postscript ? resource.layer.postscript : "");
|
|
},
|
|
|
|
getStrings = function(
|
|
resource
|
|
){
|
|
var strings = {},
|
|
cache = [],
|
|
newline = bc.newline;
|
|
resource.deps && resource.deps.forEach(function(dep){
|
|
if(dep.internStrings){
|
|
pushString(strings, dep.internStrings());
|
|
}
|
|
});
|
|
appendStringsToCache(strings, cache);
|
|
return cache.length ? "require({cache:{" + newline + cache.join("," + newline) + "}});" + newline : "";
|
|
},
|
|
|
|
getDestFilename = function(
|
|
resource
|
|
){
|
|
if((resource.layer && bc.layerOptimize) || (!resource.layer && bc.optimize)){
|
|
return resource.dest + ".uncompressed.js";
|
|
}
|
|
return resource.dest;
|
|
},
|
|
|
|
processNlsBundle = function(
|
|
resource
|
|
){
|
|
var newline = bc.newline, text, p;
|
|
|
|
if(resource.localizedSet && resource.bundleValue){
|
|
// do a check that all localizations mentioned in the root actually exist
|
|
var missing = [];
|
|
for(p in resource.bundleValue){
|
|
if(p!="root" && resource.bundleValue[p] && !resource.localizedSet[p]){
|
|
missing.push("'" + p + "'");
|
|
}
|
|
}
|
|
if(missing.length){
|
|
missing.sort();
|
|
bc.log("missingL10n", "Root: " + resource.mid + "; missing bundles: " + missing.join(",") + ".");
|
|
}
|
|
}
|
|
if(resource.bundleType=="legacy"){
|
|
if(resource.bundleValue){
|
|
if(resource.localizedSet){
|
|
// this is the root bundle; augment with all available localizations
|
|
for(p in resource.localizedSet){
|
|
resource.bundleValue[p] = 1;
|
|
}
|
|
}
|
|
text = json.stringify(resource.bundleValue);
|
|
}else{
|
|
text = "// ERROR: builder was unable to evaluate source bundle; therefore, this empty conversion was written" + newline + "{}";
|
|
}
|
|
return "define(" + (bc.insertAbsMids ? "'" + resource.mid + "'," : "") + newline + text + newline + ");";
|
|
}else{
|
|
return insertAbsMid(resource.getText(), resource);
|
|
}
|
|
},
|
|
|
|
write = function(
|
|
resource,
|
|
callback
|
|
){
|
|
if(resource.layer && (resource.layer.boot || resource.layer.discard)){
|
|
// resource.layer.boot layers are written by the writeDojo transform
|
|
return 0;
|
|
}
|
|
|
|
var copyright;
|
|
if(resource.pack){
|
|
copyright = resource.pack.copyrightNonlayers && (resource.pack.copyright || bc.copyright);
|
|
}else{
|
|
copyright = bc.copyrightNonlayers && bc.copyright;
|
|
}
|
|
if(!copyright){
|
|
copyright = "";
|
|
}
|
|
|
|
var text;
|
|
if(resource.tag.nls){
|
|
text = processNlsBundle(resource);
|
|
}else if(resource.layer){
|
|
// don't insertAbsMid or internStrings since that's done in getLayerText
|
|
text= getLayerText(resource);
|
|
if(resource.layer.compat=="1.6"){
|
|
text += "require(" + json.stringify(resource.layer.include) + ");" + bc.newline;
|
|
}
|
|
copyright = resource.layer.copyright || "";
|
|
}else{
|
|
text = insertAbsMid(resource.getText(), resource);
|
|
if(bc.internStrings){
|
|
text = getStrings(resource) + text;
|
|
}
|
|
}
|
|
|
|
// remember the uncompressed text for the optimizer
|
|
resource.uncompressedText = text;
|
|
|
|
var destFilename = getDestFilename(resource);
|
|
fileUtils.ensureDirectoryByFilename(destFilename);
|
|
fs.writeFile(destFilename, bc.newlineFilter(text, resource, "writeAmd"), resource.encoding, function(err){
|
|
callback(resource, err);
|
|
});
|
|
return callback;
|
|
};
|
|
|
|
write.getLayerText = getLayerText;
|
|
write.getDestFilename = getDestFilename;
|
|
write.computeLayerContents = computeLayerContents;
|
|
|
|
return write;
|
|
});
|
|
|