Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Commit

Permalink
Added require-const-for-all-caps option to variable-name (#2936)
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh Goldberg authored and adidahiya committed Mar 12, 2019
1 parent d881c4c commit 40120a2
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/configs/all.ts
Expand Up @@ -266,7 +266,7 @@ export const rules = {
"switch-final-break": true,
"type-literal-delimiter": true,
"unnecessary-bind": true,
"variable-name": { options: ["ban-keywords", "check-format"] },
"variable-name": { options: ["ban-keywords", "check-format", "require-const-for-all-caps"] },
whitespace: {
options: [
"check-branch",
Expand Down
61 changes: 44 additions & 17 deletions src/rules/variableNameRule.ts
Expand Up @@ -17,7 +17,7 @@

// tslint:disable object-literal-sort-keys

import { hasModifier } from "tsutils";
import * as tsutils from "tsutils";
import * as ts from "typescript";

import * as Lint from "../index";
Expand All @@ -41,6 +41,7 @@ const OPTION_LEADING_UNDERSCORE = "allow-leading-underscore";
const OPTION_TRAILING_UNDERSCORE = "allow-trailing-underscore";
const OPTION_BAN_KEYWORDS = "ban-keywords";
const OPTION_CHECK_FORMAT = "check-format";
const OPTION_REQUIRE_CONT_FOR_ALL_CAPS = "require-const-for-all-caps";
const OPTION_ALLOW_PASCAL_CASE = "allow-pascal-case";
const OPTION_ALLOW_SNAKE_CASE = "allow-snake-case";

Expand All @@ -49,11 +50,12 @@ export class Rule extends Lint.Rules.AbstractRule {
ruleName: "variable-name",
description: "Checks variable names for various errors.",
optionsDescription: Lint.Utils.dedent`
Five arguments may be optionally provided:
Six arguments may be optionally provided:
* \`"${OPTION_CHECK_FORMAT}"\`: allows only lowerCamelCased or UPPER_CASED variable names
* \`"${OPTION_LEADING_UNDERSCORE}"\` allows underscores at the beginning (only has an effect if "check-format" specified)
* \`"${OPTION_TRAILING_UNDERSCORE}"\` allows underscores at the end. (only has an effect if "check-format" specified)
* \`"${OPTION_REQUIRE_CONT_FOR_ALL_CAPS}"\`: enforces that all variables with UPPER_CASED names should be \`const\`.
* \`"${OPTION_ALLOW_PASCAL_CASE}"\` allows PascalCase in addition to lowerCamelCase.
* \`"${OPTION_ALLOW_SNAKE_CASE}"\` allows snake_case in addition to lowerCamelCase.
* \`"${OPTION_BAN_KEYWORDS}"\`: disallows the use of certain TypeScript keywords as variable or parameter names.
Expand All @@ -72,7 +74,7 @@ export class Rule extends Lint.Rules.AbstractRule {
],
},
minLength: 0,
maxLength: 5,
maxLength: 6,
},
optionExamples: [[true, "ban-keywords", "check-format", "allow-leading-underscore"]],
type: "style",
Expand All @@ -81,6 +83,8 @@ export class Rule extends Lint.Rules.AbstractRule {

public static KEYWORD_FAILURE = "variable name clashes with keyword/type";

public static FAILURE_STRING_CONST = "Only `const` variables may be UPPER_CASE.";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments));
}
Expand All @@ -91,6 +95,7 @@ interface Options {
checkFormat: boolean;
leadingUnderscore: boolean;
trailingUnderscore: boolean;
allCapsForConst: boolean;
allowPascalCase: boolean;
allowSnakeCase: boolean;
}
Expand All @@ -102,6 +107,7 @@ function parseOptions(ruleArguments: string[]): Options {
checkFormat: !banKeywords || hasOption(OPTION_CHECK_FORMAT),
leadingUnderscore: hasOption(OPTION_LEADING_UNDERSCORE),
trailingUnderscore: hasOption(OPTION_TRAILING_UNDERSCORE),
allCapsForConst: hasOption(OPTION_REQUIRE_CONT_FOR_ALL_CAPS),
allowPascalCase: hasOption(OPTION_ALLOW_PASCAL_CASE),
allowSnakeCase: hasOption(OPTION_ALLOW_SNAKE_CASE),
};
Expand Down Expand Up @@ -133,31 +139,37 @@ function walk(ctx: Lint.WalkContext<Options>): void {

case ts.SyntaxKind.VariableStatement:
// skip 'declare' keywords
if (hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword)) {
if (tsutils.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword)) {
return;
}
break;

case ts.SyntaxKind.Parameter:
case ts.SyntaxKind.PropertyDeclaration:
case ts.SyntaxKind.VariableDeclaration: {
const { name, initializer } = node as
| ts.ParameterDeclaration
| ts.PropertyDeclaration
| ts.VariableDeclaration;
if (name.kind === ts.SyntaxKind.Identifier) {
handleVariableNameFormat(name, initializer);
// do not check property declarations for keywords, they are allowed to be keywords
if (node.kind !== ts.SyntaxKind.PropertyDeclaration) {
handleVariableNameKeyword(name);
}
}
}
handleDeclaredVariable(node as ts.ParameterDeclaration | ts.PropertyDeclaration);
break;

case ts.SyntaxKind.VariableDeclaration:
handleVariableDeclaration(node as ts.VariableDeclaration);
}

return ts.forEachChild(node, cb);
});

function handleDeclaredVariable(
node: ts.ParameterDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration,
): void {
const { name, initializer } = node;

if (name.kind === ts.SyntaxKind.Identifier) {
handleVariableNameFormat(name, initializer);
// do not check property declarations for keywords, they are allowed to be keywords
if (node.kind !== ts.SyntaxKind.PropertyDeclaration) {
handleVariableNameKeyword(name);
}
}
}

function handleVariableNameFormat(name: ts.Identifier, initializer?: ts.Expression): void {
if (!options.checkFormat) {
return;
Expand All @@ -179,6 +191,21 @@ function walk(ctx: Lint.WalkContext<Options>): void {
}
}

function handleVariableDeclaration(node: ts.VariableDeclaration): void {
handleDeclaredVariable(node);

if (!ctx.options.allCapsForConst || tsutils.isBindingPattern(node.name)) {
return;
}

const declarationList = node.parent;
const text = node.name.text;

if (isUpperCase(text) && !tsutils.isNodeFlagSet(declarationList, ts.NodeFlags.Const)) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING_CONST);
}
}

function formatFailure(): string {
let failureMessage = "variable name must be in lowerCamelCase";
if (options.allowPascalCase) {
Expand Down
38 changes: 38 additions & 0 deletions test/rules/variable-name/const-only-for-caps/test.ts.lint
@@ -0,0 +1,38 @@
var snake_case_var;
~~~~~~~~~~~~~~ [regular-case]

var camelCaseVar;

var PascalCaseVar;
~~~~~~~~~~~~~ [regular-case]

var UPPER_CASE_VAR;
~~~~~~~~~~~~~~ [const]

let snake_case_let;
~~~~~~~~~~~~~~ [regular-case]

let camelCaseLet;

let PascalCaseLet;
~~~~~~~~~~~~~ [regular-case]

let UPPER_CASE_LET;
~~~~~~~~~~~~~~ [const]

const snake_case_const;
~~~~~~~~~~~~~~~~ [regular-case]

const camelCaseConst;

const PascalCaseConst;
~~~~~~~~~~~~~~~ [regular-case]

const UPPER_CASE_CONST;

class Test {
public static readonly MY_FIELD = 10;
}

[const]: Only `const` variables may be UPPER_CASE.
[regular-case]: variable name must be in lowerCamelCase or UPPER_CASE
5 changes: 5 additions & 0 deletions test/rules/variable-name/const-only-for-caps/tslint.json
@@ -0,0 +1,5 @@
{
"rules": {
"variable-name": [true, "require-const-for-all-caps"]
}
}

0 comments on commit 40120a2

Please sign in to comment.