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