Home | History | Annotate | Download | only in rules
      1 // Copyright 2016 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 /* eslint-disable */
      5 
      6 /**
      7  * @fileoverview Rule to flag non-camelcased identifiers
      8  * @author Nicholas C. Zakas
      9  */
     10 
     11 'use strict';
     12 
     13 //------------------------------------------------------------------------------
     14 // Rule Definition
     15 //------------------------------------------------------------------------------
     16 
     17 module.exports = {
     18     meta: {
     19         docs: {
     20             description: "enforce Catapult camelcase naming convention",
     21             category: "Stylistic Issues",
     22             recommended: false
     23         },
     24 
     25         schema: [
     26             {
     27                 type: "object",
     28                 properties: {
     29                     properties: {
     30                         enum: ["always", "never"]
     31                     }
     32                 },
     33                 additionalProperties: false
     34             }
     35         ]
     36     },
     37 
     38     create(context) {
     39 
     40         //--------------------------------------------------------------------------
     41         // Helpers
     42         //--------------------------------------------------------------------------
     43 
     44         // contains reported nodes to avoid reporting twice on destructuring with shorthand notation
     45         var reported = [];
     46 
     47         /**
     48          * Checks if a string contains an underscore and isn't all upper-case
     49          * @param {string} name The string to check.
     50          * @returns {boolean} if the string is underscored
     51          * @private
     52          */
     53         function isUnderscored(name) {
     54 
     55             // if there's an underscore, it might be A_VARANT, which is okay
     56             return name.indexOf("_") > -1 && name !== name.toUpperCase();
     57         }
     58 
     59         /**
     60          * Reports an AST node as a rule violation.
     61          * @param {ASTNode} node The node to report.
     62          * @returns {void}
     63          * @private
     64          */
     65         function report(node) {
     66             if (reported.indexOf(node) < 0) {
     67                 reported.push(node);
     68                 context.report(node, "Identifier '{{name}}' is not in camel case.", { name: node.name });
     69             }
     70         }
     71 
     72         var options = context.options[0] || {};
     73         let properties = options.properties || "";
     74 
     75         if (properties !== "always" && properties !== "never") {
     76             properties = "always";
     77         }
     78 
     79         return {
     80 
     81             Identifier(node) {
     82 
     83                 /*
     84                  * Leading and trailing underscores are commonly used to flag
     85                  * private/protected identifiers, strip them.
     86                  *
     87                  * NOTE: This has four Catapult-specific style exceptions:
     88                  *
     89                  *   - The prefix opt_
     90                  *   - The prefix g_
     91                  *   - The suffix _smallerIsBetter
     92                  *   - The suffix _biggerIsBetter
     93                  */
     94                 var name = node.name.replace(/(?:^opt_)|^(?:^g_)|^_+|_+$|(?:_smallerIsBetter)$|(?:_biggerIsBetter)$/g, ""),
     95                     effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
     96 
     97                 // MemberExpressions get special rules
     98                 if (node.parent.type === "MemberExpression") {
     99 
    100                     // "never" check properties
    101                     if (properties === "never") {
    102                         return;
    103                     }
    104 
    105                     // Always report underscored object names
    106                     if (node.parent.object.type === "Identifier" &&
    107                             node.parent.object.name === node.name &&
    108                             isUnderscored(name)) {
    109                         report(node);
    110 
    111                     // Report AssignmentExpressions only if they are the left side of the assignment
    112                     } else if (effectiveParent.type === "AssignmentExpression" &&
    113                             isUnderscored(name) &&
    114                             (effectiveParent.right.type !== "MemberExpression" ||
    115                             effectiveParent.left.type === "MemberExpression" &&
    116                             effectiveParent.left.property.name === node.name)) {
    117                         report(node);
    118                     }
    119 
    120                 // Properties have their own rules
    121                 } else if (node.parent.type === "Property") {
    122 
    123                     // "never" check properties
    124                     if (properties === "never") {
    125                         return;
    126                     }
    127 
    128                     if (node.parent.parent && node.parent.parent.type === "ObjectPattern" &&
    129                             node.parent.key === node && node.parent.value !== node) {
    130                         return;
    131                     }
    132 
    133                     if (isUnderscored(name) && effectiveParent.type !== "CallExpression") {
    134                         report(node);
    135                     }
    136 
    137                 // Check if it's an import specifier
    138                 } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) {
    139 
    140                     // Report only if the local imported identifier is underscored
    141                     if (node.parent.local && node.parent.local.name === node.name && isUnderscored(name)) {
    142                         report(node);
    143                     }
    144 
    145                 // Report anything that is underscored that isn't a CallExpression
    146                 } else if (isUnderscored(name) && effectiveParent.type !== "CallExpression") {
    147                     report(node);
    148                 }
    149             }
    150 
    151         };
    152 
    153     }
    154 };
    155