• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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