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