UNPKG

dojo-util

Version:

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

828 lines (692 loc) 24.6 kB
checkstyleUtil = { errors: [], commentNames: ["summary", "description", "example", "tags", "this"] }; checkstyleUtil.applyRules = function(fileName, contents){ // Do not process JSON files if(contents.charAt(0) == "{"){ return; } // Mark all the characters that are in comments. var comments = checkstyleUtil.getComments(contents); // Apply all the rules to the file for(var ruleName in checkstyleUtil.rules){ checkstyleUtil.rules[ruleName](fileName, contents, comments); } }; // Calculate the characters in a file that are in comment fields // These will be ignored by the checkstyle rules. checkstyleUtil.getComments = function(contents){ var comments = []; var i; // Initialize the array to false values. for(i = 0; i < contents.length; i++){ comments[i] = 0; } var sep = "\n"; function markRange(start, stop){ for(var i = start; i < stop; i++){ comments[i] = 1; } } function markRegexs() { var idx = contents.indexOf("/g"); var i; while(idx > -1) { if(!comments[idx] && contents.charAt(idx - 1) != "*"){ // Look back until either a forward slash // or a new line is found var prevChar = contents.charAt(idx - 1); i = idx; while(prevChar != "\n" && prevChar != "/" && i > 0){ prevChar = contents.charAt(--i); } if(prevChar == "/" && i < idx - 1){ markRange(i, idx); } } idx = contents.indexOf("/g", idx + 2) } // Now mark all .match and .replace function calls // They generally contain regular expressions, and are just too bloody difficult. var fnNames = ["match", "replace"]; var name; for (i = 0; i < fnNames.length; i++){ name = fnNames[i]; idx = contents.indexOf(name + "("); while(idx > -1){ // Find the end parenthesis if(comments[idx]){ idx = contents.indexOf(name + "(", idx + name.length); } else { var fnEnd = contents.indexOf(")", idx); markRange(idx, fnEnd + 1); } } } // Now look for all the lines that declare a regex variable, e.g. // var begRegExp = /^,|^NOT |^AND |^OR |^\(|^\)|^!|^&&|^\|\|/i; idx = contents.indexOf(" = /"); while(idx > -1){ if(!comments[idx] && contents.charAt(idx + 4) != "*"){ var eol = contents.indexOf("\n", idx + 1); markRange(idx + 3, Math.max(eol, idx + 4)); } idx = contents.indexOf(" = /", idx + 3); } } markRegexs(); var marker = null; var ch; var DOUBLE_QUOTE = 1; var SINGLE_QUOTE = 2; var LINE_COMMENT = 3; var MULTI_COMMENT = 4; var UNMARK = 5; var pos; for (i = 0; i < contents.length; i++) { var skip = false; if(comments[i]){ continue; } ch = contents[i]; switch(ch){ case "\"": if(marker == DOUBLE_QUOTE) { marker = UNMARK; } else if (marker == null) { marker = DOUBLE_QUOTE; pos = i; } break; case "'": if(marker == SINGLE_QUOTE) { marker = UNMARK; } else if (marker == null) { marker = SINGLE_QUOTE; pos = i; } break; case "/": if(marker == null){ if(contents[i + 1] == "/"){ marker = LINE_COMMENT; pos = i; skip = true; } else if(contents[i + 1] == "*"){ marker = MULTI_COMMENT; pos = i; skip = true; } } break; case "*": if (marker == MULTI_COMMENT){ if(contents[i + 1] == "/"){ marker = UNMARK; skip = true; } } break; case "\n": if(marker == LINE_COMMENT){ marker = UNMARK; } break; } if (marker != null) { comments[i] = 1; } if (marker == UNMARK){ marker = null; } if (skip) { i++; comments[i] = 1; } } return comments; } // Calculate the line number of the character at index 'pos' checkstyleUtil.getLineNumber = function(contents, pos){ var counter = 0; var sep = "\n"; for(var i = pos; i > -1; i--){ if(contents.charAt(i) == "\n"){ counter ++; } } return counter + 1; }; // Store the information for a single error. checkstyleUtil.addError = function(msg, fileName, contents, pos){ while(fileName.indexOf("../") == 0){ fileName = fileName.substring(3); } checkstyleUtil.errors.push({ file: fileName, line: checkstyleUtil.getLineNumber(contents, pos), message: msg }); }; // Find the next character in 'contents' after the index 'start' // Spaces and tabs are ignored. checkstyleUtil.getNextChar = function(contents, start, comments, ignoreNewLine){ for(var i = start; i < contents.length; i++){ if(comments && comments[i]){ continue; } if(contents.charAt(i) != " " && contents.charAt(i) != "\t" && (!ignoreNewLine || contents.charCodeAt(i) != 13)){ return { value: contents[i], pos: i }; } } return null; }; // Find the next occurrence of the character in the // 'contents' array after the index 'start' checkstyleUtil.findNextCharPos = function(contents, start, character){ for(var i = start; i < contents.length; i++){ if(contents.charAt(i) == character){ return i; } } return -1; }; // Creates a simple function that searches for the token, and // adds an error if it is found checkstyleUtil.createSimpleSearch = function(token, message){ return function(fileName, contents, comments){ var idx = contents.indexOf(token); while(idx > -1){ if(!comments[idx]){ checkstyleUtil.addError(message, fileName, contents, idx); } idx = contents.indexOf(token, idx + 1); } }; }; // Creates a function that fails a test if the given token // does not have a space to the left and right. checkstyleUtil.createSpaceWrappedSearch = function(token, message){ return function(fileName, contents, comments){ var idx = contents.indexOf(token); var before, after; var tokenLength = token.length; while(idx > -1){ before = contents.charAt(idx - 1); after = contents.charAt(idx + tokenLength); if(!comments[idx] && ((before != " " && before != "\t" && (token != "==" || before != "!") && (token != "=" || (before != "<" && before != ">" && before != "=" && before != "!" && before != "+" && before != "-" && before != "*" && before != "/" && before != "&" && before != "|" ))) || ( (after != " " && contents.charCodeAt(idx + tokenLength) != 13 && contents.charCodeAt(idx + tokenLength) != 10) && (token != "==" || after != "=") && (token != "!=" || after != "=") && (token != "<" || after != "=") && (token != ">" || after != "=") && (token != "=" || after != "=") && (token != "&" || after != "=") && (token != "|" || after != "=") && (token != "+" || after != "=") && (token != "-" || after != "=") && (token != "*" || after != "=") && (token != "/" || after != "=") ))){ checkstyleUtil.addError(message, fileName, contents, idx); } idx = contents.indexOf(token, idx + token.length); } }; }; checkstyleUtil.isEOL = function(contents, pos){ var c = contents.charCodeAt(pos); return c == 10 || c == 13 || contents.charAt(pos) == "\n"; }; // All the rules that will be applied to each file. checkstyleUtil.rules = { "elseFollowedBySpace": function(fileName, contents, comments){ var idx = contents.indexOf("else "); while(idx > -1){ if(!comments[idx] && contents.substring(idx + 5, idx + 7) != "if"){ checkstyleUtil.addError("\" else \" cannot be followed by a space", fileName, contents, idx); } idx = contents.indexOf("else {", idx + 1); } }, "trailingComma" : function(fileName, contents, comments){ var s = ","; var idx = contents.indexOf(s); var nextChar; while(idx > -1){ if(!comments[idx]){ nextChar = checkstyleUtil.getNextChar(contents, idx + 1, comments, true); if(nextChar && nextChar.value == "}"){ checkstyleUtil.addError("Trailing commas are not permitted", fileName, contents, idx); } } idx = contents.indexOf(s, idx + 1); } }, "switchCaseNewLine" : function(fileName, contents, comments){ var s = "\tcase "; var idx = contents.indexOf(s); var nextColonIdx; var eolIdx; while(idx > -1){ if(!comments[idx]){ eolIdx = contents.indexOf("\n", idx + 4); if(eolIdx > idx){ // Count backwards from the end of the line. // The first character, that is not a comment, // Should be a ':' for(var i = eolIdx; i > idx + 4; i--){ var c = contents.charAt(i); if(!comments[i] && c != ' ' && c != '\t' && c != ':' && !checkstyleUtil.isEOL(contents, i)){ checkstyleUtil.addError( "A CASE statement should be followed by a new line", fileName, contents, idx); break; } if(c == ':'){ break; } } } } idx = contents.indexOf(s, idx + 4); } }, "curlyBraceAtStartOfLine": function(fileName, contents, comments){ var idx = contents.indexOf("\n"); while(idx > -1){ var nextChar = checkstyleUtil.getNextChar(contents, idx + 1); if(nextChar && !comments[nextChar.pos] && nextChar.value == "{"){ // Go back three lines, and look for "dojo.declare". If it exists in the last three lines, // then it is ok to have { at the start of this line. var nlCount = 0; var i; for(i = idx - 1; i > -1 && nlCount < 3; i--){ if(contents[i] == "\n"){ nlCount++; } } var declarePos = contents.indexOf("dojo.declare", Math.max(0, i)); if(declarePos < 0 || declarePos > idx){ checkstyleUtil.addError("An opening curly brace should not be the first on a line", fileName, contents, idx); } } idx = contents.indexOf("\n", idx + 1); } }, "parenthesisSpaceCurlyBrace": checkstyleUtil.createSimpleSearch(") {", "A space is not permitted between a closing parenthesis and a curly brace"), "useTabs": function(fileName, contents, comments){ var idx = contents.indexOf(" "); while(idx > -1){ var nextChar = checkstyleUtil.getNextChar(contents, idx + 1); if(!comments[idx] && nextChar && nextChar.value.charCodeAt(0) != 13){ checkstyleUtil.addError("Tabs should be used instead of spaces", fileName, contents, idx); var nextLine = checkstyleUtil.findNextCharPos(contents, idx + 1, "\n"); if(nextLine < 0){ break; } idx = contents.indexOf(" ", nextLine + 1); } else{ idx = contents.indexOf(" ", idx + 2); } } }, "commentFormatting": function(fileName, contents, comments){ var commentNames = checkstyleUtil.commentNames; var invalidPrefixes = ["//", "//\t"]; var idx; for(var i = 0; i < commentNames.length; i++){ var comment = commentNames[i]; for(var j = 0; j < invalidPrefixes.length; j++){ idx = contents.indexOf(invalidPrefixes[j] + comment + ":"); // Make sure that there is a space before the comment. while(idx > -1){ checkstyleUtil.addError("Must be just a space in a comment before \"" + comment + "\"" , fileName, contents, idx); var nextLine = checkstyleUtil.findNextCharPos(contents, idx + 1, "\n"); if(nextLine < 0){ break; } idx = contents.indexOf(invalidPrefixes[j] + comment + ":", nextLine); } } idx = contents.indexOf(comment + ":"); // Make sure that the comment name is on a line by itself. The body of the comment // must be on the next line. while(idx > -1){ if(comments[idx]){ var search = idx + comment.length + 1; // Make sure that there is nothing after the comment name on the same line. while(!checkstyleUtil.isEOL(contents, search)){ if(contents[search] != " " && contents[search] != "\t"){ checkstyleUtil.addError("The comment \"" + comment + "\" must be followed by a new line" , fileName, contents, idx); break; } search++; } } idx = contents.indexOf(comment + ":", idx + comment.length + 2); } } }, "spacesAroundEquals": checkstyleUtil.createSpaceWrappedSearch("==", "The equals sign should be preceded and followed by a space"), "spacesAroundNotEquals": checkstyleUtil.createSpaceWrappedSearch("!=", "The != sign should be preceded and followed by a space"), "spacesAroundAssignment": checkstyleUtil.createSpaceWrappedSearch("=", "The = sign should be preceded and followed by a space"), "spacesAroundOr": checkstyleUtil.createSpaceWrappedSearch("||", "The || sign should be preceded and followed by a space"), "spacesAroundLessThan": checkstyleUtil.createSpaceWrappedSearch("<", "The < sign should be preceded and followed by a space"), "spacesAroundGreaterThan": checkstyleUtil.createSpaceWrappedSearch(">", "The > sign should be preceded and followed by a space"), "spacesAroundAnd": checkstyleUtil.createSpaceWrappedSearch("&&", "The && sign should be preceded and followed by a space") }; var noSpaceAfter = ["catch","do","finally","for","if","switch","try","while","with"]; // Add checks for all the elements that are not allowed to have a space after them. checkstyleUtil.createNoSpaceAfterFunction = function(name){ checkstyleUtil.rules["noSpaceAfter" + noSpaceAfter[i] + "1"] = checkstyleUtil.createSimpleSearch(" " + name +" ", "\" " + name + " \" cannot be followed by a space"); checkstyleUtil.rules["noSpaceAfter" + noSpaceAfter[i] + "2"] = checkstyleUtil.createSimpleSearch("\t" + name +" ", "\" " + name + " \" cannot be followed by a space"); } for(var i = 0; i < noSpaceAfter.length; i++){ checkstyleUtil.createNoSpaceAfterFunction(noSpaceAfter[i]); } checkstyleUtil.clear = function(){ checkstyleUtil.errors = []; } checkstyleUtil.serializeErrors = function(){ var buf = []; var errs = checkstyleUtil.errors; for(var i = 0; i < errs.length; i++){ buf.push(errs[i].file + ":" + errs[i].line + " - " + errs[i].message); } return buf.join("\n"); } checkstyleUtil.makeSimpleFixes = function(contents){ var comments = checkstyleUtil.getComments(contents); for(var i = 0; i < noSpaceAfter.length; i++){ contents = checkstyleUtil.fixSpaceAfter(contents, noSpaceAfter[i], comments); } /* contents = contents.split(" ").join("\t") .split(" ").join("\t") .split(") {").join("){") .split("\tif (").join("\tif(") .split("} else").join("}else") .split("}\telse").join("}else") .split("}else {").join("}else{") .split("\twhile (").join("\twhile(") .split("\tfor (").join("\tfor(") .split("\tswitch (").join("\tswitch("); */ contents = checkstyleUtil.replaceAllExceptComments(contents, "= ", "= ", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, " ", "\t", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, " ", "\t", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "\tif (", "\tif(", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "} else", "}else", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "}\telse", "}else", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "}else {", "}else{", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "\twhile (", "\twhile(", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "\tfor (", "\tfor(", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "\tswitch (", "\tswitch(", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, ") {", "){", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.replaceAllExceptComments(contents, "//summary:", "// summary:", {}); contents = checkstyleUtil.replaceAllExceptComments(contents, "//description:", "// description:", {}); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixTrailingWhitespace(contents); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "===", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "!==", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "<=", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "<", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, ">=", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, ">", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "!=", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "==", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "=", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "||", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixSpaceBeforeAndAfter(contents, "&&", comments); comments = checkstyleUtil.getComments(contents); contents = checkstyleUtil.fixCommentNames(contents); return contents; } checkstyleUtil.fixCommentNames = function(contents){ var commentNames = checkstyleUtil.commentNames; var i; for(i = 0; i < commentNames.length; i++){ contents = checkstyleUtil.replaceAllExceptComments(contents, "//\t" + commentNames[i] + ":", "// " + commentNames[i] + ":", {}); } for(i = 0; i < commentNames.length; i++){ var commentName = commentNames[i]; var searchToken = "// " + commentName + ":"; var idx = contents.indexOf(searchToken); while(idx > -1){ // If the comment name is not followed immediately by a new line, then insert a new line, // two forward slashes and two tabs. if(!checkstyleUtil.isEOL(contents, idx + commentName.length + 4)){ // Calculate how many tabs to put before the "//" var tabs = ""; var search = idx - 1; while(!checkstyleUtil.isEOL(contents, search)){ tabs += contents.charAt(search); search--; } var insertPos = idx + commentName.length + 4; if(contents.charAt(insertPos) == " " || contents.charAt(insertPos) == "\t"){ contents = checkstyleUtil.deleteChar(contents, insertPos); } contents = checkstyleUtil.insertChar(contents, "\n" + tabs + "//\t\t", idx + commentName.length + 4); } idx = contents.indexOf(searchToken, idx + commentName.length); } } return contents; } checkstyleUtil.replaceAllExceptComments = function(contents, old, newStr, comments){ var idx = contents.indexOf(old); var toRemove = []; while(idx > -1){ if(!comments[idx]){ toRemove.push(idx); } idx = contents.indexOf(old, idx + old.length); } // Process the string backwards so we don't have to recompute the comments each time. for(var i = toRemove.length - 1; i > -1; i--){ idx = toRemove[i]; if(!comments[idx]){ contents = contents.substring(0, idx) + newStr + contents.substring(idx + old.length, contents.length); } } return contents; } checkstyleUtil.insertChar = function(contents, ch, pos){ return contents.substring(0, pos) + ch + contents.substring(pos); } checkstyleUtil.deleteChar = function(contents, pos){ return contents.substring(0, pos) + contents.substring(pos + 1); } checkstyleUtil.fixTrailingWhitespace = function(contents) { var idx = contents.indexOf("\n"); // Find each new line character, then iterate backwards until a non-whitespace character is found // then remove the whitespace. while(idx > -1){ var search = idx - 1; while(search > -1 && (contents.charAt(search) == " " || contents.charAt(search) == "\t")){ search--; } if(search < idx -1){ contents = contents.substring(0, search + 1) + contents.substring(idx, contents.length); idx = contents.indexOf("\n", search + 2); }else{ idx = contents.indexOf("\n", idx + 1); } } return contents; } checkstyleUtil.fixSpaceAfter = function(contents, token, comments){ var idx = contents.indexOf(token + " "); while(idx > -1){ if(!comments[idx]){ contents = checkstyleUtil.deleteChar(contents, idx + token.length); } idx = contents.indexOf(token + " ", idx + token.length); } return contents; } checkstyleUtil.fixSpaceBeforeAndAfter = function(contents, token, comments){ var idx = contents.indexOf(token); var before, after; var len = token.length; while(idx > -1){ before = contents.charAt(idx - 1); after = contents.charAt(idx + len); if(!comments[idx]){ // Only insert a space before the token if: // - char before is not a space or a tab // - token is "==" and the char before is neither "!" or "=" if(before != " " && before != "\t" && (token != "==" || (before != "!" && before != "=")) && (token != "=" || (before != "<" && before != ">" && before != "=" && before != "!" && before != "+" && before != "-" && before != "*" && before != "/" && before != "&" && before != "|" )) ){ contents = checkstyleUtil.insertChar(contents, " ", idx); idx ++; } // Only insert a space after the token if: // - char after is not a space // - char after is not a new line // - char after is not "=" if((after != " " && contents.charCodeAt(idx + len) != 13 && contents.charCodeAt(idx + len) != 10) && (token != "==" || after != "=") && (token != "!=" || after != "=") && (token != "=" || after != "=") && (token != "<" || after != "=") && (token != ">" || after != "=") && (token != "&" || after != "=") && (token != "|" || after != "=") && (token != "+" || after != "=") && (token != "-" || after != "=") && (token != "*" || after != "=") && (token != "/" || after != "=") ){ contents = contents = checkstyleUtil.insertChar(contents, " ", idx + token.length); idx++; } } idx = contents.indexOf(token, idx + token.length); } return contents; } // Creates the data file suitable to be loaded into a dojo.data.ItemFileReadStore checkstyleUtil.generateReport = function(skipPrint){ var ids = 1; var json = ["{id:'" +(ids++) + "', file: 'All', isFolder:true}"]; // A map of folders that have already been found. var allFolders = {}; var messageIds = {}; var messageCounter = 1; var i, err; function getFolderName(fileName){ // Extract the folder name from a file name var idx = fileName.lastIndexOf("/"); return fileName.substring(0, idx); } // Add a folder to the list of folders. function pushFolder(folderName){ if(!allFolders[folderName]){ allFolders[folderName] = true; json.push("{id: '" +(ids++) + "', file: '" + folderName + "', folder: 1}"); } } for(i = 0; i < checkstyleUtil.errors.length; i++){ err = checkstyleUtil.errors[i]; var message = err.message; var messageId = messageIds[message]; if(!messageId){ messageId = "m" + messageCounter++; messageIds[message] = messageId; json.push("{id:'" + messageId + "',msg:'" + message + "'}"); } } pushFolder("All"); // Create the JSON records for each error. for(i = 0; i < checkstyleUtil.errors.length; i++){ err = checkstyleUtil.errors[i]; var folderName = getFolderName(err.file); pushFolder(folderName); json.push("{id:'" +(ids++) + "', file:'" + err.file + "',line:" + err.line + ",msg:{'_reference':'" + messageIds[err.message] + //"'},folder:'" + folderName + "'},folder: 0" + "}"); } // Add the date that the check was run to the store. json.push("{id:'" +(ids++) + "', date: " +(new Date()).getTime() + "}"); // Save the file. if(!skipPrint){ print("Found " + checkstyleUtil.errors.length + " checkstyle errors. " + "Open the file checkstyleReport.html to view the results."); } return "{ identifier: 'id', label:'file', items: [" + json.join(",\n") + "]}"; };