/** * Extracts values from <code>qx.Bootstrap.define().statics._checksMap</code>. * * @param {string} classCode - js code of Environment class. * @returns {Object} envKeys and envMethodNames */ function getFeatureTable(classCode) { function isFeatureMap (node) { return (node.type === 'Property' && node.key.type === 'Identifier' && node.key.name === '_checksMap' && node.value.type === 'ObjectExpression' ); } var featureMap = {}; // parse classCode var tree = esprima.parse(classCode); // search feature map var controller = new estraverse.Controller(); controller.traverse(tree, { enter : function (node, parent) { if (isFeatureMap(node)) { featureMap = node.value; } } }); return eval('(' + escodegen.generate(featureMap) + ')'); }
/** * extract values from qx.Bootstrap.define().statics._checksMap */ function getFeatureTable(classCode) { function is_feature_map (node) { return (node.type === "Property" && node.key.type === "Identifier" && node.key.name === "_checksMap" && node.value.type === "ObjectExpression" /* && node.parent && node.parent.type == "Property" && node.parent.key.type == "Identifier" && node.parent.key.name == "statics" */ ); } var feature_map = {}; // parse classCode var etree = esprima.parse(classCode); //parentAnnotator.annotate(etree); // search feature map var controller = new estraverse.Controller(); controller.traverse(etree, { enter : function (node, parent) { if (is_feature_map(node)) { feature_map = node.value; } } }); return eval('(' + escodegen.generate(feature_map) + ')'); }
/** * Finds variant nodes (<code>qx.core.Environment.(select(Async)?|get(Async)?|filter)</code> calls) * within an esprima AST. * * @param {Object} tree - esprima AST * @returns {Object[]} node - esprima node * @see {@link http://esprima.org/doc/#ast|esprima AST} */ function findVariantNodes(tree) { var result = []; var interestingEnvMethods = { 'select' : true, 'selectAsync' : true, 'get' : true, 'getAsync' : true, 'filter' : true }; // walk tree var controller = new estraverse.Controller(); controller.traverse(tree, { enter : function (node, parent) { // pick calls to qx.core.Environment.get|select|filter if (node.type === 'CallExpression' && util.get(node, 'callee.object.object.object.name') === 'qx' && util.get(node, 'callee.object.object.property.name') === 'core' && util.get(node, 'callee.object.property.name') === 'Environment' && util.get(node, 'callee.property.name') in interestingEnvMethods) { result.push(node); } } }); return result; }
/** * Extracts env keys (first argument) from * <code>qx.core.Environment.add('engine.name', function(){ ... })</code> calls and * stores them with their providing class (e.g. {'engine.name': 'qx.bom.client.Engine', 'engine.version': ...}). * * @param {Object} classCodeMap - js code of environment key provider classes e.g. {'qx.bom.client.Engine': 'long js code string'} * @returns {Object} envKeys and envProviderClasses. */ function getFeatureTable(classCodeMap) { function isQxCoreEnvAddCall (node) { return (node.type === 'CallExpression' && util.get(node, 'callee.object.object.object.name') === 'qx' && util.get(node, 'callee.object.object.property.name') === 'core' && util.get(node, 'callee.object.property.name') === 'Environment' && util.get(node, 'callee.property.name') === 'add'); } var featureMap = {}; for (var className in classCodeMap) { var tree = esprima.parse(classCodeMap[className]); var controller = new estraverse.Controller(); controller.traverse(tree, { enter : function (node, parent) { if (isQxCoreEnvAddCall(node)) { featureMap[getKeyFromEnvCall(node)] = className; } } }); } return featureMap; }
/** * Finds variant nodes (<code>qx.core.Environment.(select(Async)?|get(Async)?|filter)</code> calls) * within an esprima AST. * * @param {Object} tree - esprima AST * @returns {Object[]} node - esprima node * @see {@link http://esprima.org/doc/#ast|esprima AST} */ function findVariantNodes(tree) { var result = []; var controller = new estraverse.Controller(); controller.traverse(tree, { enter : function (node, parent) { if (isQxCoreEnvironmentCall(node)) { result.push(node); } } }); return result; }
enter: function (node) { var type = node.type, context, retVal = null; switch (type) { case Syntax.FunctionDeclaration: var parentContext = contextStack[contextStack.length - 1]; parentContext.declareVariable(node.id.name, false); context = new Scope(node, parentContext, {name: node.id.name }); contextStack.push(context); if (context.str() == functionName) { if (environmentObjectPosition != -1 && node.params.length > environmentObjectPosition) { activeParam = node.params[environmentObjectPosition].name; } } else { controller.skip(); } break; case Syntax.CallExpression: var pos = node.arguments.reduce(function (prev, curr, index) { if (curr.name && curr.name == activeParam) return index; return prev; }, -1); context = contextStack[contextStack.length - 1]; var id = context.getVariableIdentifier(node.callee.name); if (id && !analyzedCalls[id]) { analyzedCalls[id] = true; merge(result, findParametersInFunction(id, program, pos, analyzedCalls)); } break; default: } },
function findVariantNodes(etree) { var result = []; // walk etree var controller = new estraverse.Controller(); controller.traverse(etree, { enter : function (node, parent) { // pick calls to qx.core.Environment.get|select|filter if (node.type === 'CallExpression' && get(node, "callee.object.object.object.name") === 'qx' && get(node, "callee.object.object.property.name") === 'core' && get(node, "callee.object.property.name") === 'Environment' && get(node, "callee.property.name") in InterestingEnvMethods) { result.push(node); } } }); return result; }
/** * Replaces select env calls with the computed value. * * @param {Object} tree - esprima AST * @param {Object} envMap - environment settings * @returns {Object} resultTree - manipulated (desctructive) esprima AST * @see {@link http://esprima.org/doc/#ast|esprima AST} */ function replaceEnvCallSelect(tree, envMap) { var controller = new estraverse.Controller(); var resultTree = controller.replace(tree, { enter : function (node, parent) { if (isQxCoreEnvironmentCall(node, ["select"])) { var envKey = getKeyFromEnvCall(node); try { return getEnvSelectValueByKey(node, envMap[envKey]); } catch (error) { // intentionally empty // => no return means no replacement // possible reasons: // * envMap has no envKey because the envKey is runtime based (os.version) // and cannot be configured upfront. } } } }); return resultTree; }
/** * Replaces get env calls with the computed value. * * @param {Object} tree - esprima AST * @param {Object} envMap - environment settings * @returns {Object} resultTree - manipulated (desctructive) esprima AST * @see {@link http://esprima.org/doc/#ast|esprima AST} */ function replaceEnvCallGet(tree, envMap) { var controller = new estraverse.Controller(); var resultTree = controller.replace(tree, { enter : function (node, parent) { if (isQxCoreEnvironmentCall(node, ["get"])) { var envKey = getKeyFromEnvCall(node); if (envMap && envKey in envMap) { return { "type": "Literal", "value": envMap[envKey], "raw": ((typeof(envMap[envKey]) === "string") ? "\""+envMap[envKey]+"\"" : envMap[envKey].toString()) }; } } } }); return resultTree; }
/** * Extracts values from <code>qx.Bootstrap.define().statics._defaults</code>. * * @param {string} classCode - js code of Environment class. * @returns {Object} envKeys and envValues. */ function extractEnvDefaults(classCode) { function isDefaultsMap (node) { return (node.type === 'Property' && node.key.type === 'Identifier' && node.key.name === '_defaults' && node.value.type === 'ObjectExpression' ); } var defaultsMap = {}; var tree = esprima.parse(classCode); var controller = new estraverse.Controller(); controller.traverse(tree, { enter : function (node, parent) { if (isDefaultsMap(node)) { defaultsMap = node.value; } } }); return eval('(' + escodegen.generate(defaultsMap) + ')'); }
var findParametersInFunction = function (functionName, program, environmentObjectPosition, analyzedCalls) { var context = new Scope(program, null, {name: "global"}); var contextStack = [context]; var result = { shaderParameters: [], systemParameters: [] }; analyzedCalls = analyzedCalls || {}; // console.log("Looking for: ", functionName, environmentObjectPosition); var activeParam = null; var controller = new walk.Controller(); controller.traverse(program, { enter: function (node) { var type = node.type, context, retVal = null; switch (type) { case Syntax.FunctionDeclaration: var parentContext = contextStack[contextStack.length - 1]; parentContext.declareVariable(node.id.name, false); context = new Scope(node, parentContext, {name: node.id.name }); contextStack.push(context); if (context.str() == functionName) { if (environmentObjectPosition != -1 && node.params.length > environmentObjectPosition) { activeParam = node.params[environmentObjectPosition].name; } } else { controller.skip(); } break; case Syntax.CallExpression: var pos = node.arguments.reduce(function (prev, curr, index) { if (curr.name && curr.name == activeParam) return index; return prev; }, -1); context = contextStack[contextStack.length - 1]; var id = context.getVariableIdentifier(node.callee.name); if (id && !analyzedCalls[id]) { analyzedCalls[id] = true; merge(result, findParametersInFunction(id, program, pos, analyzedCalls)); } break; default: } }, leave: function (node) { var type = node.type; switch (type) { case Syntax.FunctionDeclaration: contextStack.pop(); activeParam = null; break; case Syntax.MemberExpression: var parameterName = node.property.name; // In a specific parameter of the current method if (activeParam && node.object.name == activeParam) { addSystemParameter(parameterName, result.shaderParameters); } // In 'this' is a system parameter else if (node.object.type == Syntax.ThisExpression) { addSystemParameter(parameterName, result.systemParameters, derivedSystemParameters); } // In global variable '_env' else if (node.object.name == "_env") { addSystemParameter(parameterName, result.shaderParameters); } break; } } }); return result; };
api.verify = function(text, config, filename, saveState) { var ast; // set the current parsed filename currentFilename = filename; if (!saveState) { this.reset(); } ast = parse(text.replace(/^(#![^\r\n]+[\r\n]+)/, "//$1")); //if Esprima failed to parse the file, there's no sense in setting up rules if (ast) { // process initial config to make it safe to extend config = prepareConfig(config); // parse global comments and modify config config = modifyConfigFromComments(ast, config); // enable appropriate rules Object.keys(config.rules).filter(function(key) { if (typeof config.rules[key] === "number") { return config.rules[key] > 0; } else if (Array.isArray(config.rules[key])) { // Here the rule looks like [1, ...] - the first value is the key we want return config.rules[key][0] > 0; } else { return false; } }).forEach(function(key) { var ruleCreator = rules.get(key), options = [], rule; if (Array.isArray(config.rules[key])) { // The additional config data is after the bool value options = config.rules[key].slice(1); } if (ruleCreator) { try { rule = ruleCreator(new RuleContext(key, api, options)); // add all the node types as listeners Object.keys(rule).forEach(function(nodeType) { api.on(nodeType, rule[nodeType]); }); } catch(ex) { ex.message = "Error while loading rule '" + key + "': " + ex.message; throw ex; } } else { throw new Error("Definition for rule '" + key + "' was not found."); } }); // save config so rules can access as necessary currentConfig = config; currentText = text; controller = new estraverse.Controller(); // gather data that may be needed by the rules currentScopes = escope.analyze(ast, { ignoreEval: true }).scopes; /* get all tokens from the ast and store them as a hashtable to * improve traversal speed when wanting to find tokens for a given * node */ currentTokens = []; ast.tokens.forEach(function(token) { currentTokens[token.range[0]] = token; }); // augment global scope with declared global variables addDeclaredGlobals(ast, currentScopes[0], currentConfig); /* * Each node has a type property. Whenever a particular type of node is found, * an event is fired. This allows any listeners to automatically be informed * that this type of node has been found and react accordingly. */ controller.traverse(ast, { enter: function(node, parent) { var comments = api.getComments(node), leadingComments = comments.leading, trailingComments = comments.trailing; if (leadingComments.length) { leadingComments.forEach(function(node) { api.emit(node.type + "Comment", node); }); } node.parent = parent; api.emit(node.type, node); if (trailingComments.length) { trailingComments.forEach(function(node) { api.emit(node.type + "Comment", node); }); } }, leave: function(node) { var comments = api.getComments(node), leadingComments = comments.leading, trailingComments = comments.trailing; if (trailingComments.length) { trailingComments.forEach(function(node) { api.emit(node.type + "Comment:exit", node); }); } api.emit(node.type + ":exit", node); if (leadingComments.length) { leadingComments.forEach(function(node) { api.emit(node.type + "Comment:exit", node); }); } } }); } return messages; };
api.verify = function(text, config, filename, saveState) { var ast, shebang; // set the current parsed filename currentFilename = filename; if (!saveState) { this.reset(); } ast = parse(text.replace(/^#!([^\r\n]+)/, function(match, captured) { shebang = captured; return "//" + captured; })); // if Esprima failed to parse the file, there's no sense in setting up rules if (ast) { // process initial config to make it safe to extend config = prepareConfig(config); // parse global comments and modify config modifyConfigsFromComments(ast, config, reportingConfig); // enable appropriate rules Object.keys(config.rules).filter(function(key) { return getRuleSeverity(config.rules[key]) > 0; }).forEach(function(key) { var ruleCreator = rules.get(key), severity = getRuleSeverity(config.rules[key]), options = getRuleOptions(config.rules[key]), rule; if (ruleCreator) { try { rule = ruleCreator(new RuleContext(key, api, severity, options, config.settings)); // add all the node types as listeners Object.keys(rule).forEach(function(nodeType) { api.on(nodeType, rule[nodeType]); }); } catch(ex) { ex.message = "Error while loading rule '" + key + "': " + ex.message; throw ex; } } else { throw new Error("Definition for rule '" + key + "' was not found."); } }); // save config so rules can access as necessary currentConfig = config; currentText = text; controller = new estraverse.Controller(); // gather data that may be needed by the rules currentScopes = escope.analyze(ast, { ignoreEval: true }).scopes; /* * Index the scopes by the start range of their block for efficient * lookup in getScope. */ scopeMap = []; currentScopes.forEach(function (scope, index) { var range = scope.block.range[0]; // Sometimes two scopes are returned for a given node. This is // handled later in a known way, so just don't overwrite here. if (!scopeMap[range]) { scopeMap[range] = index; } }); /* * Split text here into array of lines so * it's not being done repeatedly * by individual rules. */ currentTextLines = currentText.split(/\r?\n|\u2028|\u2029/g); // Freezing so array isn't accidentally changed by a rule. Object.freeze(currentTextLines); /* get all tokens from the ast and store them as a hashtable to * improve traversal speed when wanting to find tokens for a given * node */ currentTokens = []; ast.tokens.forEach(function(token) { currentTokens[token.range[0]] = token; }); // augment global scope with declared global variables addDeclaredGlobals(ast, currentScopes[0], currentConfig); // remove shebang comments if (shebang && ast.comments.length && ast.comments[0].value === shebang) { ast.comments.splice(0, 1); if (ast.body.length && ast.body[0].leadingComments && ast.body[0].leadingComments[0].value === shebang) { ast.body[0].leadingComments.splice(0, 1); } } /* * Each node has a type property. Whenever a particular type of node is found, * an event is fired. This allows any listeners to automatically be informed * that this type of node has been found and react accordingly. */ controller.traverse(ast, { enter: function(node, parent) { var comments = api.getComments(node); emitCommentsEnter(comments.leading); node.parent = parent; api.emit(node.type, node); emitCommentsEnter(comments.trailing); }, leave: function(node) { var comments = api.getComments(node); emitCommentsExit(comments.trailing); api.emit(node.type + ":exit", node); emitCommentsExit(comments.leading); } }); } // sort by line and column messages.sort(function(a, b) { var lineDiff = a.line - b.line; if (lineDiff === 0) { return a.column - b.column; } else { return lineDiff; } }); return messages; };
api.verify = function(text, config, saveState) { var ast, parseError = false; if (!saveState) { this.reset(); } // enable appropriate rules Object.keys(config.rules).filter(function(key) { if (typeof config.rules[key] === "number") { return config.rules[key] > 0; } else if (Array.isArray(config.rules[key])) { // Here the rule looks like [1, ...] - the first value is the key we want return config.rules[key][0] > 0; } }).forEach(function(key) { var ruleCreator = rules.get(key), options = [], rule; if (Array.isArray(config.rules[key])) { // The additional config data is after the bool value options = config.rules[key].slice(1); } if (ruleCreator) { rule = ruleCreator(new RuleContext(key, api, options)); // add all the node types as listeners Object.keys(rule).forEach(function(nodeType) { api.on(nodeType, rule[nodeType]); }); } else { throw new Error("Definition for rule '" + key + "' was not found."); } }); // save config so rules can access as necessary currentConfig = config; currentText = text; controller = new estraverse.Controller(); /* * Check for parsing errors first. If there's a parsing error, nothing * else can happen. However, a parsing error does not throw an error * from this method - it's just considered a fatal error message, a * problem that ESLint identified just like any other. */ try { ast = esprima.parse(text, { loc: true, range: true, raw: true, tokens: true }); } catch (ex) { parseError = true; messages.push({ fatal: true, // messages come as "Line X: Unexpected token foo", so strip off leading part message: ex.message.substring(ex.message.indexOf(":") + 1).trim(), line: ex.lineNumber, column: ex.column }); } if (!parseError) { // gather data that may be needed by the rules currentScopes = escope.analyze(ast).scopes; currentTokens = ast.tokens; /* * Each node has a type property. Whenever a particular type of node is found, * an event is fired. This allows any listeners to automatically be informed * that this type of node has been found and react accordingly. */ controller.traverse(ast, { enter: function(node) { api.emit(node.type, node); }, leave: function(node) { api.emit(node.type + ":after", node); } }); } return messages; };
api.verify = function(text, config, saveState) { var parseError = false, ast; if (!saveState) { this.reset(); } /* * Check for parsing errors first. If there's a parsing error, nothing * else can happen. However, a parsing error does not throw an error * from this method - it's just considered a fatal error message, a * problem that ESLint identified just like any other. */ try { ast = esprima.parse(text, { loc: true, range: true, raw: true, tokens: true, comment: true }); } catch (ex) { parseError = true; messages.push({ fatal: true, // messages come as "Line X: Unexpected token foo", so strip off leading part message: ex.message.substring(ex.message.indexOf(":") + 1).trim(), line: ex.lineNumber, column: ex.column }); } //if Esprima failed to parse the file, there's no sense in setting up rules if (!parseError) { // parse global comments and modify config rules config.rules = modifyRulesFromComments(ast, config); // enable appropriate rules Object.keys(config.rules).filter(function(key) { if (typeof config.rules[key] === "number") { return config.rules[key] > 0; } else if (Array.isArray(config.rules[key])) { // Here the rule looks like [1, ...] - the first value is the key we want return config.rules[key][0] > 0; } }).forEach(function(key) { var ruleCreator = rules.get(key), options = [], rule; if (Array.isArray(config.rules[key])) { // The additional config data is after the bool value options = config.rules[key].slice(1); } /* istanbul ignore else Too complicate to fake invalid rule*/ if (ruleCreator) { try { rule = ruleCreator(new RuleContext(key, api, options)); // add all the node types as listeners Object.keys(rule).forEach(function(nodeType) { api.on(nodeType, rule[nodeType]); }); } catch(ex) { throw new Error("Error while loading rule '" + key + "'."); } } else { throw new Error("Definition for rule '" + key + "' was not found."); } }); // save config so rules can access as necessary currentConfig = config; currentText = text; controller = new estraverse.Controller(); // gather data that may be needed by the rules currentScopes = escope.analyze(ast, { ignoreEval: true }).scopes; /* get all tokens from the ast and store them as a hashtable to * improve traversal speed when wanting to find tokens for a given * node */ currentTokens = []; ast.tokens.forEach(function(token) { currentTokens[token.range[0]] = token; }); // augment global scope with declared global variables addDeclaredGlobals(ast, currentScopes[0], currentConfig); /* * Each node has a type property. Whenever a particular type of node is found, * an event is fired. This allows any listeners to automatically be informed * that this type of node has been found and react accordingly. */ controller.traverse(ast, { enter: function(node) { var comments = api.getComments(node), leadingComments = comments.leading, trailingComments = comments.trailing; if (leadingComments) { leadingComments.forEach(function(node) { api.emit(node.type + "Comment", node); }); } api.emit(node.type, node); if (trailingComments) { trailingComments.forEach(function(node) { api.emit(node.type + "Comment", node); }); } }, leave: function(node) { var comments = api.getComments(node), leadingComments = comments.leading, trailingComments = comments.trailing; if (trailingComments) { trailingComments.forEach(function(node) { api.emit(node.type + "Comment:after", node); }); } api.emit(node.type + ":after", node); if (leadingComments) { leadingComments.forEach(function(node) { api.emit(node.type + "Comment:after", node); }); } } }); } return messages; };
api.verify = function(text, config, saveState) { if (!saveState) { this.reset(); } // enable appropriate rules Object.keys(config.rules).filter(function(key) { return config.rules[key] > 0; // ignore rules that are turned off }).forEach(function(key) { var ruleCreator = rules.get(key), rule; if (ruleCreator) { rule = ruleCreator(new RuleContext(key, api)); // add all the node types as listeners Object.keys(rule).forEach(function(nodeType) { api.on(nodeType, rule[nodeType]); }); } else { throw new Error("Definition for rule '" + key + "' was not found."); } }); // save config so rules can access as necessary currentConfig = config; currentText = text; controller = new estraverse.Controller(); /* * Each node has a type property. Whenever a particular type of node is found, * an event is fired. This allows any listeners to automatically be informed * that this type of node has been found and react accordingly. */ try { var ast = esprima.parse(text, { loc: true, range: true, raw: true, tokens: true }); currentTokens = ast.tokens; controller.traverse(ast, { enter: function(node) { api.emit(node.type, node); }, leave: function(node) { api.emit(node.type + ":after", node); } }); } catch (ex) { messages.push({ fatal: true, // messages come as "Line X: Unexpected token foo", so strip off leading part message: ex.message.substring(ex.message.indexOf(":") + 1).trim(), line: ex.lineNumber, column: ex.column }); } return messages; };
api.verify = function(textOrSourceCode, config, filenameOrOptions, saveState) { var ast, shebang, ecmaFeatures, ecmaVersion, allowInlineConfig, text = (typeof textOrSourceCode === "string") ? textOrSourceCode : null; // evaluate arguments if (typeof filenameOrOptions === "object") { currentFilename = filenameOrOptions.filename; allowInlineConfig = filenameOrOptions.allowInlineConfig; saveState = filenameOrOptions.saveState; } else { currentFilename = filenameOrOptions; } if (!saveState) { this.reset(); } // search and apply "eslint-env *". var envInFile = findEslintEnv(text || textOrSourceCode.text); if (envInFile) { if (!config || !config.env) { config = lodash.assign({}, config || {}, {env: envInFile}); } else { config = lodash.assign({}, config); config.env = lodash.assign({}, config.env, envInFile); } } // process initial config to make it safe to extend config = prepareConfig(config || {}); // only do this for text if (text !== null) { // there's no input, just exit here if (text.trim().length === 0) { sourceCode = new SourceCode(text, blankScriptAST); return messages; } ast = parse( stripUnicodeBOM(text).replace(/^#!([^\r\n]+)/, function(match, captured) { shebang = captured; return "//" + captured; }), config ); if (ast) { sourceCode = new SourceCode(text, ast); } } else { sourceCode = textOrSourceCode; ast = sourceCode.ast; } // if espree failed to parse the file, there's no sense in setting up rules if (ast) { // parse global comments and modify config if (allowInlineConfig !== false) { config = modifyConfigsFromComments(currentFilename, ast, config, reportingConfig, messages); } // ensure that severities are normalized in the config ConfigOps.normalize(config); // enable appropriate rules Object.keys(config.rules).filter(function(key) { return getRuleSeverity(config.rules[key]) > 0; }).forEach(function(key) { var ruleCreator, severity, options, rule; ruleCreator = rules.get(key); if (!ruleCreator) { var replacementMsg = getRuleReplacementMessage(key); if (replacementMsg) { ruleCreator = createStubRule(replacementMsg); } else { ruleCreator = createStubRule("Definition for rule '" + key + "' was not found"); } rules.define(key, ruleCreator); } severity = getRuleSeverity(config.rules[key]); options = getRuleOptions(config.rules[key]); try { var ruleContext = new RuleContext( key, api, severity, options, config.settings, config.parserOptions, config.parser, ruleCreator.meta); rule = ruleCreator.create ? ruleCreator.create(ruleContext) : ruleCreator(ruleContext); // add all the node types as listeners Object.keys(rule).forEach(function(nodeType) { api.on(nodeType, timing.enabled ? timing.time(key, rule[nodeType]) : rule[nodeType] ); }); } catch (ex) { ex.message = "Error while loading rule '" + key + "': " + ex.message; throw ex; } }); // save config so rules can access as necessary currentConfig = config; controller = new estraverse.Controller(); ecmaFeatures = currentConfig.parserOptions.ecmaFeatures || {}; ecmaVersion = currentConfig.parserOptions.ecmaVersion || 5; // gather data that may be needed by the rules scopeManager = escope.analyze(ast, { ignoreEval: true, nodejsScope: ecmaFeatures.globalReturn, impliedStrict: ecmaFeatures.impliedStrict, ecmaVersion: ecmaVersion, sourceType: currentConfig.parserOptions.sourceType || "script", childVisitorKeys: espree.VisitorKeys, fallback: "none" }); currentScopes = scopeManager.scopes; /* * Index the scopes by the start range of their block for efficient * lookup in getScope. */ scopeMap = []; currentScopes.forEach(function(scope, index) { var range = scope.block.range[0]; // Sometimes two scopes are returned for a given node. This is // handled later in a known way, so just don't overwrite here. if (!scopeMap[range]) { scopeMap[range] = index; } }); // augment global scope with declared global variables addDeclaredGlobals(ast, currentScopes[0], currentConfig); // remove shebang comments if (shebang && ast.comments.length && ast.comments[0].value === shebang) { ast.comments.splice(0, 1); if (ast.body.length && ast.body[0].leadingComments && ast.body[0].leadingComments[0].value === shebang) { ast.body[0].leadingComments.splice(0, 1); } } var eventGenerator = new NodeEventGenerator(api); eventGenerator = new CodePathAnalyzer(eventGenerator); eventGenerator = new CommentEventGenerator(eventGenerator, sourceCode); /* * Each node has a type property. Whenever a particular type of node is found, * an event is fired. This allows any listeners to automatically be informed * that this type of node has been found and react accordingly. */ controller.traverse(ast, { enter: function(node, parent) { node.parent = parent; eventGenerator.enterNode(node); }, leave: function(node) { eventGenerator.leaveNode(node); }, keys: espree.VisitorKeys }); } // sort by line and column messages.sort(function(a, b) { var lineDiff = a.line - b.line; if (lineDiff === 0) { return a.column - b.column; } else { return lineDiff; } }); return messages; };