Path: blob/master/web-gui/buildyourownbotnet/assets/js/codemirror/test/lint/lint.js
1294 views
/*1Simple linter, based on the Acorn [1] parser module23All of the existing linters either cramp my style or have huge4dependencies (Closure). So here's a very simple, non-invasive one5that only spots67- missing semicolons and trailing commas8- variables or properties that are reserved words9- assigning to a variable you didn't declare10- access to non-whitelisted globals11(use a '// declare global: foo, bar' comment to declare extra12globals in a file)1314[1]: https://github.com/marijnh/acorn/15*/1617var topAllowedGlobals = Object.create(null);18("Error RegExp Number String Array Function Object Math Date undefined " +19"parseInt parseFloat Infinity NaN isNaN " +20"window document navigator prompt alert confirm console " +21"FileReader Worker postMessage importScripts " +22"setInterval clearInterval setTimeout clearTimeout " +23"CodeMirror test")24.split(" ").forEach(function(n) { topAllowedGlobals[n] = true; });2526var fs = require("fs"), acorn = require("./acorn.js"), walk = require("./walk.js");2728var scopePasser = walk.make({29ScopeBody: function(node, prev, c) { c(node, node.scope); }30});3132function checkFile(fileName) {33var file = fs.readFileSync(fileName, "utf8"), notAllowed;34if (notAllowed = file.match(/[\x00-\x08\x0b\x0c\x0e-\x19\uFEFF\t]|[ \t]\n/)) {35var msg;36if (notAllowed[0] == "\t") msg = "Found tab character";37else if (notAllowed[0].indexOf("\n") > -1) msg = "Trailing whitespace";38else msg = "Undesirable character " + notAllowed[0].charCodeAt(0);39var info = acorn.getLineInfo(file, notAllowed.index);40fail(msg + " at line " + info.line + ", column " + info.column, {source: fileName});41}4243var globalsSeen = Object.create(null);4445try {46var parsed = acorn.parse(file, {47locations: true,48ecmaVersion: 3,49strictSemicolons: true,50allowTrailingCommas: false,51forbidReserved: true,52sourceFile: fileName53});54} catch (e) {55fail(e.message, {source: fileName});56return;57}5859var scopes = [];6061walk.simple(parsed, {62ScopeBody: function(node, scope) {63node.scope = scope;64scopes.push(scope);65}66}, walk.scopeVisitor, {vars: Object.create(null)});6768var ignoredGlobals = Object.create(null);6970function inScope(name, scope) {71for (var cur = scope; cur; cur = cur.prev)72if (name in cur.vars) return true;73}74function checkLHS(node, scope) {75if (node.type == "Identifier" && !(node.name in ignoredGlobals) &&76!inScope(node.name, scope)) {77ignoredGlobals[node.name] = true;78fail("Assignment to global variable", node.loc);79}80}8182walk.simple(parsed, {83UpdateExpression: function(node, scope) {checkLHS(node.argument, scope);},84AssignmentExpression: function(node, scope) {checkLHS(node.left, scope);},85Identifier: function(node, scope) {86if (node.name == "arguments") return;87// Mark used identifiers88for (var cur = scope; cur; cur = cur.prev)89if (node.name in cur.vars) {90cur.vars[node.name].used = true;91return;92}93globalsSeen[node.name] = node.loc;94},95FunctionExpression: function(node) {96if (node.id) fail("Named function expression", node.loc);97}98}, scopePasser);99100if (!globalsSeen.exports) {101var allowedGlobals = Object.create(topAllowedGlobals), m;102if (m = file.match(/\/\/ declare global:\s+(.*)/))103m[1].split(/,\s*/g).forEach(function(n) { allowedGlobals[n] = true; });104for (var glob in globalsSeen)105if (!(glob in allowedGlobals))106fail("Access to global variable " + glob + ". Add a '// declare global: " + glob +107"' comment or add this variable in test/lint/lint.js.", globalsSeen[glob]);108}109110111for (var i = 0; i < scopes.length; ++i) {112var scope = scopes[i];113for (var name in scope.vars) {114var info = scope.vars[name];115if (!info.used && info.type != "catch clause" && info.type != "function name" && name.charAt(0) != "_")116fail("Unused " + info.type + " " + name, info.node.loc);117}118}119}120121var failed = false;122function fail(msg, pos) {123if (pos.start) msg += " (" + pos.start.line + ":" + pos.start.column + ")";124console.log(pos.source + ": " + msg);125failed = true;126}127128function checkDir(dir) {129fs.readdirSync(dir).forEach(function(file) {130var fname = dir + "/" + file;131if (/\.js$/.test(file)) checkFile(fname);132else if (file != "dep" && fs.lstatSync(fname).isDirectory()) checkDir(fname);133});134}135136exports.checkDir = checkDir;137exports.checkFile = checkFile;138exports.success = function() { return !failed; };139140141