UNPKG

dojo-util

Version:

Dojo utilities including build system for optimizing JavaScript application performance, and DOH testing tool

197 lines (173 loc) 7.25 kB
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]); } }; });