1/** 2 * @fileoverview Require usage of specified node modules. 3 * @author Rich Trott 4 */ 5'use strict'; 6 7var path = require('path'); 8 9//------------------------------------------------------------------------------ 10// Rule Definition 11//------------------------------------------------------------------------------ 12 13module.exports = function(context) { 14 // trim required module names 15 var requiredModules = context.options; 16 17 var foundModules = []; 18 19 // if no modules are required we don't need to check the CallExpressions 20 if (requiredModules.length === 0) { 21 return {}; 22 } 23 24 /** 25 * Function to check if a node is a string literal. 26 * @param {ASTNode} node The node to check. 27 * @returns {boolean} If the node is a string literal. 28 */ 29 function isString(node) { 30 return node && node.type === 'Literal' && typeof node.value === 'string'; 31 } 32 33 /** 34 * Function to check if a node is a require call. 35 * @param {ASTNode} node The node to check. 36 * @returns {boolean} If the node is a require call. 37 */ 38 function isRequireCall(node) { 39 return node.callee.type === 'Identifier' && node.callee.name === 'require'; 40 } 41 42 /** 43 * Function to check if a node has an argument that is a required module and 44 * return its name. 45 * @param {ASTNode} node The node to check 46 * @returns {undefined|String} required module name or undefined 47 */ 48 function getRequiredModuleName(node) { 49 var moduleName; 50 51 // node has arguments and first argument is string 52 if (node.arguments.length && isString(node.arguments[0])) { 53 var argValue = path.basename(node.arguments[0].value.trim()); 54 55 // check if value is in required modules array 56 if (requiredModules.indexOf(argValue) !== -1) { 57 moduleName = argValue; 58 } 59 } 60 61 return moduleName; 62 } 63 64 return { 65 'CallExpression': function(node) { 66 if (isRequireCall(node)) { 67 var requiredModuleName = getRequiredModuleName(node); 68 69 if (requiredModuleName) { 70 foundModules.push(requiredModuleName); 71 } 72 } 73 }, 74 'Program:exit': function(node) { 75 if (foundModules.length < requiredModules.length) { 76 var missingModules = requiredModules.filter( 77 function(module) { 78 return foundModules.indexOf(module === -1); 79 } 80 ); 81 missingModules.forEach(function(moduleName) { 82 context.report( 83 node, 84 'Mandatory module "{{moduleName}}" must be loaded.', 85 { moduleName: moduleName } 86 ); 87 }); 88 } 89 } 90 }; 91}; 92 93module.exports.schema = { 94 'type': 'array', 95 'additionalItems': { 96 'type': 'string' 97 }, 98 'uniqueItems': true 99}; 100