197 lines
7.1 KiB
JavaScript
197 lines
7.1 KiB
JavaScript
define([
|
|
"../buildControl",
|
|
"../fileUtils",
|
|
"dojo/_base/lang"
|
|
], function(bc, fileUtils, lang) {
|
|
// note: this transform originated from the v1.6 build system. In 1.8 it has been enhanced to handle
|
|
// relative paths for imports when the src and destination tree structures are not predictable.
|
|
//
|
|
// The cssImportIgnore logic remains although it is of questionable value because it depends on naming a file
|
|
// exactly as it is named in an import directive. Instead, the tag "importIgnore" can be used to prevent expanding
|
|
// a CSS file and the tag "noOptimize" can be used to prevent optimizing a CSS file. The tag system gives a
|
|
// much more general method to handle this problem. Of course cssImportIgnore can be disabled by simply setting
|
|
// it to falsy.
|
|
|
|
var cssImportRegExp = /\@import\s+(url\()?\s*([^);]+)\s*(\))?([\w, ]*)(;)?/g,
|
|
cssUrlRegExp = /url\(\s*([^\)]+)\s*\)?/g,
|
|
|
|
checkSlashes = function(name){
|
|
return name.replace(/\\/g, "/");
|
|
},
|
|
|
|
cssImportIgnore = (bc.cssImportIgnore ?
|
|
// trim any spaces off of all items; add a trailing comma for fast lookups
|
|
bc.cssImportIgnore.split(",").map(function(item){return lang.trim(item);}).join(",") + "," :
|
|
""),
|
|
|
|
cleanCssUrlQuotes = function(url){
|
|
// If an URL from a CSS url value contains start/end quotes, remove them.
|
|
// This is not done in the regexp, since the CSS spec allows for ' and " in the URL
|
|
// which makes the regex overly complicated if they are backslash escaped.
|
|
|
|
url = lang.trim(url);
|
|
if(url.charAt(0) == "'" || url.charAt(0) == '"'){
|
|
url = url.substring(1, url.length - 1);
|
|
}
|
|
return url;
|
|
},
|
|
|
|
removeComments = function(text, filename){
|
|
var originalText = text,
|
|
startIndex = -1,
|
|
endIndex;
|
|
while((startIndex = text.indexOf("/*")) != -1){
|
|
endIndex = text.indexOf("*/", startIndex + 2);
|
|
if(endIndex == -1){
|
|
bc.log("cssOptimizeImproperComment", ["CSS file", filename]);
|
|
return originalText;
|
|
}
|
|
text = text.substring(0, startIndex) + text.substring(endIndex + 2, text.length);
|
|
}
|
|
return text;
|
|
},
|
|
|
|
isRelative = function(url){
|
|
var colonIndex = url.indexOf(":");
|
|
return url.charAt(0) != "/" && (colonIndex == -1 || colonIndex > url.indexOf("/"));
|
|
},
|
|
|
|
getDestRelativeFilename = function(dest, relativeResource){
|
|
var referenceSegments = dest.split("/"),
|
|
relativeSegments = relativeResource.dest.split("/");
|
|
referenceSegments.pop();
|
|
while(referenceSegments.length && relativeSegments.length && referenceSegments[0]==relativeSegments[0]){
|
|
referenceSegments.shift();
|
|
relativeSegments.shift();
|
|
}
|
|
for(var i = 0; i<referenceSegments.length; i++){
|
|
relativeSegments.unshift("..");
|
|
}
|
|
return relativeSegments.join("/");
|
|
},
|
|
|
|
getDestRelativeUrlFromSrcUrl = function(dest, fullSrcFilename, msgInfo){
|
|
var relativeResource = bc.resources[fullSrcFilename];
|
|
if(relativeResource){
|
|
return getDestRelativeFilename(dest, relativeResource);
|
|
}else{
|
|
bc.log("cssOptimizeUnableToResolveURL", msgInfo);
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
flattenCss = function(resource){
|
|
if(resource.optimizedText){
|
|
return;
|
|
}
|
|
var dest = resource.dest,
|
|
src = resource.src,
|
|
srcReferencePath = fileUtils.getFilepath(checkSlashes(src)),
|
|
text = resource.text,
|
|
imports = [];
|
|
text = removeComments(text, src);
|
|
text = text.replace(cssImportRegExp, function(fullMatch, urlStart, importUrl, urlEnd, mediaTypes){
|
|
importUrl = checkSlashes(cleanCssUrlQuotes(importUrl));
|
|
var fixedRelativeUrl,
|
|
fullSrcFilename = (isRelative(importUrl) ? fileUtils.compactPath(fileUtils.catPath(srcReferencePath, importUrl)) : importUrl),
|
|
importResource = bc.resources[fullSrcFilename],
|
|
ignore = false;
|
|
|
|
// don't expand if explicitly instructed not to by build control
|
|
if(
|
|
(cssImportIgnore && cssImportIgnore.indexOf(importUrl + ",") != -1) || // the v1.6- technique
|
|
(importResource && importResource.tag.importIgnore) // the v1.8+ technique
|
|
){
|
|
ignore = true;
|
|
bc.log("cssOptimizeIgnored", ["CSS file", src, "import directive", fullMatch]);
|
|
}
|
|
|
|
// don't expand if multiple media types given
|
|
if(mediaTypes && lang.trim(mediaTypes)!="all"){
|
|
ignore = true;
|
|
bc.log("cssOptimizeIgnoredMultiMediaTypes", ["CSS file", src, "import directive", fullMatch]);
|
|
}
|
|
|
|
// even if not expanding, make sure relative urls are adjusted correctly
|
|
if(ignore){
|
|
if(isRelative(importUrl) && (fixedRelativeUrl = getDestRelativeUrlFromSrcUrl(dest, fullSrcFilename, ["CSS file", src, "import directive", fullMatch]))){
|
|
return '@import url("' + fixedRelativeUrl + '")' + (mediaTypes || "") + ";";
|
|
}else{
|
|
// right or wrong, this is our only choice at this point
|
|
return fullMatch;
|
|
}
|
|
}
|
|
|
|
// at this point, we want to expand the import into the calling resource
|
|
|
|
if(!importResource){
|
|
bc.log("cssOptimizeIgnoredNoResource", ["CSS file", src, "import directive", fullMatch]);
|
|
return fullMatch;
|
|
}
|
|
|
|
flattenCss(importResource);
|
|
var importContents = importResource.optimizedText;
|
|
|
|
// since importContents is flattened, all relative urls are computed with respect to the destination location of importResource
|
|
// these urls need to be adjusted with respect to resource
|
|
var importResourceDestPath = fileUtils.getFilepath(importResource.dest);
|
|
return importContents.replace(cssUrlRegExp, function(fullMatch, urlMatch){
|
|
var fixedUrl = checkSlashes(cleanCssUrlQuotes(urlMatch)),
|
|
queryString = "",
|
|
queryStart = fixedUrl.search(/[?#]/);
|
|
if(queryStart > 0){
|
|
queryString = fixedUrl.slice(queryStart);
|
|
fixedUrl = fixedUrl.slice(0, queryStart);
|
|
}
|
|
|
|
if(isRelative(fixedUrl)){
|
|
var fullDestFilename = fileUtils.compactPath(fileUtils.catPath(importResourceDestPath, fixedUrl)),
|
|
relativeResource = bc.resourcesByDest[fullDestFilename];
|
|
if(!relativeResource){
|
|
bc.log("cssOptimizeUnableToResolveURL", ["CSS file", src, "import", importResource.src, "relative URL", fullMatch]);
|
|
}else{
|
|
return 'url("' + getDestRelativeFilename(resource.dest, relativeResource) + queryString + '")';
|
|
}
|
|
}
|
|
// right or wrong, this is our only choice at this point
|
|
return fullMatch;
|
|
});
|
|
});
|
|
|
|
//Hoist imports to maintain valid CSS
|
|
text = text.replace(cssImportRegExp, function(fullMatch){
|
|
imports.push(fullMatch);
|
|
return "";
|
|
});
|
|
|
|
if(imports.length) {
|
|
text = imports.join("\n") + "\n" + text;
|
|
}
|
|
|
|
if(/keepLines/i.test(bc.cssOptimize)){
|
|
// remove multiple empty lines.
|
|
text = text.replace(/(\r\n)+/g, "\r\n").replace(/\n+/g, "\n");
|
|
}else{
|
|
// remove newlines and extra spaces
|
|
text = text.replace(/[\r\n]/g, "").replace(/\s+/g, " ").replace(/\{\s/g, "{").replace(/\s\}/g, "}");
|
|
}
|
|
|
|
resource.optimizedText = text;
|
|
if(!resource.tag.noOptimize){
|
|
resource.rawText = resource.text;
|
|
resource.text = text;
|
|
}
|
|
};
|
|
|
|
return function(resource, callback) {
|
|
try{
|
|
if(bc.cssOptimize && !resource.tag.noOptimize){
|
|
flattenCss(resource);
|
|
bc.log("cssOptimize", ["file", resource.src]);
|
|
}
|
|
}catch(e){
|
|
bc.log("cssOptimizeFailed", ["file", resource.src, "error", e]);
|
|
}
|
|
};
|
|
});
|