diff --git a/src/rules/variableNameRule.ts b/src/rules/variableNameRule.ts index f9a49a6e68a..4733e2be884 100644 --- a/src/rules/variableNameRule.ts +++ b/src/rules/variableNameRule.ts @@ -30,6 +30,7 @@ const OPTION_TRAILING_UNDERSCORE = "allow-trailing-underscore"; const OPTION_BAN_KEYWORDS = "ban-keywords"; const OPTION_CHECK_FORMAT = "check-format"; const OPTION_ALLOW_PASCAL_CASE = "allow-pascal-case"; +const OPTION_ALLOW_SNAKE_CASE = "allow-snake-case"; export class Rule extends Lint.Rules.AbstractRule { public static metadata: Lint.IRuleMetadata = { @@ -42,6 +43,7 @@ export class Rule extends Lint.Rules.AbstractRule { * \`"${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_ALLOW_PASCAL_CASE}"\` allows PascalCase in addition to camelCase. + * \`"${OPTION_ALLOW_SNAKE_CASE}"\` allows snake_case in addition to camelCase. * \`"${OPTION_BAN_KEYWORDS}"\`: disallows the use of certain TypeScript keywords as variable or parameter names. * These are: ${bannedKeywordsStr}`, options: { @@ -53,6 +55,7 @@ export class Rule extends Lint.Rules.AbstractRule { OPTION_LEADING_UNDERSCORE, OPTION_TRAILING_UNDERSCORE, OPTION_ALLOW_PASCAL_CASE, + OPTION_ALLOW_SNAKE_CASE, OPTION_BAN_KEYWORDS, ], }, @@ -64,7 +67,6 @@ export class Rule extends Lint.Rules.AbstractRule { typescriptOnly: false, }; - public static FORMAT_FAILURE = "variable name must be in camelcase or uppercase"; public static KEYWORD_FAILURE = "variable name clashes with keyword/type"; public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { @@ -78,6 +80,7 @@ interface Options { leadingUnderscore: boolean; trailingUnderscore: boolean; allowPascalCase: boolean; + allowSnakeCase: boolean; } function parseOptions(ruleArguments: string[]): Options { const banKeywords = hasOption(OPTION_BAN_KEYWORDS); @@ -88,6 +91,7 @@ function parseOptions(ruleArguments: string[]): Options { leadingUnderscore: hasOption(OPTION_LEADING_UNDERSCORE), trailingUnderscore: hasOption(OPTION_TRAILING_UNDERSCORE), allowPascalCase: hasOption(OPTION_ALLOW_PASCAL_CASE), + allowSnakeCase: hasOption(OPTION_ALLOW_SNAKE_CASE), }; function hasOption(name: string): boolean { @@ -148,7 +152,7 @@ function walk(ctx: Lint.WalkContext): void { } if (!isCamelCase(text, options) && !isUpperCase(text)) { - ctx.addFailureAtNode(name, Rule.FORMAT_FAILURE); + ctx.addFailureAtNode(name, formatFailure()); } } @@ -157,6 +161,17 @@ function walk(ctx: Lint.WalkContext): void { ctx.addFailureAtNode(name, Rule.KEYWORD_FAILURE); } } + + function formatFailure(): string { + let failureMessage = "variable name must be in camelcase"; + if (options.allowPascalCase) { + failureMessage += ", pascalcase"; + } + if (options.allowSnakeCase) { + failureMessage += ", snakecase"; + } + return failureMessage + " or uppercase"; + } } function isAlias(name: string, initializer: ts.Expression): boolean { @@ -184,7 +199,10 @@ function isCamelCase(name: string, options: Options): boolean { if (!options.allowPascalCase && !isLowerCase(firstCharacter)) { return false; } - return middle.indexOf("_") === -1; + if (!options.allowSnakeCase && middle.indexOf("_") !== -1) { + return false; + } + return true; } function isLowerCase(name: string): boolean { diff --git a/test/rules/variable-name/allow-pascal-case/test.ts.lint b/test/rules/variable-name/allow-pascal-case/test.ts.lint index 3f8360d31f7..3262073ea99 100644 --- a/test/rules/variable-name/allow-pascal-case/test.ts.lint +++ b/test/rules/variable-name/allow-pascal-case/test.ts.lint @@ -1,14 +1,14 @@ var validName1 = "hi"; var VALIDNAME2 = "there"; var ValidName3 = ","; -var invalid_name2 = " "; // failure - ~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] +var invalid_name1 = " "; // failure + ~~~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] class Test { private Invalid_name3 = "how"; // failure - ~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] private _optionallyValid = "are"; // sometimes a failure - ~~~~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] } function test() { @@ -20,26 +20,26 @@ function test() { declare var DeclaresAreValid: any; export function functionWithInvalidParamNames (bad_name, AnotherOne) { // 1 failure - ~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] // } let { foo, bar } = { foo: 1, bar: 2 }; let [ invalid_bar, ...invalid_baz ] = [1, 2, 3, 4]; // 3 failures - ~~~~~~~~~~~ [variable name must be in camelcase or uppercase] - ~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] + ~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] export function anotherFunctionWithInvalidParamNames ([first_element, SecondElement]) { // 1 failure - ~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] // } export function functionWithInvalidSpread(invalid_arg: ...number) { // 1 failure - ~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] // } let optionallyValid_ = "bar"; - ~~~~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] let _$httpBackend_ = "leading and trailing"; - ~~~~~~~~~~~~~~ [variable name must be in camelcase or uppercase] + ~~~~~~~~~~~~~~ [variable name must be in camelcase, pascalcase or uppercase] diff --git a/test/rules/variable-name/allow-snake-case/test.ts.lint b/test/rules/variable-name/allow-snake-case/test.ts.lint new file mode 100644 index 00000000000..d233f60ccfe --- /dev/null +++ b/test/rules/variable-name/allow-snake-case/test.ts.lint @@ -0,0 +1,64 @@ +var validName1 = "hi"; +var VALIDNAME2 = "there"; +var valid_name3 = "tslint"; +var Invalid_name1 = " "; // failure + ~~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] +var InvalidName2 = " "; // failure + ~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + +class Test { + private valid_name = " "; + private Invalid_name1 = "how"; // failure + ~~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + private _optionallyValid = "are"; // sometimes a failure + ~~~~~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] +} + +function test() { + () => { + var valid_name = " "; + var InVaLiDnAmE4 = "you"; // failure + ~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + }; +} + +declare var DeclaresAreValid: any; + +export function functionWithInvalidParamNames (ok_name, BadName) { // 1 failure + ~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] +} + +let { foo, bar } = { foo: 1, bar: 2 }; +let [ InvalidFoo, invalid_bar, ...invalid_baz ] = [1, 2, 3, 4]; // 1 failure + ~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + +export function anotherFunctionWithInvalidParamNames ([first_element, SecondElement]) { // 1 failure + ~~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + // +} + +export function functionWithInvalidSpread(InvalidArg: ...number) { // 1 failure + ~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + // +} + +let optionallyValid_ = "bar"; + ~~~~~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] +let _$httpBackend_ = "leading and trailing"; + ~~~~~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + +// Aliases. +class X { + ValidAlias = ValidAlias; +} + +var ValidAlias = some.ValidAlias; +var ValidAlias = some.InValidAlias; + ~~~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] + +var someObject = {MoreValidAlias: 1}; +var {MoreValidAlias: localVar} = someObject; +var {MoreValidAlias: local_var} = someObject; +var {MoreValidAlias: LocalVar} = someObject; + ~~~~~~~~ [variable name must be in camelcase, snakecase or uppercase] +var {MoreValidAlias} = someObject; diff --git a/test/rules/variable-name/allow-snake-case/tslint.json b/test/rules/variable-name/allow-snake-case/tslint.json new file mode 100644 index 00000000000..16bbbfb2052 --- /dev/null +++ b/test/rules/variable-name/allow-snake-case/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "variable-name": [true, "allow-snake-case"] + } +}