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

Added require-const-for-all-caps option to variable-name #2936

Merged
merged 20 commits into from
Mar 12, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/configs/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,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", "const-only-for-caps"] },
whitespace: {
options: [
"check-branch",
Expand Down
61 changes: 44 additions & 17 deletions src/rules/variableNameRule.ts
Original file line number Diff line number Diff line change
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_ALL_CAPS_FOR_CONST = "const-only-for-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_ALL_CAPS_FOR_CONST}"\`: 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_ALL_CAPS_FOR_CONST),
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"variable-name": [true, "const-only-for-caps"]
}
}